| 
									
										
										
										
											2014-04-30 21:47:17 +08:00
										 |  |  | # Tcl client library - used by the Redis test
 | 
					
						
							|  |  |  | # Copyright (C) 2009-2014 Salvatore Sanfilippo
 | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  | # Released under the BSD license like Redis itself
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Example usage:
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # set r [redis 127.0.0.1 6379]
 | 
					
						
							|  |  |  | # $r lpush mylist foo
 | 
					
						
							|  |  |  | # $r lpush mylist bar
 | 
					
						
							|  |  |  | # $r lrange mylist 0 -1
 | 
					
						
							|  |  |  | # $r close
 | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  | # | 
					
						
							|  |  |  | # Non blocking usage example:
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # proc handlePong {r type reply} {
 | 
					
						
							|  |  |  | #     puts "PONG $type '$reply'"
 | 
					
						
							|  |  |  | #     if {$reply ne "PONG"} {
 | 
					
						
							|  |  |  | #         $r ping [list handlePong]
 | 
					
						
							|  |  |  | #     }
 | 
					
						
							|  |  |  | # }
 | 
					
						
							| 
									
										
										
										
											2014-08-01 02:39:49 +08:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  | # set r [redis]
 | 
					
						
							|  |  |  | # $r blocking 0
 | 
					
						
							|  |  |  | # $r get fo [list handlePong]
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # vwait forever
 | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-19 17:05:08 +08:00
										 |  |  | package require Tcl 8.5 | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  | package provide redis 0.1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace eval redis {} | 
					
						
							|  |  |  | set ::redis::id 0 | 
					
						
							|  |  |  | array set ::redis::fd {} | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  | array set ::redis::addr {} | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  | array set ::redis::blocking {} | 
					
						
							| 
									
										
										
										
											2010-06-16 03:16:27 +08:00
										 |  |  | array set ::redis::deferred {} | 
					
						
							| 
									
										
										
										
											2021-07-05 00:43:58 +08:00
										 |  |  | array set ::redis::readraw {} | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  | array set ::redis::reconnect {} | 
					
						
							| 
									
										
										
										
											2021-03-31 04:11:32 +08:00
										 |  |  | array set ::redis::tls {} | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  | array set ::redis::callback {} | 
					
						
							|  |  |  | array set ::redis::state {} ;# State in non-blocking reply reading
 | 
					
						
							| 
									
										
										
										
											2010-04-08 21:56:21 +08:00
										 |  |  | array set ::redis::statestack {} ;# Stack of states, for nested mbulks
 | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-05 00:43:58 +08:00
										 |  |  | proc redis {{server 127.0.0.1} {port 6379} {defer 0} {tls 0} {tlsoptions {}} {readraw 0}} { | 
					
						
							| 
									
										
										
										
											2019-09-12 15:56:54 +08:00
										 |  |  |     if {$tls} { | 
					
						
							|  |  |  |         package require tls | 
					
						
							|  |  |  |         ::tls::init \ | 
					
						
							|  |  |  |             -cafile "$::tlsdir/ca.crt" \ | 
					
						
							| 
									
										
										
										
											2020-12-12 00:31:40 +08:00
										 |  |  |             -certfile "$::tlsdir/client.crt" \ | 
					
						
							|  |  |  |             -keyfile "$::tlsdir/client.key" \ | 
					
						
							| 
									
										
										
										
											2019-09-12 16:10:22 +08:00
										 |  |  |             {*}$tlsoptions | 
					
						
							| 
									
										
										
										
											2019-09-12 15:56:54 +08:00
										 |  |  |         set fd [::tls::socket $server $port] | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         set fd [socket $server $port] | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  |     fconfigure $fd -translation binary | 
					
						
							|  |  |  |     set id [incr ::redis::id] | 
					
						
							|  |  |  |     set ::redis::fd($id) $fd | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |     set ::redis::addr($id) [list $server $port] | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  |     set ::redis::blocking($id) 1 | 
					
						
							| 
									
										
										
										
											2010-06-16 03:16:27 +08:00
										 |  |  |     set ::redis::deferred($id) $defer | 
					
						
							| 
									
										
										
										
											2021-07-05 00:43:58 +08:00
										 |  |  |     set ::redis::readraw($id) $readraw | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |     set ::redis::reconnect($id) 0 | 
					
						
							| 
									
										
										
										
											2021-03-31 04:11:32 +08:00
										 |  |  |     set ::redis::tls($id) $tls | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  |     ::redis::redis_reset_state $id | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  |     interp alias {} ::redis::redisHandle$id {} ::redis::__dispatch__ $id | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  | # This is a wrapper to the actual dispatching procedure that handles
 | 
					
						
							|  |  |  | # reconnection if needed.
 | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  | proc ::redis::__dispatch__ {id method args} { | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |     set errorcode [catch {::redis::__dispatch__raw__ $id $method $args} retval] | 
					
						
							|  |  |  |     if {$errorcode && $::redis::reconnect($id) && $::redis::fd($id) eq {}} { | 
					
						
							|  |  |  |         # Try again if the connection was lost.
 | 
					
						
							|  |  |  |         # FIXME: we don't re-select the previously selected DB, nor we check
 | 
					
						
							|  |  |  |         # if we are inside a transaction that needs to be re-issued from
 | 
					
						
							|  |  |  |         # scratch.
 | 
					
						
							|  |  |  |         set errorcode [catch {::redis::__dispatch__raw__ $id $method $args} retval] | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return -code $errorcode $retval | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | proc ::redis::__dispatch__raw__ {id method argv} { | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  |     set fd $::redis::fd($id) | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Reconnect the link if needed.
 | 
					
						
							|  |  |  |     if {$fd eq {}} { | 
					
						
							|  |  |  |         lassign $::redis::addr($id) host port | 
					
						
							| 
									
										
										
										
											2021-03-31 04:11:32 +08:00
										 |  |  |         if {$::redis::tls($id)} { | 
					
						
							| 
									
										
										
										
											2019-09-12 15:56:54 +08:00
										 |  |  |             set ::redis::fd($id) [::tls::socket $host $port] | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             set ::redis::fd($id) [socket $host $port] | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |         fconfigure $::redis::fd($id) -translation binary | 
					
						
							|  |  |  |         set fd $::redis::fd($id) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  |     set blocking $::redis::blocking($id) | 
					
						
							| 
									
										
										
										
											2010-06-16 03:16:27 +08:00
										 |  |  |     set deferred $::redis::deferred($id) | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  |     if {$blocking == 0} { | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |         if {[llength $argv] == 0} { | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  |             error "Please provide a callback in non-blocking mode" | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |         set callback [lindex $argv end] | 
					
						
							|  |  |  |         set argv [lrange $argv 0 end-1] | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  |     if {[info command ::redis::__method__$method] eq {}} { | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |         set cmd "*[expr {[llength $argv]+1}]\r\n" | 
					
						
							| 
									
										
										
										
											2010-10-15 21:50:29 +08:00
										 |  |  |         append cmd "$[string length $method]\r\n$method\r\n" | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |         foreach a $argv { | 
					
						
							| 
									
										
										
										
											2010-10-15 21:50:29 +08:00
										 |  |  |             append cmd "$[string length $a]\r\n$a\r\n" | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-10-15 21:50:29 +08:00
										 |  |  |         ::redis::redis_write $fd $cmd | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |         if {[catch {flush $fd}]} { | 
					
						
							| 
									
										
										
										
											2020-11-23 03:21:42 +08:00
										 |  |  |             catch {close $fd} | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |             set ::redis::fd($id) {} | 
					
						
							|  |  |  |             return -code error "I/O error reading reply" | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-10-15 21:50:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-16 03:16:27 +08:00
										 |  |  |         if {!$deferred} { | 
					
						
							|  |  |  |             if {$blocking} { | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |                 ::redis::redis_read_reply $id $fd | 
					
						
							| 
									
										
										
										
											2010-06-16 03:16:27 +08:00
										 |  |  |             } else { | 
					
						
							|  |  |  |                 # Every well formed reply read will pop an element from this
 | 
					
						
							|  |  |  |                 # list and use it as a callback. So pipelining is supported
 | 
					
						
							|  |  |  |                 # in non blocking mode.
 | 
					
						
							|  |  |  |                 lappend ::redis::callback($id) $callback | 
					
						
							|  |  |  |                 fileevent $fd readable [list ::redis::redis_readable $fd $id] | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |         uplevel 1 [list ::redis::__method__$method $id $fd] $argv | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  | proc ::redis::__method__blocking {id fd val} { | 
					
						
							|  |  |  |     set ::redis::blocking($id) $val | 
					
						
							|  |  |  |     fconfigure $fd -blocking $val | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  | proc ::redis::__method__reconnect {id fd val} { | 
					
						
							|  |  |  |     set ::redis::reconnect($id) $val | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-16 03:16:27 +08:00
										 |  |  | proc ::redis::__method__read {id fd} { | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |     ::redis::redis_read_reply $id $fd | 
					
						
							| 
									
										
										
										
											2010-06-16 03:16:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-13 17:25:40 +08:00
										 |  |  | proc ::redis::__method__write {id fd buf} { | 
					
						
							|  |  |  |     ::redis::redis_write $fd $buf | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | proc ::redis::__method__flush {id fd} { | 
					
						
							|  |  |  |     flush $fd | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  | proc ::redis::__method__close {id fd} { | 
					
						
							|  |  |  |     catch {close $fd} | 
					
						
							|  |  |  |     catch {unset ::redis::fd($id)} | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |     catch {unset ::redis::addr($id)} | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  |     catch {unset ::redis::blocking($id)} | 
					
						
							| 
									
										
										
										
											2014-06-18 21:07:08 +08:00
										 |  |  |     catch {unset ::redis::deferred($id)} | 
					
						
							| 
									
										
										
										
											2021-07-05 00:43:58 +08:00
										 |  |  |     catch {unset ::redis::readraw($id)} | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |     catch {unset ::redis::reconnect($id)} | 
					
						
							| 
									
										
										
										
											2021-03-31 04:11:32 +08:00
										 |  |  |     catch {unset ::redis::tls($id)} | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  |     catch {unset ::redis::state($id)} | 
					
						
							| 
									
										
										
										
											2010-04-08 21:56:21 +08:00
										 |  |  |     catch {unset ::redis::statestack($id)} | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  |     catch {unset ::redis::callback($id)} | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  |     catch {interp alias {} ::redis::redisHandle$id {}} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | proc ::redis::__method__channel {id fd} { | 
					
						
							|  |  |  |     return $fd | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-31 00:54:28 +08:00
										 |  |  | proc ::redis::__method__deferred {id fd val} { | 
					
						
							|  |  |  |     set ::redis::deferred($id) $val | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-05 00:43:58 +08:00
										 |  |  | proc ::redis::__method__readraw {id fd val} { | 
					
						
							|  |  |  |     set ::redis::readraw($id) $val | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  | proc ::redis::redis_write {fd buf} { | 
					
						
							|  |  |  |     puts -nonewline $fd $buf | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | proc ::redis::redis_writenl {fd buf} { | 
					
						
							|  |  |  |     redis_write $fd $buf | 
					
						
							|  |  |  |     redis_write $fd "\r\n" | 
					
						
							|  |  |  |     flush $fd | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | proc ::redis::redis_readnl {fd len} { | 
					
						
							|  |  |  |     set buf [read $fd $len] | 
					
						
							|  |  |  |     read $fd 2 ; # discard CR LF
 | 
					
						
							|  |  |  |     return $buf | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | proc ::redis::redis_bulk_read {fd} { | 
					
						
							|  |  |  |     set count [redis_read_line $fd] | 
					
						
							|  |  |  |     if {$count == -1} return {} | 
					
						
							|  |  |  |     set buf [redis_readnl $fd $count] | 
					
						
							|  |  |  |     return $buf | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  | proc ::redis::redis_multi_bulk_read {id fd} { | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  |     set count [redis_read_line $fd] | 
					
						
							|  |  |  |     if {$count == -1} return {} | 
					
						
							|  |  |  |     set l {} | 
					
						
							| 
									
										
										
										
											2012-04-07 05:52:28 +08:00
										 |  |  |     set err {} | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  |     for {set i 0} {$i < $count} {incr i} { | 
					
						
							| 
									
										
										
										
											2012-04-07 05:52:28 +08:00
										 |  |  |         if {[catch { | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |             lappend l [redis_read_reply $id $fd] | 
					
						
							| 
									
										
										
										
											2012-04-07 05:52:28 +08:00
										 |  |  |         } e] && $err eq {}} { | 
					
						
							|  |  |  |             set err $e | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2012-04-07 05:52:28 +08:00
										 |  |  |     if {$err ne {}} {return -code error $err} | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  |     return $l | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-10 04:54:47 +08:00
										 |  |  | proc ::redis::redis_read_map {id fd} { | 
					
						
							|  |  |  |     set count [redis_read_line $fd] | 
					
						
							|  |  |  |     if {$count == -1} return {} | 
					
						
							| 
									
										
										
										
											2021-01-05 14:29:20 +08:00
										 |  |  |     set d {} | 
					
						
							| 
									
										
										
										
											2020-11-10 04:54:47 +08:00
										 |  |  |     set err {} | 
					
						
							|  |  |  |     for {set i 0} {$i < $count} {incr i} { | 
					
						
							|  |  |  |         if {[catch { | 
					
						
							| 
									
										
										
										
											2021-01-05 14:29:20 +08:00
										 |  |  |             set k [redis_read_reply $id $fd] ; # key
 | 
					
						
							|  |  |  |             set v [redis_read_reply $id $fd] ; # value
 | 
					
						
							|  |  |  |             dict set d $k $v | 
					
						
							| 
									
										
										
										
											2020-11-10 04:54:47 +08:00
										 |  |  |         } e] && $err eq {}} { | 
					
						
							|  |  |  |             set err $e | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if {$err ne {}} {return -code error $err} | 
					
						
							| 
									
										
										
										
											2021-01-05 14:29:20 +08:00
										 |  |  |     return $d | 
					
						
							| 
									
										
										
										
											2020-11-10 04:54:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  | proc ::redis::redis_read_line fd { | 
					
						
							|  |  |  |     string trim [gets $fd] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-10 04:54:47 +08:00
										 |  |  | proc ::redis::redis_read_null fd { | 
					
						
							|  |  |  |     gets $fd | 
					
						
							|  |  |  |     return {} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 00:14:31 +08:00
										 |  |  | proc ::redis::redis_read_bool fd { | 
					
						
							|  |  |  |     set v [redis_read_line $fd] | 
					
						
							|  |  |  |     if {$v == "t"} {return 1} | 
					
						
							|  |  |  |     if {$v == "f"} {return 0} | 
					
						
							|  |  |  |     return -code error "Bad protocol, '$v' as bool type" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  | proc ::redis::redis_read_reply {id fd} { | 
					
						
							| 
									
										
										
										
											2021-07-05 00:43:58 +08:00
										 |  |  |     if {$::redis::readraw($id)} { | 
					
						
							|  |  |  |         return [redis_read_line $fd] | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 00:14:31 +08:00
										 |  |  |     while {1} { | 
					
						
							|  |  |  |         set type [read $fd 1] | 
					
						
							|  |  |  |         switch -exact -- $type { | 
					
						
							|  |  |  |             _ {return [redis_read_null $fd]} | 
					
						
							|  |  |  |             : - | 
					
						
							|  |  |  |             ( - | 
					
						
							|  |  |  |             + {return [redis_read_line $fd]} | 
					
						
							|  |  |  |             , {return [expr {double([redis_read_line $fd])}]} | 
					
						
							|  |  |  |             # {return [redis_read_bool $fd]}
 | 
					
						
							|  |  |  |             - {return -code error [redis_read_line $fd]} | 
					
						
							|  |  |  |             $ {return [redis_bulk_read $fd]} | 
					
						
							|  |  |  |             > - | 
					
						
							|  |  |  |             ~ - | 
					
						
							|  |  |  |             * {return [redis_multi_bulk_read $id $fd]} | 
					
						
							|  |  |  |             % {return [redis_read_map $id $fd]} | 
					
						
							|  |  |  |             | { | 
					
						
							|  |  |  |                 # ignore attributes for now (nowhere to store them)
 | 
					
						
							|  |  |  |                 redis_read_map $id $fd | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             default { | 
					
						
							|  |  |  |                 if {$type eq {}} { | 
					
						
							|  |  |  |                     catch {close $fd} | 
					
						
							|  |  |  |                     set ::redis::fd($id) {} | 
					
						
							|  |  |  |                     return -code error "I/O error reading reply" | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return -code error "Bad protocol, '$type' as reply type byte" | 
					
						
							| 
									
										
										
										
											2014-06-18 21:52:14 +08:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2014-05-14 06:14:35 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-11-03 18:28:37 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | proc ::redis::redis_reset_state id { | 
					
						
							|  |  |  |     set ::redis::state($id) [dict create buf {} mbulk -1 bulk -1 reply {}] | 
					
						
							| 
									
										
										
										
											2010-04-08 21:56:21 +08:00
										 |  |  |     set ::redis::statestack($id) {} | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | proc ::redis::redis_call_callback {id type reply} { | 
					
						
							|  |  |  |     set cb [lindex $::redis::callback($id) 0] | 
					
						
							|  |  |  |     set ::redis::callback($id) [lrange $::redis::callback($id) 1 end] | 
					
						
							|  |  |  |     uplevel #0 $cb [list ::redis::redisHandle$id $type $reply] | 
					
						
							|  |  |  |     ::redis::redis_reset_state $id | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Read a reply in non-blocking mode.
 | 
					
						
							|  |  |  | proc ::redis::redis_readable {fd id} { | 
					
						
							|  |  |  |     if {[eof $fd]} { | 
					
						
							|  |  |  |         redis_call_callback $id eof {} | 
					
						
							|  |  |  |         ::redis::__method__close $id $fd | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if {[dict get $::redis::state($id) bulk] == -1} { | 
					
						
							|  |  |  |         set line [gets $fd] | 
					
						
							|  |  |  |         if {$line eq {}} return ;# No complete line available, return
 | 
					
						
							|  |  |  |         switch -exact -- [string index $line 0] { | 
					
						
							|  |  |  |             : - | 
					
						
							|  |  |  |             + {redis_call_callback $id reply [string range $line 1 end-1]} | 
					
						
							|  |  |  |             - {redis_call_callback $id err [string range $line 1 end-1]} | 
					
						
							|  |  |  |             $ { | 
					
						
							|  |  |  |                 dict set ::redis::state($id) bulk \ | 
					
						
							|  |  |  |                     [expr [string range $line 1 end-1]+2] | 
					
						
							|  |  |  |                 if {[dict get $::redis::state($id) bulk] == 1} { | 
					
						
							|  |  |  |                     # We got a $-1, hack the state to play well with this.
 | 
					
						
							|  |  |  |                     dict set ::redis::state($id) bulk 2 | 
					
						
							|  |  |  |                     dict set ::redis::state($id) buf "\r\n" | 
					
						
							|  |  |  |                     ::redis::redis_readable $fd $id | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2010-04-08 21:56:21 +08:00
										 |  |  |             * { | 
					
						
							|  |  |  |                 dict set ::redis::state($id) mbulk [string range $line 1 end-1] | 
					
						
							|  |  |  |                 # Handle *-1
 | 
					
						
							|  |  |  |                 if {[dict get $::redis::state($id) mbulk] == -1} { | 
					
						
							|  |  |  |                     redis_call_callback $id reply {} | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2010-04-07 19:55:06 +08:00
										 |  |  |             default { | 
					
						
							|  |  |  |                 redis_call_callback $id err \ | 
					
						
							|  |  |  |                     "Bad protocol, $type as reply type byte" | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         set totlen [dict get $::redis::state($id) bulk] | 
					
						
							|  |  |  |         set buflen [string length [dict get $::redis::state($id) buf]] | 
					
						
							|  |  |  |         set toread [expr {$totlen-$buflen}] | 
					
						
							|  |  |  |         set data [read $fd $toread] | 
					
						
							|  |  |  |         set nread [string length $data] | 
					
						
							|  |  |  |         dict append ::redis::state($id) buf $data | 
					
						
							|  |  |  |         # Check if we read a complete bulk reply
 | 
					
						
							|  |  |  |         if {[string length [dict get $::redis::state($id) buf]] == | 
					
						
							|  |  |  |             [dict get $::redis::state($id) bulk]} { | 
					
						
							|  |  |  |             if {[dict get $::redis::state($id) mbulk] == -1} { | 
					
						
							|  |  |  |                 redis_call_callback $id reply \ | 
					
						
							|  |  |  |                     [string range [dict get $::redis::state($id) buf] 0 end-2] | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 dict with ::redis::state($id) { | 
					
						
							|  |  |  |                     lappend reply [string range $buf 0 end-2] | 
					
						
							|  |  |  |                     incr mbulk -1 | 
					
						
							|  |  |  |                     set bulk -1 | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if {[dict get $::redis::state($id) mbulk] == 0} { | 
					
						
							|  |  |  |                     redis_call_callback $id reply \ | 
					
						
							|  |  |  |                         [dict get $::redis::state($id) reply] | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |