Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
1259672f
Commit
1259672f
authored
Nov 03, 2009
by
antirez
Browse files
client libs removed from Redis git
parent
5762b7f0
Changes
110
Expand all
Hide whitespace changes
Inline
Side-by-side
client-libraries/README
deleted
100644 → 0
View file @
5762b7f0
Redis client libraries
----------------------
In this directory you'll find client libraries for different languages.
This are the latest releases available at the time this Redis tar.gz for this
release was created, and are good for most uses, but if you need more fresh
code or recent bugfixes read more.
How to get the lastest versions of client libraries source code
---------------------------------------------------------------
Note that while the pure PHP, Tcl, Python and Ruby_2 (Ruby alternative lib)
libraries are the most uptodate available libraries, all the other libraries
have their own repositories where it's possible to grab the most recent version:
Ruby lib source code:
http://github.com/ezmobius/redis-rb/tree/master
git://github.com/ezmobius/redis-rb.git
Erlang lib source code:
http://bitbucket.org/adroll/erldis/
Perl lib source code:
(web) http://svn.rot13.org/index.cgi/Redis
(svn) svn://svn.rot13.org/Redis/
Redis-php PHP C module:
http://code.google.com/p/phpredis/
Lua lib source code:
http://github.com/nrk/redis-lua/tree/master
git://github.com/nrk/redis-lua.git
Clojure lib source code:
http://github.com/ragnard/redis-clojure/
git://github.com/ragnard/redis-clojure.git
For all the rest check the Redis tarball or Git repository.
client-libraries/clojure/.gitignore
deleted
100644 → 0
View file @
5762b7f0
classes
\#*
.\#*
*.jar
build.properties
\ No newline at end of file
client-libraries/clojure/LICENSE
deleted
100644 → 0
View file @
5762b7f0
Copyright (c) 2009 Ragnar Dahlén (r.dahlen@gmail.com)
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
client-libraries/clojure/README.markdown
deleted
100644 → 0
View file @
5762b7f0
# redis-clojure
A Clojure client library for the
[
Redis
](
http://code.google.com/p/redis
)
key value storage system.
## Dependencies
To use redis-clojure, you'll need:
*
The
[
Clojure
](
http://clojure.org
)
programming language
*
The
[
Clojure-Contrib
](
http://code.google.com/p/clojure-contrib
)
library (for running the tests)
## Building
To build redis-clojure:
ant -Dclojure.jar=/path/to/clojure.jar
This will build
`redis-clojure.jar`
.
## Running tests
To run tests:
ant -Dclojure.jar=/path/to/clojure.jar -Dclojure-contrib.jar=/path/to/clojure-contrib.jar test
*Note*
you need to have
`redis-server`
running first.
## Using
To use redis-clojure in your application, simply make sure either
`redis-clojure.jar`
or the contents of the
`src/`
directory is on your
classpath.
This can be accomplished like so:
(add-classpath "file:///path/to/redis-clojure.jar")
## Examples
Check the
`examples/`
directory.
*Note*
you need to have
`redis-server`
running first.
## Todo
*
Work on performance
*
Maybe implement pipelining
client-libraries/clojure/benchmarks/clojure.clj
deleted
100644 → 0
View file @
5762b7f0
(
add-classpath
"file:///Users/ragge/Projects/clojure/redis-clojure/redis-clojure.jar"
)
(
ns
benchmarks.clojure
(
:use
clojure.contrib.pprint
)
(
:require
redis
))
(
defstruct
benchmark-options
:host
:port
:db
:clients
:requests
:key-size
:keyspace-size
:data-size
)
(
defstruct
client
:id
:request-times
:requests-performed
:requests-per-second
)
(
defstruct
result
:options
:clients
:total-time
:requests
)
(
defmacro
defbenchmark
[
name
&
body
]
(
let
[
benchmark-name
(
symbol
(
str
name
"-benchmark"
))]
`
(
def
~
(
with-meta
benchmark-name
{
:benchmark
true
})
(
fn
~
benchmark-name
[
client
#
options
#
result
#
]
(
redis/with-server
{
:host
(
options
#
:host
)
:port
(
options
#
:port
)
:db
(
options
#
:db
)}
(
let
[
requests
#
(
:requests
options
#
)
requests-done
#
(
:requests
result
#
)]
(
loop
[
requests-performed
#
0
request-times
#
[]]
(
if
(
>=
@
requests-done
#
requests
#
)
(
assoc
client
#
:request-times
request-times
#
:requests-performed
requests-performed
#
)
(
do
(
let
[
start
#
(
System/nanoTime
)]
~@
body
(
let
[
end
#
(
System/nanoTime
)
elapsed
#
(
/
(
float
(
-
end
#
start
#
))
1000000.0
)]
(
dosync
(
commute
requests-done
#
inc
))
(
recur
(
inc
requests-performed
#
)
(
conj
request-times
#
elapsed
#
)))))))))))))
(
defbenchmark
ping
(
redis/ping
))
(
defbenchmark
get
(
redis/get
(
str
"key-"
(
rand-int
1000
))))
(
defbenchmark
set
(
redis/set
(
str
"key-"
(
rand-int
1000
))
"abc"
))
(
defbenchmark
exists-set-and-get
(
let
[
key
(
str
"key-"
(
rand-int
100
))]
(
redis/exists
key
)
(
redis/set
key
"blahongaa!"
)
(
redis/get
key
)))
(
def
*default-options*
(
struct-map
benchmark-options
:host
"127.0.0.1"
:port
6379
:db
15
:clients
1
:requests
10000
))
(
defn
create-clients
[
options
]
(
for
[
id
(
range
(
:clients
options
))]
(
agent
(
struct
client
id
))))
(
defn
create-result
[
options
clients
]
(
let
[
result
(
struct
result
options
clients
0
(
ref
0
))]
result
))
(
defn
requests-by-ms
[
clients
]
(
let
[
all-times
(
apply
concat
(
map
#
(
:request-times
(
deref
%
))
clients
))
all-times-in-ms
(
map
#
(
int
(
/
%
1
))
all-times
)]
(
sort
(
reduce
(
fn
[
m
time
]
(
if
(
m
time
)
(
assoc
m
time
(
inc
(
m
time
)))
(
assoc
m
time
1
)))
{}
all-times-in-ms
))))
(
defn
report-request-times
[
clients
requests
]
(
let
[
requests-dist
(
map
#
(
let
[
perc
(
*
100
(
/
(
last
%
)
requests
))]
(
conj
%
perc
))
(
requests-by-ms
clients
))]
(
loop
[
items
requests-dist
seen
0
]
(
if-not
(
empty?
items
)
(
do
(
let
[
item
(
first
items
)
seen
(
+
seen
(
last
item
))]
(
println
(
format
"%.2f%% < %d ms"
(
float
seen
)
(
inc
(
first
item
))))
(
recur
(
rest
items
)
seen
)))))))
(
defn
report-client-rps
[
client
]
(
let
[{
:keys
[
id
requests-performed
request-times
]}
@
client
]
(
when
(
<
0
requests-performed
)
(
let
[
total-time
(
apply
+
request-times
)
requests-per-second
(
/
(
float
requests-performed
)
total-time
)]
(
println
total-time
)
(
println
(
format
"Client %d: %f rps"
id
(
float
requests-per-second
)))))))
(
defn
report-result
[
result
]
(
let
[{
:keys
[
clients
options
]}
result
name
(
:name
result
)
time
(
:total-time
result
)
time-in-seconds
(
/
time
1000
)
requests
(
deref
(
:requests
result
))
requests-per-second
(
/
requests
time-in-seconds
)
]
(
do
(
println
(
format
"====== %s =====\n"
name
))
(
println
(
format
" %d requests completed in %f seconds\n"
requests
time-in-seconds
))
(
println
(
format
" %d parallel clients\n"
(
:clients
options
)))
(
report-request-times
clients
requests
)
;(dorun (map report-client-rps clients))
(
println
(
format
"%f requests per second\n\n"
requests-per-second
))
)
)
)
(
defn
run-benchmark
[
fn
options
]
(
let
[
clients
(
create-clients
options
)
result
(
create-result
options
clients
)
start
(
System/nanoTime
)]
(
dorun
(
map
#
(
send-off
%
fn
options
result
)
clients
))
(
apply
await
clients
)
(
let
[
elapsed
(
/
(
double
(
-
(
System/nanoTime
)
start
))
1000000.0
)]
(
dorun
(
map
#
(
when
(
agent-errors
%
)
(
pprint
(
agent-errors
%
)))
clients
))
(
assoc
result
:name
(
str
fn
)
:options
options
:clients
clients
:total-time
elapsed
))))
(
defn
find-all-benchmarks
[
ns
]
(
filter
#
(
:benchmark
(
meta
%
))
(
vals
(
ns-map
ns
))))
(
defn
run-and-report
[
fn
options
]
(
let
[
result
(
run-benchmark
fn
options
)]
(
report-result
result
)))
(
defn
run-all-benchmarks
[
ns
]
(
let
[
benchmarks
(
find-all-benchmarks
ns
)]
(
dorun
(
map
#
(
run-and-report
%
*default-options*
)
benchmarks
))))
;(run-all-benchmarks)
;(report-result (run-benchmark ping-benchmark *default-options*))
;(run-benchmark get-benchmark *default-options*)
client-libraries/clojure/benchmarks/ruby.clj
deleted
100644 → 0
View file @
5762b7f0
(
ns
benchmarks.ruby
(
:require
redis
))
(
dotimes
[
n
4
]
(
redis/with-server
{
:db
15
}
(
redis/set
"foo"
"The first line we sent to the server is some text"
)
(
time
(
dotimes
[
i
20000
]
(
let
[
key
(
str
"key"
i
)]
(
redis/set
key
"The first line we sent to the server is some text"
)
(
redis/get
"foo"
))))))
;(redis/with-server
; {}
; (redis/set "foo" "The first line we sent to the server is some text")
; (time
; (dotimes [i 20000]
; (let [key (str "push_trim" i)]
; (redis/lpush key i)
; (redis/ltrim key 0 30)))))
client-libraries/clojure/build.xml
deleted
100644 → 0
View file @
5762b7f0
<project
name=
"redis"
default=
"jar"
>
<description>
Redis client library for Clojure.
</description>
<property
file=
"build.properties"
/>
<property
name=
"dist.dir"
location=
"dist"
/>
<property
name=
"build.dir"
location=
"classes"
/>
<property
name=
"lib.dir"
location=
"lib"
/>
<property
name=
"source.dir"
location=
"src"
/>
<property
name=
"redis-clojure.jar"
location=
"redis-clojure.jar"
/>
<target
name=
"clean"
description=
"Remove generated files"
>
<delete
file=
"redis-clojure.jar"
/>
<delete
dir=
"${build.dir}"
/>
</target>
<target
name=
"init"
depends=
"clean"
>
<tstamp/>
<mkdir
dir=
"${build.dir}"
/>
</target>
<target
name=
"compile"
depends=
"init"
description=
"Compile sources"
>
<java
classname=
"clojure.lang.Compile"
>
<classpath>
<path
location=
"${build.dir}"
/>
<path
location=
"${source.dir}"
/>
<path
location=
"${clojure.jar}"
/>
<path
location=
"${clojure-contrib.jar}"
/>
</classpath>
<sysproperty
key=
"clojure.compile.path"
value=
"${build.dir}"
/>
<arg
value=
"redis"
/>
</java>
</target>
<target
name=
"jar"
description=
"Create jar file"
depends=
"compile"
>
<jar
jarfile=
"${redis-clojure.jar}"
>
<path
location=
"LICENSE"
/>
<!--<fileset dir="${source.dir}" includes="**/*.clj"/>-->
<fileset
dir=
"${build.dir}"
includes=
"**/*.class"
/>
<manifest>
<attribute
name=
"Built-By"
value=
"${user.name}"
/>
</manifest>
</jar>
</target>
<target
name=
"test"
description=
"Run tests"
>
<java
classname=
"clojure.main"
>
<classpath>
<path
location=
"${source.dir}"
/>
<path
location=
"${clojure.jar}"
/>
<path
location=
"${clojure-contrib.jar}"
/>
</classpath>
<arg
value=
"-e"
/>
<arg
value=
"(require 'redis.tests 'redis.tests.internal) (clojure.contrib.test-is/run-tests 'redis.tests 'redis.tests.internal)"
/>
</java>
</target>
<target
name=
"bm"
depends=
"benchmark"
/>
<target
name=
"benchmark"
description=
"Run benchmark"
>
<java
classname=
"clojure.main"
>
<classpath>
<path
location=
"${basedir}"
/>
<path
location=
"${source.dir}"
/>
<!--<path location="${redis-clojure.jar}"/>-->
<path
location=
"${clojure.jar}"
/>
<path
location=
"${clojure-contrib.jar}"
/>
</classpath>
<arg
value=
"-e"
/>
<arg
value=
"(require 'benchmarks.clojure) (benchmarks.clojure/run-all-benchmarks 'benchmarks.clojure)"
/>
</java>
</target>
<target
name=
"benchmark-ruby"
description=
"Run benchmark equivalent to the benchmarks of the Ruby library"
>
<java
classname=
"clojure.main"
>
<classpath>
<path
location=
"${basedir}"
/>
<path
location=
"${source.dir}"
/>
<!--<path location="${redis-clojure.jar}"/>-->
<path
location=
"${clojure.jar}"
/>
<path
location=
"${clojure-contrib.jar}"
/>
</classpath>
<arg
value=
"-e"
/>
<arg
value=
"(require 'benchmarks.ruby)"
/>
</java>
</target>
</project>
client-libraries/clojure/examples/demo.clj
deleted
100644 → 0
View file @
5762b7f0
;;
;; Simple demo of redis-clojure functionality
;;
;; Make sure redis-clojure.jar or the contents of the src/ directory
;; is on the classpath.
;;
;; Either:
;; (add-classpath "file:///path/to/redis-clojure.jar"
;; or:
;; (add-classpath "file:///path/to/redis/src-dir/")
;;
(
add-classpath
"file:///Users/ragge/Projects/clojure/redis-clojure/redis-clojure.jar"
)
(
ns
demo
(
:require
redis
))
(
redis/with-server
{
:host
"127.0.0.1"
:port
6379
:db
0
}
(
do
(
println
"Sending ping"
)
(
println
"Reply:"
(
redis/ping
))
(
println
"Server info:"
)
(
let
[
info
(
redis/info
)]
(
dorun
(
map
(
fn
[
entry
]
(
println
(
str
"- "
(
first
entry
)
": "
(
last
entry
))))
info
)))
(
println
"Setting key 'foo' to 'bar'"
)
(
println
"Reply:"
(
redis/set
"foo"
"bar"
))
(
println
"Getting value of key 'foo'"
)
(
println
"Reply:"
(
redis/get
"foo"
))))
client-libraries/clojure/src/redis.clj
deleted
100644 → 0
View file @
5762b7f0
;(add-classpath "file:///Users/ragge/Projects/clojure/redis-clojure/src/")
(
ns
redis
(
:refer-clojure
:exclude
[
get
set
type
keys
sort
])
(
:use
redis.internal
))
;(set! *warn-on-reflection* true)
(
defmacro
with-server
"Evaluates body in the context of a new connection to a Redis server
then closes the connection.
server-spec is a map with any of the following keys:
:host hostname (default \"127.0.0.1\")
:port port (default 6379)
:db database to use (default 0)"
[
server-spec
&
body
]
`
(
with-server*
~
server-spec
(
fn
[]
(
do
(
redis/select
(
:db
*server*
))
~@
body
))))
;;
;; Reply conversion functions
;;
(
defn
int-to-bool
"Convert integer reply to a boolean value"
[
int
]
(
=
1
int
))
(
defn
string-to-keyword
"Convert a string reply to a keyword"
[
string
]
(
keyword
string
))
(
defn
string-to-seq
"Convert a space separated string to a sequence of words"
[
#^
String
string
]
(
if
(
empty?
string
)
nil
(
re-seq
#
"\S+"
string
)))
(
defn
string-to-map
"Convert strings with format 'key:value\r\n'+ to a map with {key
value} pairs"
[
#^
String
string
]
(
let
[
lines
(
.split
string
"(\\r\\n|:)"
)]
(
apply
hash-map
lines
)))
(
defn
int-to-date
"Return a Date representation of a UNIX timestamp"
[
int
]
(
new
java.util.Date
(
long
int
)))
(
defn
seq-to-set
[
sequence
]
(
clojure.core/set
sequence
))
;;
;; Commands
;;
(
defcommands
;; Connection handling
(
auth
[]
:inline
)
(
quit
[
password
]
:inline
)
(
ping
[]
:inline
)
;; String commands
(
set
[
key
value
]
:bulk
)
(
get
[
key
]
:inline
)
(
getset
[
key
value
]
:bulk
)
(
setnx
[
key
value
]
:bulk
int-to-bool
)
(
incr
[
key
]
:inline
)
(
incrby
[
key
integer
]
:inline
)
(
decr
[
key
]
:inline
)
(
decrby
[
key
integer
]
:inline
)
(
exists
[
key
]
:inline
int-to-bool
)
(
mget
[
key
&
keys
]
:inline
)
(
del
[
key
]
:inline
int-to-bool
)
;; Key space commands
(
type
[
key
]
:inline
string-to-keyword
)
(
keys
[
pattern
]
:inline
string-to-seq
)
(
randomkey
[]
:inline
)
(
rename
[
oldkey
newkey
]
:inline
)
(
renamenx
[
oldkey
newkey
]
:inline
int-to-bool
)
(
dbsize
[]
:inline
)
(
expire
[
key
seconds
]
:inline
int-to-bool
)
(
ttl
[
key
]
:inline
)
;; List commands
(
rpush
[
key
value
]
:bulk
)
(
lpush
[
key
value
]
:bulk
)
(
llen
[
key
]
:inline
)
(
lrange
[
key
start
end
]
:inline
)
(
ltrim
[
key
start
end
]
:inline
)
(
lindex
[
key
index
]
:inline
)
(
lset
[
key
index
value
]
:bulk
)
(
lrem
[
key
count
value
]
:bulk
)
(
lpop
[
key
]
:inline
)
(
rpop
[
key
]
:inline
)
;; Set commands
(
sadd
[
key
member
]
:bulk
int-to-bool
)
(
srem
[
key
member
]
:bulk
int-to-bool
)
(
spop
[
key
]
:inline
)
(
smove
[
srckey
destkey
member
]
:bulk
int-to-bool
)
(
scard
[
key
]
:inline
)
(
sismember
[
key
member
]
:bulk
int-to-bool
)
(
sinter
[
key
&
keys
]
:inline
seq-to-set
)
(
sinterstore
[
destkey
key
&
keys
]
:inline
)
(
sunion
[
key
&
keys
]
:inline
seq-to-set
)
(
sunionstore
[
destkey
key
&
keys
]
:inline
)
(
sdiff
[
key
&
keys
]
:inline
seq-to-set
)
(
sdiffstore
[
destkey
key
&
keys
]
:inline
)
(
smembers
[
key
]
:inline
seq-to-set
)
;; Multiple database handling commands
(
select
[
index
]
:inline
)
(
move
[
key
dbindex
]
:inline
)
(
flushdb
[]
:inline
)
(
flushall
[]
:inline
)
;; Sorting
(
sort
[
key
&
options
]
:sort
)
;; Persistence
(
save
[]
:inline
)
(
bgsave
[]
:inline
)
(
lastsave
[]
:inline
int-to-date
)
(
shutdown
[]
:inline
)
;; Remote control
(
info
[]
:inline
string-to-map
)
;;(monitor [] :inline))
)
client-libraries/clojure/src/redis/internal.clj
deleted
100644 → 0
View file @
5762b7f0
(
ns
redis.internal
(
:import
[
java.io
InputStream
OutputStream
Reader
InputStreamReader
BufferedReader
]
[
java.net
Socket
]))
(
def
*cr*
0
x0d
)
(
def
*lf*
0
x0a
)
(
defn-
cr?
[
c
]
(
=
c
*cr*
))
(
defn-
lf?
[
c
]
(
=
c
*lf*
))
(
defn-
uppercase
[
#^
String
s
]
(
.toUpperCase
s
))
(
defn-
trim
[
#^
String
s
]
(
.trim
s
))
(
defn-
parse-int
[
#^
String
s
]
(
Integer/parseInt
s
))
(
defn-
char-array
[
len
]
(
make-array
Character/TYPE
len
))
(
def
*default-host*
"127.0.0.1"
)
(
def
*default-port*
6379
)
(
def
*default-db*
0
)
(
def
*default-timeout*
5
)
(
defstruct
server
:host
:port
:db
:timeout
:socket
)
(
def
*server*
(
struct-map
server
:host
*default-host*
:port
*default-port*
:db
*default-db*
:timeout
*default-timeout*
;; not yet used
:socket
nil
))
(
defn
connect-to-server
"Create a Socket connected to server"
[
server
]
(
let
[{
:keys
[
host
port
timeout
]}
server
socket
(
Socket.
#^
String
host
#^
Integer
port
)]
(
doto
socket
(
.setTcpNoDelay
true
)
(
.setKeepAlive
true
))))
(
defn
with-server*
[
server-spec
func
]
(
let
[
server
(
merge
*server*
server-spec
)]
(
with-open
[
#^
Socket
socket
(
connect-to-server
server
)]
(
binding
[
*server*
(
assoc
server
:socket
socket
)]
(
func
)))))
(
defn
socket*
[]
(
or
(
:socket
*server*
)
(
throw
(
Exception.
"Not connected to a Redis server"
))))
(
defn
send-command
"Send a command string to server"
[
#^
String
cmd
]
(
let
[
out
(
.getOutputStream
(
#^
Socket
socket*
))
bytes
(
.getBytes
cmd
)]
(
.write
out
bytes
)))
(
defn
read-crlf
"Read a CR+LF combination from Reader"
[
#^
Reader
reader
]
(
let
[
cr
(
.read
reader
)
lf
(
.read
reader
)]
(
when-not
(
and
(
cr?
cr
)
(
lf?
lf
))
(
throw
(
Exception.
"Error reading CR/LF"
)))
nil
))
(
defn
read-line-crlf
"Read from reader until exactly a CR+LF combination is
found. Returns the line read without trailing CR+LF.
This is used instead of Reader.readLine() method since it tries to
read either a CR, a LF or a CR+LF, which we don't want in this
case."
[
#^
Reader
reader
]
(
loop
[
line
[]
c
(
.read
reader
)]
(
when
(
<
c
0
)
(
throw
(
Exception.
"Error reading line: EOF reached before CR/LF sequence"
)))
(
if
(
cr?
c
)
(
let
[
next
(
.read
reader
)]
(
if
(
lf?
next
)
(
apply
str
line
)
(
throw
(
Exception.
"Error reading line: Missing LF"
))))
(
recur
(
conj
line
(
char
c
))
(
.read
reader
)))))
;;
;; Reply dispatching
;;
(
defn
reply-type
([
#^
BufferedReader
reader
]
(
char
(
.read
reader
))))
(
defmulti
parse-reply
reply-type
:default
:unknown
)
(
defn
read-reply
([]
(
let
[
input-stream
(
.getInputStream
(
#^
Socket
socket*
))
reader
(
BufferedReader.
(
InputStreamReader.
input-stream
))]
(
read-reply
reader
)))
([
#^
BufferedReader
reader
]
(
parse-reply
reader
)))
(
defmethod
parse-reply
:unknown
[
#^
BufferedReader
reader
]
(
throw
(
Exception.
(
str
"Unknown reply type:"
))))
(
defmethod
parse-reply
\-
[
#^
BufferedReader
reader
]
(
let
[
error
(
read-line-crlf
reader
)]
(
throw
(
Exception.
(
str
"Server error: "
error
)))))
(
defmethod
parse-reply
\+
[
#^
BufferedReader
reader
]
(
read-line-crlf
reader
))
(
defmethod
parse-reply
\$
[
#^
BufferedReader
reader
]
(
let
[
line
(
read-line-crlf
reader
)
length
(
parse-int
line
)]
(
if
(
<
length
0
)
nil
(
let
[
#^
chars
cbuf
(
char-array
length
)
nread
(
.read
reader
cbuf
0
length
)]
(
if
(
not=
nread
length
)
(
throw
(
Exception.
"Could not read correct number of bytes"
))
(
do
(
read-crlf
reader
)
;; CRLF
(
String.
cbuf
)))))))
(
defmethod
parse-reply
\*
[
#^
BufferedReader
reader
]
(
let
[
line
(
read-line-crlf
reader
)
count
(
parse-int
line
)]
(
if
(
<
count
0
)
nil
(
loop
[
i
count
replies
[]]
(
if
(
zero?
i
)
replies
(
recur
(
dec
i
)
(
conj
replies
(
read-reply
reader
))))))))
(
defmethod
parse-reply
\:
[
#^
BufferedReader
reader
]
(
let
[
line
(
trim
(
read-line-crlf
reader
))
int
(
parse-int
line
)]
int
))
(
defn
str-join
"Join elements in sequence with separator"
[
separator
sequence
]
(
apply
str
(
interpose
separator
sequence
)))
(
defn
inline-command
"Create a string for an inline command"
[
name
&
args
]
(
let
[
cmd
(
str-join
" "
(
conj
args
name
))]
(
str
cmd
"\r\n"
)))
(
defn
bulk-command
"Create a string for an bulk command"
[
name
&
args
]
(
let
[
data
(
str
(
last
args
))
data-length
(
count
(
str
data
))
args*
(
concat
(
butlast
args
)
[
data-length
])
cmd
(
apply
inline-command
name
args*
)]
(
str
cmd
data
"\r\n"
)))
(
defn-
sort-command-args-to-string
[
args
]
(
loop
[
arg-strings
[]
args
args
]
(
if
(
empty?
args
)
(
str-join
" "
arg-strings
)
(
let
[
type
(
first
args
)
args
(
rest
args
)]
(
condp
=
type
:by
(
let
[
pattern
(
first
args
)]
(
recur
(
conj
arg-strings
"BY"
pattern
)
(
rest
args
)))
:limit
(
let
[
start
(
first
args
)
end
(
second
args
)]
(
recur
(
conj
arg-strings
"LIMIT"
start
end
)
(
drop
2
args
)))
:get
(
let
[
pattern
(
first
args
)]
(
recur
(
conj
arg-strings
"GET"
pattern
)
(
rest
args
)))
:alpha
(
recur
(
conj
arg-strings
"ALPHA"
)
args
)
:asc
(
recur
(
conj
arg-strings
"ASC"
)
args
)
:desc
(
recur
(
conj
arg-strings
"DESC"
)
args
)
(
throw
(
Exception.
(
str
"Error parsing SORT arguments: Unknown argument: "
type
))))))))
(
defn
sort-command
[
name
&
args
]
(
when-not
(
=
name
"SORT"
)
(
throw
(
Exception.
"Sort command name must be 'SORT'"
)))
(
let
[
key
(
first
args
)
arg-string
(
sort-command-args-to-string
(
rest
args
))
cmd
(
str
"SORT "
key
)]
(
if
(
empty?
arg-string
)
(
str
cmd
"\r\n"
)
(
str
cmd
" "
arg-string
"\r\n"
))))
(
def
command-fns
{
:inline
'inline-command
:bulk
'bulk-command
:sort
'sort-command
})
(
defn
parse-params
"Return a restructuring of params, which is of form:
[arg* (& more)?]
into
[(arg1 arg2 ..) more]"
[
params
]
(
let
[[
args
rest
]
(
split-with
#
(
not=
%
'&
)
params
)]
[
args
(
last
rest
)]))
(
defmacro
defcommand
"Define a function for Redis command name with parameters
params. Type is one of :inline or :bulk, which determines how the
command string is constructued."
([
name
params
type
]
`
(
defcommand
~
name
~
params
~
type
(
fn
[
reply
#
]
reply
#
)))
([
name
params
type
reply-fn
]
`
(
~
name
~
params
~
type
~
reply-fn
)
(
do
(
let
[
command
(
uppercase
(
str
name
))
command-fn
(
type
command-fns
)
[
command-params
command-params-rest
]
(
parse-params
params
)]
`
(
defn
~
name
~
params
(
let
[
request
#
(
apply
~
command-fn
~
command
~@
command-params
~
command-params-rest
)]
(
send-command
request
#
)
(
~
reply-fn
(
read-reply
)))))
)))
(
defmacro
defcommands
[
&
command-defs
]
`
(
do
~@
(
map
(
fn
[
command-def
]
`
(
defcommand
~@
command-def
))
command-defs
)))
client-libraries/clojure/src/redis/tests.clj
deleted
100644 → 0
View file @
5762b7f0
(
ns
redis.tests
(
:refer-clojure
:exclude
[
get
set
keys
type
sort
])
(
:require
redis
)
(
:use
[
clojure.contrib.test-is
]))
(
defn
server-fixture
[
f
]
(
redis/with-server
{
:host
"127.0.0.1"
:port
6379
:db
15
}
;; String value
(
redis/set
"foo"
"bar"
)
;; List with three items
(
redis/rpush
"list"
"one"
)
(
redis/rpush
"list"
"two"
)
(
redis/rpush
"list"
"three"
)
;; Set with three members
(
redis/sadd
"set"
"one"
)
(
redis/sadd
"set"
"two"
)
(
redis/sadd
"set"
"three"
)
(
f
)
(
redis/flushdb
)))
(
use-fixtures
:each
server-fixture
)
(
deftest
ping
(
is
(
=
"PONG"
(
redis/ping
))))
(
deftest
set
(
redis/set
"bar"
"foo"
)
(
is
(
=
"foo"
(
redis/get
"bar"
)))
(
redis/set
"foo"
"baz"
)
(
is
(
=
"baz"
(
redis/get
"foo"
))))
(
deftest
get
(
is
(
=
nil
(
redis/get
"bar"
)))
(
is
(
=
"bar"
(
redis/get
"foo"
))))
(
deftest
getset
(
is
(
=
nil
(
redis/getset
"bar"
"foo"
)))
(
is
(
=
"foo"
(
redis/get
"bar"
)))
(
is
(
=
"bar"
(
redis/getset
"foo"
"baz"
)))
(
is
(
=
"baz"
(
redis/get
"foo"
))))
(
deftest
mget
(
is
(
=
[
nil
]
(
redis/mget
"bar"
)))
(
redis/set
"bar"
"baz"
)
(
redis/set
"baz"
"buz"
)
(
is
(
=
[
"bar"
]
(
redis/mget
"foo"
)))
(
is
(
=
[
"bar"
"baz"
]
(
redis/mget
"foo"
"bar"
)))
(
is
(
=
[
"bar"
"baz"
"buz"
]
(
redis/mget
"foo"
"bar"
"baz"
)))
(
is
(
=
[
"bar"
nil
"buz"
]
(
redis/mget
"foo"
"bra"
"baz"
)))
)
(
deftest
setnx
(
is
(
=
true
(
redis/setnx
"bar"
"foo"
)))
(
is
(
=
"foo"
(
redis/get
"bar"
)))
(
is
(
=
false
(
redis/setnx
"foo"
"baz"
)))
(
is
(
=
"bar"
(
redis/get
"foo"
))))
(
deftest
incr
(
is
(
=
1
(
redis/incr
"nonexistent"
)))
(
is
(
=
1
(
redis/incr
"foo"
)))
(
is
(
=
2
(
redis/incr
"foo"
))))
(
deftest
incrby
(
is
(
=
42
(
redis/incrby
"nonexistent"
42
)))
(
is
(
=
0
(
redis/incrby
"foo"
0
)))
(
is
(
=
5
(
redis/incrby
"foo"
5
))))
(
deftest
decr
(
is
(
=
-1
(
redis/decr
"nonexistent"
)))
(
is
(
=
-1
(
redis/decr
"foo"
)))
(
is
(
=
-2
(
redis/decr
"foo"
))))
(
deftest
decrby
(
is
(
=
-42
(
redis/decrby
"nonexistent"
42
)))
(
is
(
=
0
(
redis/decrby
"foo"
0
)))
(
is
(
=
-5
(
redis/decrby
"foo"
5
))))
(
deftest
exists
(
is
(
=
true
(
redis/exists
"foo"
)))
(
is
(
=
false
(
redis/exists
"nonexistent"
))))
(
deftest
del
(
is
(
=
false
(
redis/del
"nonexistent"
)))
(
is
(
=
true
(
redis/del
"foo"
)))
(
is
(
=
nil
(
redis/get
"foo"
))))
(
deftest
type
(
is
(
=
:none
(
redis/type
"nonexistent"
)))
(
is
(
=
:string
(
redis/type
"foo"
)))
(
is
(
=
:list
(
redis/type
"list"
)))
(
is
(
=
:set
(
redis/type
"set"
))))
(
deftest
keys
(
is
(
=
nil
(
redis/keys
"a*"
)))
(
is
(
=
[
"foo"
]
(
redis/keys
"f*"
)))
(
is
(
=
[
"foo"
]
(
redis/keys
"f?o"
)))
(
redis/set
"fuu"
"baz"
)
(
is
(
=
#
{
"foo"
"fuu"
}
(
clojure.core/set
(
redis/keys
"f*"
)))))
(
deftest
randomkey
(
redis/flushdb
)
(
redis/set
"foo"
"bar"
)
(
is
(
=
"foo"
(
redis/randomkey
)))
(
redis/flushdb
)
(
is
(
=
""
(
redis/randomkey
))))
(
deftest
rename
(
is
(
thrown?
Exception
(
redis/rename
"foo"
"foo"
)))
(
is
(
thrown?
Exception
(
redis/rename
"nonexistent"
"foo"
)))
(
redis/rename
"foo"
"bar"
)
(
is
(
=
"bar"
(
redis/get
"bar"
)))
(
is
(
=
nil
(
redis/get
"foo"
)))
(
redis/set
"foo"
"bar"
)
(
redis/set
"bar"
"baz"
)
(
redis/rename
"foo"
"bar"
)
(
is
(
=
"bar"
(
redis/get
"bar"
)))
(
is
(
=
nil
(
redis/get
"foo"
)))
)
(
deftest
renamenx
(
is
(
thrown?
Exception
(
redis/renamenx
"foo"
"foo"
)))
(
is
(
thrown?
Exception
(
redis/renamenx
"nonexistent"
"foo"
)))
(
is
(
=
true
(
redis/renamenx
"foo"
"bar"
)))
(
is
(
=
"bar"
(
redis/get
"bar"
)))
(
is
(
=
nil
(
redis/get
"foo"
)))
(
redis/set
"foo"
"bar"
)
(
redis/set
"bar"
"baz"
)
(
is
(
=
false
(
redis/renamenx
"foo"
"bar"
)))
)
(
deftest
dbsize
(
let
[
size-before
(
redis/dbsize
)]
(
redis/set
"anewkey"
"value"
)
(
let
[
size-after
(
redis/dbsize
)]
(
is
(
=
size-after
(
+
1
size-before
))))))
(
deftest
expire
(
is
(
=
true
(
redis/expire
"foo"
1
)))
(
Thread/sleep
2000
)
(
is
(
=
false
(
redis/exists
"foo"
)))
(
redis/set
"foo"
"bar"
)
(
is
(
=
true
(
redis/expire
"foo"
20
)))
(
is
(
=
false
(
redis/expire
"foo"
10
)))
(
is
(
=
false
(
redis/expire
"nonexistent"
42
)))
)
(
deftest
ttl
(
is
(
=
-1
(
redis/ttl
"nonexistent"
)))
(
is
(
=
-1
(
redis/ttl
"foo"
)))
(
redis/expire
"foo"
42
)
(
is
(
<
40
(
redis/ttl
"foo"
))))
;;
;; List commands
;;
(
deftest
rpush
(
is
(
thrown?
Exception
(
redis/rpush
"foo"
)))
(
redis/rpush
"newlist"
"one"
)
(
is
(
=
1
(
redis/llen
"newlist"
)))
(
is
(
=
"one"
(
redis/lindex
"newlist"
0
)))
(
redis/del
"newlist"
)
(
redis/rpush
"list"
"item"
)
(
is
(
=
"item"
(
redis/rpop
"list"
))))
(
deftest
lpush
(
is
(
thrown?
Exception
(
redis/lpush
"foo"
)))
(
redis/lpush
"newlist"
"item"
)
(
is
(
=
1
(
redis/llen
"newlist"
)))
(
is
(
=
"item"
(
redis/lindex
"newlist"
0
)))
(
redis/lpush
"list"
"item"
)
(
is
(
=
"item"
(
redis/lpop
"list"
))))
(
deftest
llen
(
is
(
thrown?
Exception
(
redis/llen
"foo"
)))
(
is
(
=
0
(
redis/llen
"newlist"
)))
(
is
(
=
3
(
redis/llen
"list"
))))
(
deftest
lrange
(
is
(
thrown?
Exception
(
redis/lrange
"foo"
0
1
)))
(
is
(
=
nil
(
redis/lrange
"newlist"
0
42
)))
(
is
(
=
[
"one"
]
(
redis/lrange
"list"
0
0
)))
(
is
(
=
[
"three"
]
(
redis/lrange
"list"
-1
-1
)))
(
is
(
=
[
"one"
"two"
]
(
redis/lrange
"list"
0
1
)))
(
is
(
=
[
"one"
"two"
"three"
]
(
redis/lrange
"list"
0
2
)))
(
is
(
=
[
"one"
"two"
"three"
]
(
redis/lrange
"list"
0
42
)))
(
is
(
=
[]
(
redis/lrange
"list"
42
0
)))
)
;; TBD
(
deftest
ltrim
(
is
(
thrown?
Exception
(
redis/ltrim
"foo"
0
0
))))
(
deftest
lindex
(
is
(
thrown?
Exception
(
redis/lindex
"foo"
0
)))
(
is
(
=
nil
(
redis/lindex
"list"
42
)))
(
is
(
=
nil
(
redis/lindex
"list"
-4
)))
(
is
(
=
"one"
(
redis/lindex
"list"
0
)))
(
is
(
=
"three"
(
redis/lindex
"list"
2
)))
(
is
(
=
"three"
(
redis/lindex
"list"
-1
))))
(
deftest
lset
(
is
(
thrown?
Exception
(
redis/lset
"foo"
0
"bar"
)))
(
is
(
thrown?
Exception
(
redis/lset
"list"
42
"value"
)))
(
redis/lset
"list"
0
"test"
)
(
is
(
=
"test"
(
redis/lindex
"list"
0
)))
(
redis/lset
"list"
2
"test2"
)
(
is
(
=
"test2"
(
redis/lindex
"list"
2
)))
(
redis/lset
"list"
-1
"test3"
)
(
is
(
=
"test3"
(
redis/lindex
"list"
2
))))
(
deftest
lrem
(
is
(
thrown?
Exception
(
redis/lrem
"foo"
0
"bar"
)))
(
is
(
=
0
(
redis/lrem
"newlist"
0
""
)))
(
is
(
=
1
(
redis/lrem
"list"
1
"two"
)))
(
is
(
=
1
(
redis/lrem
"list"
42
"three"
)))
(
is
(
=
1
(
redis/llen
"list"
))))
(
deftest
lpop
(
is
(
thrown?
Exception
(
redis/lpop
"foo"
)))
(
is
(
=
nil
(
redis/lpop
"newlist"
)))
(
is
(
=
"one"
(
redis/lpop
"list"
)))
(
is
(
=
2
(
redis/llen
"list"
))))
(
deftest
rpop
(
is
(
thrown?
Exception
(
redis/rpop
"foo"
)))
(
is
(
=
nil
(
redis/rpop
"newlist"
)))
(
is
(
=
"three"
(
redis/rpop
"list"
)))
(
is
(
=
2
(
redis/llen
"list"
))))
;;
;; Set commands
;;
(
deftest
sadd
(
is
(
thrown?
Exception
(
redis/sadd
"foo"
"bar"
)))
(
is
(
=
true
(
redis/sadd
"newset"
"member"
)))
(
is
(
=
true
(
redis/sismember
"newset"
"member"
)))
(
is
(
=
false
(
redis/sadd
"set"
"two"
)))
(
is
(
=
true
(
redis/sadd
"set"
"four"
)))
(
is
(
=
true
(
redis/sismember
"set"
"four"
))))
(
deftest
srem
(
is
(
thrown?
Exception
(
redis/srem
"foo"
"bar"
)))
(
is
(
=
false
(
redis/srem
"newset"
"member"
)))
(
is
(
=
true
(
redis/srem
"set"
"two"
)))
(
is
(
=
false
(
redis/sismember
"set"
"two"
)))
(
is
(
=
false
(
redis/srem
"set"
"blahonga"
))))
(
deftest
spop
(
is
(
thrown?
Exception
(
redis/spop
"foo"
"bar"
)))
(
is
(
=
nil
(
redis/spop
"newset"
)))
(
is
(
contains?
#
{
"one"
"two"
"three"
}
(
redis/spop
"set"
))))
(
deftest
smove
(
is
(
thrown?
Exception
(
redis/smove
"foo"
"set"
"one"
)))
(
is
(
thrown?
Exception
(
redis/smove
"set"
"foo"
"one"
)))
(
redis/sadd
"set1"
"two"
)
(
is
(
=
false
(
redis/smove
"set"
"set1"
"four"
)))
(
is
(
=
#
{
"two"
}
(
redis/smembers
"set1"
)))
(
is
(
=
true
(
redis/smove
"set"
"set1"
"one"
)))
(
is
(
=
#
{
"one"
"two"
}
(
redis/smembers
"set1"
))))
(
deftest
scard
(
is
(
thrown?
Exception
(
redis/scard
"foo"
)))
(
is
(
=
3
(
redis/scard
"set"
))))
(
deftest
sismember
(
is
(
thrown?
Exception
(
redis/sismember
"foo"
"bar"
)))
(
is
(
=
false
(
redis/sismember
"set"
"blahonga"
)))
(
is
(
=
true
(
redis/sismember
"set"
"two"
))))
(
deftest
sinter
(
is
(
thrown?
Exception
(
redis/sinter
"foo"
"set"
)))
(
is
(
=
#
{}
(
redis/sinter
"nonexistent"
)))
(
redis/sadd
"set1"
"one"
)
(
redis/sadd
"set1"
"four"
)
(
is
(
=
#
{
"one"
"two"
"three"
}
(
redis/sinter
"set"
)))
(
is
(
=
#
{
"one"
}
(
redis/sinter
"set"
"set1"
)))
(
is
(
=
#
{}
(
redis/sinter
"set"
"set1"
"nonexistent"
))))
(
deftest
sinterstore
(
redis/sinterstore
"foo"
"set"
)
(
is
(
=
#
{
"one"
"two"
"three"
}
(
redis/smembers
"foo"
)))
(
redis/sadd
"set1"
"one"
)
(
redis/sadd
"set1"
"four"
)
(
redis/sinterstore
"newset"
"set"
"set1"
)
(
is
(
=
#
{
"one"
}
(
redis/smembers
"newset"
))))
(
deftest
sunion
(
is
(
thrown?
Exception
(
redis/sunion
"foo"
"set"
)))
(
is
(
=
#
{}
(
redis/sunion
"nonexistent"
)))
(
redis/sadd
"set1"
"one"
)
(
redis/sadd
"set1"
"four"
)
(
is
(
=
#
{
"one"
"two"
"three"
}
(
redis/sunion
"set"
)))
(
is
(
=
#
{
"one"
"two"
"three"
"four"
}
(
redis/sunion
"set"
"set1"
)))
(
is
(
=
#
{
"one"
"two"
"three"
"four"
}
(
redis/sunion
"set"
"set1"
"nonexistent"
))))
(
deftest
sunionstore
(
redis/sunionstore
"foo"
"set"
)
(
is
(
=
#
{
"one"
"two"
"three"
}
(
redis/smembers
"foo"
)))
(
redis/sadd
"set1"
"one"
)
(
redis/sadd
"set1"
"four"
)
(
redis/sunionstore
"newset"
"set"
"set1"
)
(
is
(
=
#
{
"one"
"two"
"three"
"four"
}
(
redis/smembers
"newset"
))))
(
deftest
sdiff
(
is
(
thrown?
Exception
(
redis/sdiff
"foo"
"set"
)))
(
is
(
=
#
{}
(
redis/sdiff
"nonexistent"
)))
(
redis/sadd
"set1"
"one"
)
(
redis/sadd
"set1"
"four"
)
(
is
(
=
#
{
"one"
"two"
"three"
}
(
redis/sdiff
"set"
)))
(
is
(
=
#
{
"two"
"three"
}
(
redis/sdiff
"set"
"set1"
)))
(
is
(
=
#
{
"two"
"three"
}
(
redis/sdiff
"set"
"set1"
"nonexistent"
))))
(
deftest
sdiffstore
(
redis/sdiffstore
"foo"
"set"
)
(
is
(
=
#
{
"one"
"two"
"three"
}
(
redis/smembers
"foo"
)))
(
redis/sadd
"set1"
"one"
)
(
redis/sadd
"set1"
"four"
)
(
redis/sdiffstore
"newset"
"set"
"set1"
)
(
is
(
=
#
{
"two"
"three"
}
(
redis/smembers
"newset"
))))
(
deftest
smembers
(
is
(
thrown?
Exception
(
redis/smembers
"foo"
)))
(
is
(
=
#
{
"one"
"two"
"three"
}
(
redis/smembers
"set"
))))
;;
;; Sorting
;;
(
deftest
sort
(
redis/lpush
"ids"
1
)
(
redis/lpush
"ids"
4
)
(
redis/lpush
"ids"
2
)
(
redis/lpush
"ids"
3
)
(
redis/set
"object_1"
"one"
)
(
redis/set
"object_2"
"two"
)
(
redis/set
"object_3"
"three"
)
(
redis/set
"object_4"
"four"
)
(
redis/set
"name_1"
"Derek"
)
(
redis/set
"name_2"
"Charlie"
)
(
redis/set
"name_3"
"Bob"
)
(
redis/set
"name_4"
"Alice"
)
(
is
(
=
[
"one"
"two"
"three"
]
(
redis/sort
"list"
)))
(
is
(
=
[
"one"
"three"
"two"
]
(
redis/sort
"list"
:alpha
)))
(
is
(
=
[
"1"
"2"
"3"
"4"
]
(
redis/sort
"ids"
)))
(
is
(
=
[
"1"
"2"
"3"
"4"
]
(
redis/sort
"ids"
:asc
)))
(
is
(
=
[
"4"
"3"
"2"
"1"
]
(
redis/sort
"ids"
:desc
)))
(
is
(
=
[
"1"
"2"
]
(
redis/sort
"ids"
:asc
:limit
0
2
)))
(
is
(
=
[
"4"
"3"
]
(
redis/sort
"ids"
:desc
:limit
0
2
)))
(
is
(
=
[
"4"
"3"
"2"
"1"
]
(
redis/sort
"ids"
:by
"name_*"
:alpha
)))
(
is
(
=
[
"one"
"two"
"three"
"four"
]
(
redis/sort
"ids"
:get
"object_*"
)))
(
is
(
=
[
"one"
"two"
]
(
redis/sort
"ids"
:by
"name_*"
:alpha
:limit
0
2
:desc
:get
"object_*"
))))
;;
;; Multiple database handling commands
;;
(
deftest
select
(
redis/select
0
)
(
is
(
=
nil
(
redis/get
"akeythat_probably_doesnotexsistindb0"
))))
(
deftest
flushdb
(
redis/flushdb
)
(
is
(
=
0
(
redis/dbsize
))))
;;
;; Persistence commands
;;
(
deftest
save
(
redis/save
))
(
deftest
bgsave
(
redis/bgsave
))
(
deftest
lastsave
(
let
[
ages-ago
(
new
java.util.Date
(
long
1
))]
(
is
(
.before
ages-ago
(
redis/lastsave
)))))
client-libraries/clojure/src/redis/tests/internal.clj
deleted
100644 → 0
View file @
5762b7f0
(
ns
redis.tests.internal
(
:require
[
redis.internal
:as
redis
])
(
:use
[
clojure.contrib.test-is
])
(
:import
[
java.io
StringReader
BufferedReader
]))
;;
;; Helpers
;;
(
defn-
wrap-in-reader
[
#^
String
s
]
(
let
[
reader
(
BufferedReader.
(
StringReader.
s
))]
reader
))
(
defn-
read-reply
[
#^
String
s
]
(
redis/read-reply
(
wrap-in-reader
s
)))
;;
;; Command generation
;;
(
deftest
inline-command
(
is
(
=
"FOO\r\n"
(
redis/inline-command
"FOO"
)))
(
is
(
=
"FOO bar\r\n"
(
redis/inline-command
"FOO"
"bar"
)))
(
is
(
=
"FOO bar baz\r\n"
(
redis/inline-command
"FOO"
"bar"
"baz"
))))
(
deftest
bulk-command
(
is
(
=
"FOO 3\r\nbar\r\n"
(
redis/bulk-command
"FOO"
"bar"
)))
(
is
(
=
"SET foo 3\r\nbar\r\n"
(
redis/bulk-command
"SET"
"foo"
"bar"
)))
(
is
(
=
"SET foo bar 3\r\nbaz\r\n"
(
redis/bulk-command
"SET"
"foo"
"bar"
"baz"
))))
(
deftest
sort-command
(
is
(
=
"SORT key\r\n"
(
redis/sort-command
"SORT"
"key"
)))
(
is
(
=
"SORT key BY pattern\r\n"
(
redis/sort-command
"SORT"
"key"
:by
"pattern"
)))
(
is
(
=
"SORT key LIMIT 0 10\r\n"
(
redis/sort-command
"SORT"
"key"
:limit
0
10
)))
(
is
(
=
"SORT key ASC\r\n"
(
redis/sort-command
"SORT"
"key"
:asc
)))
(
is
(
=
"SORT key DESC\r\n"
(
redis/sort-command
"SORT"
"key"
:desc
)))
(
is
(
=
"SORT key ALPHA\r\n"
(
redis/sort-command
"SORT"
"key"
:alpha
)))
(
is
(
=
"SORT key GET object_* GET object2_*\r\n"
(
redis/sort-command
"SORT"
"key"
:get
"object_*"
:get
"object2_*"
)))
(
is
(
=
"SORT key BY weight_* LIMIT 0 10 GET object_* ALPHA DESC\r\n"
(
redis/sort-command
"SORT"
"key"
:by
"weight_*"
:limit
0
10
:get
"object_*"
:alpha
:desc
))))
;;
;; Reply parsing
;;
(
deftest
read-crlf
(
is
(
thrown?
Exception
(
redis/read-crlf
(
wrap-in-reader
"\n"
))))
(
is
(
thrown?
Exception
(
redis/read-crlf
(
wrap-in-reader
""
))))
(
is
(
thrown?
Exception
(
redis/read-crlf
(
wrap-in-reader
"\r1"
))))
(
is
(
=
nil
(
redis/read-crlf
(
wrap-in-reader
"\r\n"
)))))
;; (deftest read-newline-crlf
;; (is (thrown? Exception
;; (redis/read-line-crlf (wrap-in-reader "")))))
;;
;; Reply parsing
;;
(
deftest
reply
(
is
(
thrown?
Exception
(
read-reply
""
)))
(
is
(
thrown?
Exception
(
read-reply
"\r\n"
))))
(
deftest
error-reply
(
is
(
thrown?
Exception
(
read-reply
"-\r\n"
)))
(
is
(
thrown-with-msg?
Exception
#
".*Test"
(
read-reply
"-Test\r\n"
))))
(
deftest
simple-reply
(
is
(
thrown?
Exception
(
read-reply
"+"
)))
(
is
(
=
""
(
read-reply
"+\r\n"
)))
(
is
(
=
"foobar"
(
read-reply
"+foobar\r\n"
))))
(
deftest
integer-reply
(
is
(
thrown?
Exception
(
read-reply
":\r\n"
)))
(
is
(
=
0
(
read-reply
":0\r\n"
)))
(
is
(
=
42
(
read-reply
":42\r\n"
)))
(
is
(
=
42
(
read-reply
": 42 \r\n"
)))
(
is
(
=
429348754
(
read-reply
":429348754\r\n"
))))
(
deftest
bulk-reply
(
is
(
thrown?
Exception
(
read-reply
"$\r\n"
)))
(
is
(
thrown?
Exception
(
read-reply
"$2\r\n1\r\n"
)))
(
is
(
thrown?
Exception
(
read-reply
"$3\r\n1\r\n"
)))
(
is
(
=
nil
(
read-reply
"$-1\r\n"
)))
(
is
(
=
"foobar"
(
read-reply
"$6\r\nfoobar\r\n"
)))
(
is
(
=
"foo\r\nbar"
(
read-reply
"$8\r\nfoo\r\nbar\r\n"
))))
(
deftest
multi-bulk-reply
(
is
(
thrown?
Exception
(
read-reply
"*1\r\n"
)))
(
is
(
thrown?
Exception
(
read-reply
"*4\r\n:0\r\n:0\r\n:0\r\n"
)))
(
is
(
=
nil
(
read-reply
"*-1\r\n"
)))
(
is
(
=
[
1
]
(
read-reply
"*1\r\n:1\r\n"
)))
(
is
(
=
[
"foo"
"bar"
]
(
read-reply
"*2\r\n+foo\r\n+bar\r\n"
)))
(
is
(
=
[
1
"foo"
"foo\r\nbar"
]
(
read-reply
"*3\r\n:1\r\n+foo\r\n$8\r\nfoo\r\nbar\r\n"
))))
client-libraries/cpp/Makefile
deleted
100644 → 0
View file @
5762b7f0
# Redis C++ Client Library Makefile
#CFLAGS?= -pedantic -O2 -Wall -W -DNDEBUG
CFLAGS
?=
-pedantic
-O0
-W
-DDEBUG
-g
CC
=
g++
CLIENTOBJS
=
anet.o redisclient.o
LIBNAME
=
libredisclient.a
TESTAPP
=
test_client
TESTAPPOBJS
=
test_client.o
TESTAPPLIBS
=
$(LIBNAME)
-lstdc
++
all
:
$(LIBNAME) $(TESTAPP)
$(LIBNAME)
:
$(CLIENTOBJS)
ar rcs
$(LIBNAME)
$(CLIENTOBJS)
.c.o
:
$(CC)
-c
$(CFLAGS)
$<
.cpp.o
:
$(CC)
-c
$(CFLAGS)
$<
$(TESTAPP)
:
$(LIBNAME) $(TESTAPPOBJS)
$(CC)
-o
$(TESTAPP)
$(TESTAPPOBJS)
$(TESTAPPLIBS)
test
:
$(TESTAPP)
@
./test_client
check
:
test
clean
:
rm
-rf
$(LIBNAME)
*
.o
$(TESTAPP)
dep
:
$(CC)
-MM
*
.c
*
.cpp
log
:
git log
'--pretty=format:%ad %s'
--date
=
short
>
Changelog
anet.o
:
anet.c fmacros.h anet.h
redisclient.o
:
redisclient.cpp redisclient.h anet.h
client-libraries/cpp/README.rst
deleted
100644 → 0
View file @
5762b7f0
redis-cpp-client
================
* A C++ client for the Redis_ key-value database (which is hosted at github_).
* This client has no external dependencies other than g++ (no Boost for instance).
* It uses anet from antirez_ (redis' author), which is bundled.
* This client is licensed under the same license as redis.
* Tested on Linux and Mac OS X.
* This is a work in progress. I will update this README when the client is "done".
If I had to put a version number on it right now, I'd call it version 0.85
.. _Redis: http://code.google.com/p/redis/
.. _github: http://github.com/antirez/redis/tree/master
.. _antirez: https://github.com/antirez
client-libraries/cpp/TODO
deleted
100644 → 0
View file @
5762b7f0
general:
- check for complete support for 0.100 (compiles; existing tests pass)
command handlers:
- support DEL as vararg
- support MLLEN and MSCARD
- support SDIFF
- support SDIFFSTORE
unit tests:
- sort with limit
- sort lexicographically
- sort with pattern and weights
extras:
- benchmarking "test" app
- consistent hashing?
maybe/someday:
- make all string literals constants so they can be easily changed
- add conveniences that store a std::set in its entirety (same for std::list, std::vector)
client-libraries/cpp/anet.c
deleted
100644 → 0
View file @
5762b7f0
/* anet.c -- Basic TCP socket stuff made a bit less boring
*
* Copyright (c) 2006-2009, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "fmacros.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include "anet.h"
static
void
anetSetError
(
char
*
err
,
const
char
*
fmt
,
...)
{
va_list
ap
;
if
(
!
err
)
return
;
va_start
(
ap
,
fmt
);
vsnprintf
(
err
,
ANET_ERR_LEN
,
fmt
,
ap
);
va_end
(
ap
);
}
int
anetNonBlock
(
char
*
err
,
int
fd
)
{
int
flags
;
/* Set the socket nonblocking.
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
* interrupted by a signal. */
if
((
flags
=
fcntl
(
fd
,
F_GETFL
))
==
-
1
)
{
anetSetError
(
err
,
"fcntl(F_GETFL): %s
\n
"
,
strerror
(
errno
));
return
ANET_ERR
;
}
if
(
fcntl
(
fd
,
F_SETFL
,
flags
|
O_NONBLOCK
)
==
-
1
)
{
anetSetError
(
err
,
"fcntl(F_SETFL,O_NONBLOCK): %s
\n
"
,
strerror
(
errno
));
return
ANET_ERR
;
}
return
ANET_OK
;
}
int
anetTcpNoDelay
(
char
*
err
,
int
fd
)
{
int
yes
=
1
;
if
(
setsockopt
(
fd
,
IPPROTO_TCP
,
TCP_NODELAY
,
&
yes
,
sizeof
(
yes
))
==
-
1
)
{
anetSetError
(
err
,
"setsockopt TCP_NODELAY: %s
\n
"
,
strerror
(
errno
));
return
ANET_ERR
;
}
return
ANET_OK
;
}
int
anetSetSendBuffer
(
char
*
err
,
int
fd
,
int
buffsize
)
{
if
(
setsockopt
(
fd
,
SOL_SOCKET
,
SO_SNDBUF
,
&
buffsize
,
sizeof
(
buffsize
))
==
-
1
)
{
anetSetError
(
err
,
"setsockopt SO_SNDBUF: %s
\n
"
,
strerror
(
errno
));
return
ANET_ERR
;
}
return
ANET_OK
;
}
int
anetTcpKeepAlive
(
char
*
err
,
int
fd
)
{
int
yes
=
1
;
if
(
setsockopt
(
fd
,
SOL_SOCKET
,
SO_KEEPALIVE
,
&
yes
,
sizeof
(
yes
))
==
-
1
)
{
anetSetError
(
err
,
"setsockopt SO_KEEPALIVE: %s
\n
"
,
strerror
(
errno
));
return
ANET_ERR
;
}
return
ANET_OK
;
}
int
anetResolve
(
char
*
err
,
char
*
host
,
char
*
ipbuf
)
{
struct
sockaddr_in
sa
;
sa
.
sin_family
=
AF_INET
;
if
(
inet_aton
(
host
,
&
sa
.
sin_addr
)
==
0
)
{
struct
hostent
*
he
;
he
=
gethostbyname
(
host
);
if
(
he
==
NULL
)
{
anetSetError
(
err
,
"can't resolve: %s
\n
"
,
host
);
return
ANET_ERR
;
}
memcpy
(
&
sa
.
sin_addr
,
he
->
h_addr
,
sizeof
(
struct
in_addr
));
}
strcpy
(
ipbuf
,
inet_ntoa
(
sa
.
sin_addr
));
return
ANET_OK
;
}
#define ANET_CONNECT_NONE 0
#define ANET_CONNECT_NONBLOCK 1
static
int
anetTcpGenericConnect
(
char
*
err
,
char
*
addr
,
int
port
,
int
flags
)
{
int
s
,
on
=
1
;
struct
sockaddr_in
sa
;
if
((
s
=
socket
(
AF_INET
,
SOCK_STREAM
,
0
))
==
-
1
)
{
anetSetError
(
err
,
"creating socket: %s
\n
"
,
strerror
(
errno
));
return
ANET_ERR
;
}
/* Make sure connection-intensive things like the redis benckmark
* will be able to close/open sockets a zillion of times */
setsockopt
(
s
,
SOL_SOCKET
,
SO_REUSEADDR
,
&
on
,
sizeof
(
on
));
sa
.
sin_family
=
AF_INET
;
sa
.
sin_port
=
htons
(
port
);
if
(
inet_aton
(
addr
,
&
sa
.
sin_addr
)
==
0
)
{
struct
hostent
*
he
;
he
=
gethostbyname
(
addr
);
if
(
he
==
NULL
)
{
anetSetError
(
err
,
"can't resolve: %s
\n
"
,
addr
);
close
(
s
);
return
ANET_ERR
;
}
memcpy
(
&
sa
.
sin_addr
,
he
->
h_addr
,
sizeof
(
struct
in_addr
));
}
if
(
flags
&
ANET_CONNECT_NONBLOCK
)
{
if
(
anetNonBlock
(
err
,
s
)
!=
ANET_OK
)
return
ANET_ERR
;
}
if
(
connect
(
s
,
(
struct
sockaddr
*
)
&
sa
,
sizeof
(
sa
))
==
-
1
)
{
if
(
errno
==
EINPROGRESS
&&
flags
&
ANET_CONNECT_NONBLOCK
)
return
s
;
anetSetError
(
err
,
"connect: %s
\n
"
,
strerror
(
errno
));
close
(
s
);
return
ANET_ERR
;
}
return
s
;
}
int
anetTcpConnect
(
char
*
err
,
char
*
addr
,
int
port
)
{
return
anetTcpGenericConnect
(
err
,
addr
,
port
,
ANET_CONNECT_NONE
);
}
int
anetTcpNonBlockConnect
(
char
*
err
,
char
*
addr
,
int
port
)
{
return
anetTcpGenericConnect
(
err
,
addr
,
port
,
ANET_CONNECT_NONBLOCK
);
}
/* Like read(2) but make sure 'count' is read before to return
* (unless error or EOF condition is encountered) */
int
anetRead
(
int
fd
,
char
*
buf
,
int
count
)
{
int
nread
,
totlen
=
0
;
while
(
totlen
!=
count
)
{
nread
=
read
(
fd
,
buf
,
count
-
totlen
);
if
(
nread
==
0
)
return
totlen
;
if
(
nread
==
-
1
)
return
-
1
;
totlen
+=
nread
;
buf
+=
nread
;
}
return
totlen
;
}
/* Like write(2) but make sure 'count' is read before to return
* (unless error is encountered) */
int
anetWrite
(
int
fd
,
char
*
buf
,
int
count
)
{
int
nwritten
,
totlen
=
0
;
while
(
totlen
!=
count
)
{
nwritten
=
write
(
fd
,
buf
,
count
-
totlen
);
if
(
nwritten
==
0
)
return
totlen
;
if
(
nwritten
==
-
1
)
return
-
1
;
totlen
+=
nwritten
;
buf
+=
nwritten
;
}
return
totlen
;
}
int
anetTcpServer
(
char
*
err
,
int
port
,
char
*
bindaddr
)
{
int
s
,
on
=
1
;
struct
sockaddr_in
sa
;
if
((
s
=
socket
(
AF_INET
,
SOCK_STREAM
,
0
))
==
-
1
)
{
anetSetError
(
err
,
"socket: %s
\n
"
,
strerror
(
errno
));
return
ANET_ERR
;
}
if
(
setsockopt
(
s
,
SOL_SOCKET
,
SO_REUSEADDR
,
&
on
,
sizeof
(
on
))
==
-
1
)
{
anetSetError
(
err
,
"setsockopt SO_REUSEADDR: %s
\n
"
,
strerror
(
errno
));
close
(
s
);
return
ANET_ERR
;
}
memset
(
&
sa
,
0
,
sizeof
(
sa
));
sa
.
sin_family
=
AF_INET
;
sa
.
sin_port
=
htons
(
port
);
sa
.
sin_addr
.
s_addr
=
htonl
(
INADDR_ANY
);
if
(
bindaddr
)
{
if
(
inet_aton
(
bindaddr
,
&
sa
.
sin_addr
)
==
0
)
{
anetSetError
(
err
,
"Invalid bind address
\n
"
);
close
(
s
);
return
ANET_ERR
;
}
}
if
(
bind
(
s
,
(
struct
sockaddr
*
)
&
sa
,
sizeof
(
sa
))
==
-
1
)
{
anetSetError
(
err
,
"bind: %s
\n
"
,
strerror
(
errno
));
close
(
s
);
return
ANET_ERR
;
}
if
(
listen
(
s
,
32
)
==
-
1
)
{
anetSetError
(
err
,
"listen: %s
\n
"
,
strerror
(
errno
));
close
(
s
);
return
ANET_ERR
;
}
return
s
;
}
int
anetAccept
(
char
*
err
,
int
serversock
,
char
*
ip
,
int
*
port
)
{
int
fd
;
struct
sockaddr_in
sa
;
unsigned
int
saLen
;
while
(
1
)
{
saLen
=
sizeof
(
sa
);
fd
=
accept
(
serversock
,
(
struct
sockaddr
*
)
&
sa
,
&
saLen
);
if
(
fd
==
-
1
)
{
if
(
errno
==
EINTR
)
continue
;
else
{
anetSetError
(
err
,
"accept: %s
\n
"
,
strerror
(
errno
));
return
ANET_ERR
;
}
}
break
;
}
if
(
ip
)
strcpy
(
ip
,
inet_ntoa
(
sa
.
sin_addr
));
if
(
port
)
*
port
=
ntohs
(
sa
.
sin_port
);
return
fd
;
}
client-libraries/cpp/anet.h
deleted
100644 → 0
View file @
5762b7f0
/* anet.c -- Basic TCP socket stuff made a bit less boring
*
* Copyright (c) 2006-2009, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ANET_H
#define ANET_H
#define ANET_OK 0
#define ANET_ERR -1
#define ANET_ERR_LEN 256
int
anetTcpConnect
(
char
*
err
,
char
*
addr
,
int
port
);
int
anetTcpNonBlockConnect
(
char
*
err
,
char
*
addr
,
int
port
);
int
anetRead
(
int
fd
,
char
*
buf
,
int
count
);
int
anetResolve
(
char
*
err
,
char
*
host
,
char
*
ipbuf
);
int
anetTcpServer
(
char
*
err
,
int
port
,
char
*
bindaddr
);
int
anetAccept
(
char
*
err
,
int
serversock
,
char
*
ip
,
int
*
port
);
int
anetWrite
(
int
fd
,
char
*
buf
,
int
count
);
int
anetNonBlock
(
char
*
err
,
int
fd
);
int
anetTcpNoDelay
(
char
*
err
,
int
fd
);
int
anetTcpKeepAlive
(
char
*
err
,
int
fd
);
#endif
client-libraries/cpp/fmacros.h
deleted
100644 → 0
View file @
5762b7f0
#ifndef _REDIS_FMACRO_H
#define _REDIS_FMACRO_H
#define _BSD_SOURCE
#define _XOPEN_SOURCE
#endif
client-libraries/cpp/redisclient.cpp
deleted
100644 → 0
View file @
5762b7f0
This diff is collapsed.
Click to expand it.
client-libraries/cpp/redisclient.h
deleted
100644 → 0
View file @
5762b7f0
/* redisclient.h -- a C++ client library for redis.
*
* Copyright (c) 2009, Brian Hammond <brian at fictorial dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef REDISCLIENT_H
#define REDISCLIENT_H
#include <string>
#include <vector>
#include <set>
#include <stdexcept>
#include <ctime>
namespace
redis
{
enum
server_role
{
role_master
,
role_slave
};
struct
server_info
{
std
::
string
version
;
bool
bgsave_in_progress
;
unsigned
long
connected_clients
;
unsigned
long
connected_slaves
;
unsigned
long
used_memory
;
unsigned
long
changes_since_last_save
;
unsigned
long
last_save_time
;
unsigned
long
total_connections_received
;
unsigned
long
total_commands_processed
;
unsigned
long
uptime_in_seconds
;
unsigned
long
uptime_in_days
;
server_role
role
;
};
// Generic error that is thrown when communicating with the redis server.
class
redis_error
{
public:
redis_error
(
const
std
::
string
&
err
);
operator
std
::
string
();
operator
const
std
::
string
()
const
;
private:
std
::
string
err_
;
};
// Some socket-level I/O or general connection error.
class
connection_error
:
public
redis_error
{
public:
connection_error
(
const
std
::
string
&
err
);
};
// Redis gave us a reply we were not expecting.
// Possibly an internal error (here or in redis, probably here).
class
protocol_error
:
public
redis_error
{
public:
protocol_error
(
const
std
::
string
&
err
);
};
// A key that you expected to exist does not in fact exist.
class
key_error
:
public
redis_error
{
public:
key_error
(
const
std
::
string
&
err
);
};
// A value of an expected type or other semantics was found to be invalid.
class
value_error
:
public
redis_error
{
public:
value_error
(
const
std
::
string
&
err
);
};
// You should construct a 'client' object per connection to a redis-server.
//
// Please read the online redis command reference:
// http://code.google.com/p/redis/wiki/CommandReference
//
// No provisions for customizing the allocator on the string/bulk value type
// (std::string) are provided. If needed, you can always change the
// string_type typedef in your local version.
class
client
{
public:
typedef
std
::
string
string_type
;
typedef
std
::
vector
<
string_type
>
string_vector
;
typedef
std
::
set
<
string_type
>
string_set
;
typedef
long
int_type
;
explicit
client
(
const
string_type
&
host
=
"localhost"
,
unsigned
int
port
=
6379
);
~
client
();
//
// Connection handling
//
void
auth
(
const
string_type
&
pass
);
//
// Commands operating on string values
//
// Note that empty string values do not denote nonexistent keys but well,
// empty values! If a nonexistent key is queried, the value returned will
// be missing_value, including when string_vector objects are returned.
//
static
string_type
missing_value
;
// set a key to a string value
void
set
(
const
string_type
&
key
,
const
string_type
&
value
);
// return the string value of the key
string_type
get
(
const
string_type
&
key
);
// set a key to a string returning the old value of the key
string_type
getset
(
const
string_type
&
key
,
const
string_type
&
value
);
// multi-get, return the strings values of the keys
void
mget
(
const
string_vector
&
keys
,
string_vector
&
out
);
// set a key to a string value if the key does not exist. returns true if
// the key was set, else false. This does not throw since you are ok with
// this failing if the dst key already exists.
bool
setnx
(
const
string_type
&
key
,
const
string_type
&
value
);
// increment the integer value of key
// returns new value
int_type
incr
(
const
string_type
&
key
);
// increment the integer value of key by integer
// returns new value
int_type
incrby
(
const
string_type
&
key
,
int_type
by
);
// decrement the integer value of key
// returns new value
int_type
decr
(
const
string_type
&
key
);
// decrement the integer value of key by integer
// returns new value
int_type
decrby
(
const
string_type
&
key
,
int_type
by
);
// test if a key exists
bool
exists
(
const
string_type
&
key
);
// delete a key
// throws if doesn't exist
void
del
(
const
string_type
&
key
);
enum
datatype
{
datatype_none
,
// key doesn't exist
datatype_string
,
datatype_list
,
datatype_set
};
// return the type of the value stored at key
datatype
type
(
const
string_type
&
key
);
//
// Commands operating on the key space
//
// find all the keys matching a given pattern
// returns numbers of keys appended to 'out'
int_type
keys
(
const
string_type
&
pattern
,
string_vector
&
out
);
// return a random key from the key space
// returns empty string if db is empty
string_type
randomkey
();
// rename the old key in the new one, destroying the new key if
// it already exists
void
rename
(
const
string_type
&
old_name
,
const
string_type
&
new_name
);
// rename the old key in the new one, if the new key does not already
// exist. This does not throw since you are ok with this failing if the
// new_name key already exists.
bool
renamenx
(
const
string_type
&
old_name
,
const
string_type
&
new_name
);
// return the number of keys in the current db
int_type
dbsize
();
// set a time to live in seconds on a key.
// fails if there's already a timeout on the key.
// NB: there's currently no generic way to remove a timeout on a key
void
expire
(
const
string_type
&
key
,
unsigned
int
secs
);
//
// Commands operating on lists
//
// Append an element to the tail of the list value at key
void
rpush
(
const
string_type
&
key
,
const
string_type
&
value
);
// Append an element to the head of the list value at key
void
lpush
(
const
string_type
&
key
,
const
string_type
&
value
);
// Return the length of the list value at key
// Returns 0 if the list does not exist; see 'exists'
int_type
llen
(
const
string_type
&
key
);
// Fetch a range of elements from the list at key
// end can be negative for reverse offsets
// Returns number of elements appended to 'out'
int_type
lrange
(
const
string_type
&
key
,
int_type
start
,
int_type
end
,
string_vector
&
out
);
// Fetches the entire list at key.
int_type
get_list
(
const
string_type
&
key
,
string_vector
&
out
)
{
return
lrange
(
key
,
0
,
-
1
,
out
);
}
// Trim the list at key to the specified range of elements
void
ltrim
(
const
string_type
&
key
,
int_type
start
,
int_type
end
);
// Return the element at index position from the list at key
string_type
lindex
(
const
string_type
&
key
,
int_type
);
// set a new value as the element at index position of the list at key
void
lset
(
const
string_type
&
key
,
int_type
index
,
const
string_type
&
);
// If count is zero all the elements are removed. If count is negative
// elements are removed from tail to head, instead to go from head to tail
// that is the normal behaviour. So for example LREM with count -2 and
// hello as value to remove against the list (a,b,c,hello,x,hello,hello)
// will lave the list (a,b,c,hello,x). Returns the number of removed
// elements if the operation succeeded.
//
// Note: this will not throw if the number of elements removed != count
// since you might want to remove at most count elements by don't care if
// < count elements are removed. See lrem_exact().
int_type
lrem
(
const
string_type
&
key
,
int_type
count
,
const
string_type
&
value
);
// An extension of 'lrem' that wants to remove exactly 'count' elements.
// Throws value_error if 'count' elements are not found & removed from the
// list at 'key'.
void
lrem_exact
(
const
string_type
&
key
,
int_type
count
,
const
string_type
&
value
)
{
if
(
lrem
(
key
,
count
,
value
)
!=
count
)
throw
value_error
(
"failed to remove exactly N elements from list"
);
}
// Return and remove (atomically) the first element of the list at key
string_type
lpop
(
const
string_type
&
key
);
// Return and remove (atomically) the last element of the list at key
string_type
rpop
(
const
string_type
&
key
);
//
// Commands operating on sets
//
// Add the specified member to the set value at key
// returns true if added, or false if already a member of the set.
void
sadd
(
const
string_type
&
key
,
const
string_type
&
value
);
// Remove the specified member from the set value at key
// returns true if removed or false if value is not a member of the set.
void
srem
(
const
string_type
&
key
,
const
string_type
&
value
);
// Move the specified member from one set to another atomically
// returns true if element was moved, else false (e.g. not found)
void
smove
(
const
string_type
&
srckey
,
const
string_type
&
dstkey
,
const
string_type
&
value
);
// Return the number of elements (the cardinality) of the set at key
int_type
scard
(
const
string_type
&
key
);
// Test if the specified value is a member of the set at key
// Returns false if key doesn't exist or value is not a member of the set at key
bool
sismember
(
const
string_type
&
key
,
const
string_type
&
value
);
// Return the intersection between the sets stored at key1, key2, ..., keyN
int_type
sinter
(
const
string_vector
&
keys
,
string_set
&
out
);
// Compute the intersection between the sets stored at key1, key2, ...,
// keyN, and store the resulting set at dstkey
// Returns the number of items in the intersection
int_type
sinterstore
(
const
string_type
&
dstkey
,
const
string_vector
&
keys
);
// Return the union between the sets stored at key1, key2, ..., keyN
int_type
sunion
(
const
string_vector
&
keys
,
string_set
&
out
);
// Compute the union between the sets stored at key1, key2, ..., keyN,
// and store the resulting set at dstkey
// Returns the number of items in the intersection
int_type
sunionstore
(
const
string_type
&
dstkey
,
const
string_vector
&
keys
);
// Return all the members of the set value at key
int_type
smembers
(
const
string_type
&
key
,
string_set
&
out
);
//
// Multiple databases handling commands
//
// Select the DB having the specified index
void
select
(
int_type
dbindex
);
// Move the key from the currently selected DB to the DB having as index
// dbindex. Throws if key was already in the db at dbindex or not found in
// currently selected db.
void
move
(
const
string_type
&
key
,
int_type
dbindex
);
// Remove all the keys of the currently selected DB
void
flushdb
();
// Remove all the keys from all the databases
void
flushall
();
//
// Sorting
// Just go read http://code.google.com/p/redis/wiki/SortCommand
//
enum
sort_order
{
sort_order_ascending
,
sort_order_descending
};
int_type
sort
(
const
string_type
&
key
,
string_vector
&
out
,
sort_order
order
=
sort_order_ascending
,
bool
lexicographically
=
false
);
int_type
sort
(
const
string_type
&
key
,
string_vector
&
out
,
int_type
limit_start
,
int_type
limit_end
,
sort_order
order
=
sort_order_ascending
,
bool
lexicographically
=
false
);
int_type
sort
(
const
string_type
&
key
,
string_vector
&
out
,
const
string_type
&
by_pattern
,
int_type
limit_start
,
int_type
limit_end
,
const
string_vector
&
get_patterns
,
sort_order
order
=
sort_order_ascending
,
bool
lexicographically
=
false
);
//
// Persistence control commands
//
// Synchronously save the DB on disk
void
save
();
// Asynchronously save the DB on disk
void
bgsave
();
// Return the UNIX time stamp of the last successfully saving of the
// dataset on disk
time_t
lastsave
();
// Synchronously save the DB on disk, then shutdown the server. This
// object's connection to the server will be lost on success. Otherwise,
// redis_error is raised. Thus, on success, you should delete or otherwise
// no longer use the object.
void
shutdown
();
//
// Remote server control commands
//
// Provide information and statistics about the server
void
info
(
server_info
&
out
);
private:
client
(
const
client
&
);
client
&
operator
=
(
const
client
&
);
void
send_
(
const
std
::
string
&
);
void
recv_ok_reply_
();
void
recv_int_ok_reply_
();
std
::
string
recv_single_line_reply_
();
int_type
recv_bulk_reply_
(
char
prefix
);
std
::
string
recv_bulk_reply_
();
int_type
recv_multi_bulk_reply_
(
string_vector
&
out
);
int_type
recv_multi_bulk_reply_
(
string_set
&
out
);
int_type
recv_int_reply_
();
private:
int
socket_
;
};
}
#endif
Prev
1
2
3
4
5
6
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment