Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
422cf21f
Commit
422cf21f
authored
Jun 04, 2010
by
antirez
Browse files
Merge branch 'lists' of
git://github.com/pietern/redis
parents
4dc1218c
dda20542
Changes
6
Hide whitespace changes
Inline
Side-by-side
Makefile
View file @
422cf21f
...
...
@@ -15,7 +15,7 @@ endif
CCOPT
=
$(CFLAGS)
$(CCLINK)
$(ARCH)
$(PROF)
DEBUG
?=
-g
-rdynamic
-ggdb
OBJ
=
adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o
OBJ
=
adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o
ziplist.o
BENCHOBJ
=
ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o
CLIOBJ
=
anet.o sds.o adlist.o redis-cli.o zmalloc.o linenoise.o
CHECKDUMPOBJ
=
redis-check-dump.o lzf_c.o lzf_d.o
...
...
@@ -53,6 +53,7 @@ redis.o: redis.c fmacros.h config.h redis.h ae.h sds.h anet.h dict.h \
adlist.h zmalloc.h lzf.h pqsort.h zipmap.h staticsymbols.h sha1.h
sds.o
:
sds.c sds.h zmalloc.h
zipmap.o
:
zipmap.c zmalloc.h
ziplist.o
:
ziplist.c zmalloc.h
zmalloc.o
:
zmalloc.c config.h
redis-server
:
$(OBJ)
...
...
redis.c
View file @
422cf21f
...
...
@@ -75,6 +75,7 @@
#include "lzf.h"
/* LZF compression library */
#include "pqsort.h"
/* Partial qsort for SORT+LIMIT */
#include "zipmap.h"
/* Compact dictionary-alike data structure */
#include "ziplist.h"
/* Compact list data structure */
#include "sha1.h"
/* SHA1 is used for DEBUG DIGEST */
#include "release.h"
/* Release and/or git repository information */
...
...
@@ -125,13 +126,15 @@
/* Objects encoding. Some kind of objects like Strings and Hashes can be
* internally represented in multiple ways. The 'encoding' field of the object
* is set to one of this fields for this object. */
#define REDIS_ENCODING_RAW 0
/* Raw representation */
#define REDIS_ENCODING_INT 1
/* Encoded as integer */
#define REDIS_ENCODING_ZIPMAP 2
/* Encoded as zipmap */
#define REDIS_ENCODING_HT 3
/* Encoded as an hash table */
#define REDIS_ENCODING_RAW 0
/* Raw representation */
#define REDIS_ENCODING_INT 1
/* Encoded as integer */
#define REDIS_ENCODING_HT 2
/* Encoded as hash table */
#define REDIS_ENCODING_ZIPMAP 3
/* Encoded as zipmap */
#define REDIS_ENCODING_LIST 4
/* Encoded as zipmap */
#define REDIS_ENCODING_ZIPLIST 5
/* Encoded as ziplist */
static
char
*
strencoding
[]
=
{
"raw"
,
"int"
,
"
zipmap"
,
"hashtable
"
"raw"
,
"int"
,
"
hashtable"
,
"zipmap"
,
"list"
,
"ziplist
"
};
/* Object types only used for dumping to disk */
...
...
@@ -234,9 +237,11 @@ static char* strencoding[] = {
#define APPENDFSYNC_ALWAYS 1
#define APPENDFSYNC_EVERYSEC 2
/*
Hashes
related defaults */
/*
Zip structure
related defaults */
#define REDIS_HASH_MAX_ZIPMAP_ENTRIES 64
#define REDIS_HASH_MAX_ZIPMAP_VALUE 512
#define REDIS_LIST_MAX_ZIPLIST_ENTRIES 1024
#define REDIS_LIST_MAX_ZIPLIST_VALUE 32
/* We can print the stacktrace, so our assert is defined this way: */
#define redisAssert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1)))
...
...
@@ -422,9 +427,11 @@ struct redisServer {
off_t
vm_page_size
;
off_t
vm_pages
;
unsigned
long
long
vm_max_memory
;
/*
Hashes
config */
/*
Zip structure
config */
size_t
hash_max_zipmap_entries
;
size_t
hash_max_zipmap_value
;
size_t
list_max_ziplist_entries
;
size_t
list_max_ziplist_value
;
/* Virtual memory state */
FILE
*
vm_fp
;
int
vm_fd
;
...
...
@@ -643,6 +650,7 @@ static struct redisCommand *lookupCommand(char *name);
static
void
call
(
redisClient
*
c
,
struct
redisCommand
*
cmd
);
static
void
resetClient
(
redisClient
*
c
);
static
void
convertToRealHash
(
robj
*
o
);
static
void
listTypeConvert
(
robj
*
o
,
int
enc
);
static
int
pubsubUnsubscribeAllChannels
(
redisClient
*
c
,
int
notify
);
static
int
pubsubUnsubscribeAllPatterns
(
redisClient
*
c
,
int
notify
);
static
void
freePubsubPattern
(
void
*
p
);
...
...
@@ -1752,6 +1760,8 @@ static void initServerConfig() {
server
.
vm_blocked_clients
=
0
;
server
.
hash_max_zipmap_entries
=
REDIS_HASH_MAX_ZIPMAP_ENTRIES
;
server
.
hash_max_zipmap_value
=
REDIS_HASH_MAX_ZIPMAP_VALUE
;
server
.
list_max_ziplist_entries
=
REDIS_LIST_MAX_ZIPLIST_ENTRIES
;
server
.
list_max_ziplist_value
=
REDIS_LIST_MAX_ZIPLIST_VALUE
;
server
.
shutdown_asap
=
0
;
resetServerSaveParams
();
...
...
@@ -2030,6 +2040,10 @@ static void loadServerConfig(char *filename) {
server
.
hash_max_zipmap_entries
=
memtoll
(
argv
[
1
],
NULL
);
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"hash-max-zipmap-value"
)
&&
argc
==
2
){
server
.
hash_max_zipmap_value
=
memtoll
(
argv
[
1
],
NULL
);
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"list-max-ziplist-entries"
)
&&
argc
==
2
){
server
.
list_max_ziplist_entries
=
memtoll
(
argv
[
1
],
NULL
);
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"list-max-ziplist-value"
)
&&
argc
==
2
){
server
.
list_max_ziplist_value
=
memtoll
(
argv
[
1
],
NULL
);
}
else
{
err
=
"Bad directive or wrong number of arguments"
;
goto
loaderr
;
}
...
...
@@ -3035,9 +3049,17 @@ static robj *dupStringObject(robj *o) {
static
robj
*
createListObject
(
void
)
{
list
*
l
=
listCreate
();
robj
*
o
=
createObject
(
REDIS_LIST
,
l
);
listSetFreeMethod
(
l
,
decrRefCount
);
return
createObject
(
REDIS_LIST
,
l
);
o
->
encoding
=
REDIS_ENCODING_LIST
;
return
o
;
}
static
robj
*
createZiplistObject
(
void
)
{
unsigned
char
*
zl
=
ziplistNew
();
robj
*
o
=
createObject
(
REDIS_LIST
,
zl
);
o
->
encoding
=
REDIS_ENCODING_ZIPLIST
;
return
o
;
}
static
robj
*
createSetObject
(
void
)
{
...
...
@@ -3070,7 +3092,16 @@ static void freeStringObject(robj *o) {
}
static
void
freeListObject
(
robj
*
o
)
{
listRelease
((
list
*
)
o
->
ptr
);
switch
(
o
->
encoding
)
{
case
REDIS_ENCODING_LIST
:
listRelease
((
list
*
)
o
->
ptr
);
break
;
case
REDIS_ENCODING_ZIPLIST
:
zfree
(
o
->
ptr
);
break
;
default:
redisPanic
(
"Unknown list encoding type"
);
}
}
static
void
freeSetObject
(
robj
*
o
)
{
...
...
@@ -3644,38 +3675,32 @@ static int rdbSaveRawString(FILE *fp, unsigned char *s, size_t len) {
return
0
;
}
/* Save a long long value as either an encoded string or a string. */
static
int
rdbSaveLongLongAsStringObject
(
FILE
*
fp
,
long
long
value
)
{
unsigned
char
buf
[
32
];
int
enclen
=
rdbEncodeInteger
(
value
,
buf
);
if
(
enclen
>
0
)
{
if
(
fwrite
(
buf
,
enclen
,
1
,
fp
)
==
0
)
return
-
1
;
}
else
{
/* Encode as string */
enclen
=
ll2string
((
char
*
)
buf
,
32
,
value
);
redisAssert
(
enclen
<
32
);
if
(
rdbSaveLen
(
fp
,
enclen
)
==
-
1
)
return
-
1
;
if
(
fwrite
(
buf
,
enclen
,
1
,
fp
)
==
0
)
return
-
1
;
}
return
0
;
}
/* Like rdbSaveStringObjectRaw() but handle encoded objects */
static
int
rdbSaveStringObject
(
FILE
*
fp
,
robj
*
obj
)
{
int
retval
;
/* Avoid to decode the object, then encode it again, if the
* object is alrady integer encoded. */
if
(
obj
->
encoding
==
REDIS_ENCODING_INT
)
{
long
val
=
(
long
)
obj
->
ptr
;
unsigned
char
buf
[
5
];
int
enclen
;
if
((
enclen
=
rdbEncodeInteger
(
val
,
buf
))
>
0
)
{
if
(
fwrite
(
buf
,
enclen
,
1
,
fp
)
==
0
)
return
-
1
;
return
0
;
}
/* otherwise... fall throught and continue with the usual
* code path. */
}
/* Avoid incr/decr ref count business when possible.
* This plays well with copy-on-write given that we are probably
* in a child process (BGSAVE). Also this makes sure key objects
* of swapped objects are not incRefCount-ed (an assert does not allow
* this in order to avoid bugs) */
if
(
obj
->
encoding
!=
REDIS_ENCODING_RAW
)
{
obj
=
getDecodedObject
(
obj
);
retval
=
rdbSaveRawString
(
fp
,
obj
->
ptr
,
sdslen
(
obj
->
ptr
));
decrRefCount
(
obj
);
return
rdbSaveLongLongAsStringObject
(
fp
,(
long
)
obj
->
ptr
);
}
else
{
retval
=
rdbSaveRawString
(
fp
,
obj
->
ptr
,
sdslen
(
obj
->
ptr
));
redisAssert
(
obj
->
encoding
==
REDIS_ENCODING_RAW
);
return
rdbSaveRawString
(
fp
,
obj
->
ptr
,
sdslen
(
obj
->
ptr
));
}
return
retval
;
}
/* Save a double value. Doubles are saved as strings prefixed by an unsigned
...
...
@@ -3728,16 +3753,37 @@ static int rdbSaveObject(FILE *fp, robj *o) {
if
(
rdbSaveStringObject
(
fp
,
o
)
==
-
1
)
return
-
1
;
}
else
if
(
o
->
type
==
REDIS_LIST
)
{
/* Save a list value */
list
*
list
=
o
->
ptr
;
listIter
li
;
listNode
*
ln
;
if
(
rdbSaveLen
(
fp
,
listLength
(
list
))
==
-
1
)
return
-
1
;
listRewind
(
list
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
robj
*
eleobj
=
listNodeValue
(
ln
);
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
unsigned
char
*
p
;
unsigned
char
*
vstr
;
unsigned
int
vlen
;
long
long
vlong
;
if
(
rdbSaveLen
(
fp
,
ziplistLen
(
o
->
ptr
))
==
-
1
)
return
-
1
;
p
=
ziplistIndex
(
o
->
ptr
,
0
);
while
(
ziplistGet
(
p
,
&
vstr
,
&
vlen
,
&
vlong
))
{
if
(
vstr
)
{
if
(
rdbSaveRawString
(
fp
,
vstr
,
vlen
)
==
-
1
)
return
-
1
;
}
else
{
if
(
rdbSaveLongLongAsStringObject
(
fp
,
vlong
)
==
-
1
)
return
-
1
;
}
p
=
ziplistNext
(
o
->
ptr
,
p
);
}
}
else
if
(
o
->
encoding
==
REDIS_ENCODING_LIST
)
{
list
*
list
=
o
->
ptr
;
listIter
li
;
listNode
*
ln
;
if
(
rdbSaveStringObject
(
fp
,
eleobj
)
==
-
1
)
return
-
1
;
if
(
rdbSaveLen
(
fp
,
listLength
(
list
))
==
-
1
)
return
-
1
;
listRewind
(
list
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
robj
*
eleobj
=
listNodeValue
(
ln
);
if
(
rdbSaveStringObject
(
fp
,
eleobj
)
==
-
1
)
return
-
1
;
}
}
else
{
redisPanic
(
"Unknown list encoding"
);
}
}
else
if
(
o
->
type
==
REDIS_SET
)
{
/* Save a set value */
...
...
@@ -4107,34 +4153,60 @@ static int rdbLoadDoubleValue(FILE *fp, double *val) {
/* Load a Redis object of the specified type from the specified file.
* On success a newly allocated object is returned, otherwise NULL. */
static
robj
*
rdbLoadObject
(
int
type
,
FILE
*
fp
)
{
robj
*
o
;
robj
*
o
,
*
ele
,
*
dec
;
size_t
len
;
redisLog
(
REDIS_DEBUG
,
"LOADING OBJECT %d (at %d)
\n
"
,
type
,
ftell
(
fp
));
if
(
type
==
REDIS_STRING
)
{
/* Read string value */
if
((
o
=
rdbLoadEncodedStringObject
(
fp
))
==
NULL
)
return
NULL
;
o
=
tryObjectEncoding
(
o
);
}
else
if
(
type
==
REDIS_LIST
||
type
==
REDIS_SET
)
{
/* Read list
/set
value */
uint32_t
listlen
;
}
else
if
(
type
==
REDIS_LIST
)
{
/* Read list value */
if
((
len
=
rdbLoadLen
(
fp
,
NULL
))
==
REDIS_RDB_LENERR
)
return
NULL
;
if
((
listlen
=
rdbLoadLen
(
fp
,
NULL
))
==
REDIS_RDB_LENERR
)
return
NULL
;
o
=
(
type
==
REDIS_LIST
)
?
createListObject
()
:
createSetObject
();
/* Use a real list when there are too many entries */
if
(
len
>
server
.
list_max_ziplist_entries
)
{
o
=
createListObject
();
}
else
{
o
=
createZiplistObject
();
}
/* Load every single element of the list */
while
(
len
--
)
{
if
((
ele
=
rdbLoadEncodedStringObject
(
fp
))
==
NULL
)
return
NULL
;
/* If we are using a ziplist and the value is too big, convert
* the object to a real list. */
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPLIST
&&
ele
->
encoding
==
REDIS_ENCODING_RAW
&&
sdslen
(
ele
->
ptr
)
>
server
.
list_max_ziplist_value
)
listTypeConvert
(
o
,
REDIS_ENCODING_LIST
);
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
dec
=
getDecodedObject
(
ele
);
o
->
ptr
=
ziplistPush
(
o
->
ptr
,
dec
->
ptr
,
sdslen
(
dec
->
ptr
),
REDIS_TAIL
);
decrRefCount
(
dec
);
decrRefCount
(
ele
);
}
else
{
ele
=
tryObjectEncoding
(
ele
);
listAddNodeTail
(
o
->
ptr
,
ele
);
incrRefCount
(
ele
);
}
}
}
else
if
(
type
==
REDIS_SET
)
{
/* Read list/set value */
if
((
len
=
rdbLoadLen
(
fp
,
NULL
))
==
REDIS_RDB_LENERR
)
return
NULL
;
o
=
createSetObject
();
/* It's faster to expand the dict to the right size asap in order
* to avoid rehashing */
if
(
type
==
REDIS_SET
&&
list
len
>
DICT_HT_INITIAL_SIZE
)
dictExpand
(
o
->
ptr
,
list
len
);
if
(
len
>
DICT_HT_INITIAL_SIZE
)
dictExpand
(
o
->
ptr
,
len
);
/* Load every single element of the list/set */
while
(
listlen
--
)
{
robj
*
ele
;
while
(
len
--
)
{
if
((
ele
=
rdbLoadEncodedStringObject
(
fp
))
==
NULL
)
return
NULL
;
ele
=
tryObjectEncoding
(
ele
);
if
(
type
==
REDIS_LIST
)
{
listAddNodeTail
((
list
*
)
o
->
ptr
,
ele
);
}
else
{
dictAdd
((
dict
*
)
o
->
ptr
,
ele
,
NULL
);
}
dictAdd
((
dict
*
)
o
->
ptr
,
ele
,
NULL
);
}
}
else
if
(
type
==
REDIS_ZSET
)
{
/* Read list/set value */
...
...
@@ -4833,24 +4905,251 @@ static void moveCommand(redisClient *c) {
}
/* =================================== Lists ================================ */
static
void
pushGenericCommand
(
redisClient
*
c
,
int
where
)
{
robj
*
lobj
;
list
*
list
;
lobj
=
lookupKeyWrite
(
c
->
db
,
c
->
argv
[
1
]);
/* Check the argument length to see if it requires us to convert the ziplist
* to a real list. Only check raw-encoded objects because integer encoded
* objects are never too long. */
static
void
listTypeTryConversion
(
robj
*
subject
,
robj
*
value
)
{
if
(
subject
->
encoding
!=
REDIS_ENCODING_ZIPLIST
)
return
;
if
(
value
->
encoding
==
REDIS_ENCODING_RAW
&&
sdslen
(
value
->
ptr
)
>
server
.
list_max_ziplist_value
)
listTypeConvert
(
subject
,
REDIS_ENCODING_LIST
);
}
static
void
listTypePush
(
robj
*
subject
,
robj
*
value
,
int
where
)
{
/* Check if we need to convert the ziplist */
listTypeTryConversion
(
subject
,
value
);
if
(
subject
->
encoding
==
REDIS_ENCODING_ZIPLIST
&&
ziplistLen
(
subject
->
ptr
)
>
server
.
list_max_ziplist_entries
)
listTypeConvert
(
subject
,
REDIS_ENCODING_LIST
);
if
(
subject
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
int
pos
=
(
where
==
REDIS_HEAD
)
?
ZIPLIST_HEAD
:
ZIPLIST_TAIL
;
value
=
getDecodedObject
(
value
);
subject
->
ptr
=
ziplistPush
(
subject
->
ptr
,
value
->
ptr
,
sdslen
(
value
->
ptr
),
pos
);
decrRefCount
(
value
);
}
else
if
(
subject
->
encoding
==
REDIS_ENCODING_LIST
)
{
if
(
where
==
REDIS_HEAD
)
{
listAddNodeHead
(
subject
->
ptr
,
value
);
}
else
{
listAddNodeTail
(
subject
->
ptr
,
value
);
}
incrRefCount
(
value
);
}
else
{
redisPanic
(
"Unknown list encoding"
);
}
}
static
robj
*
listTypePop
(
robj
*
subject
,
int
where
)
{
robj
*
value
=
NULL
;
if
(
subject
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
unsigned
char
*
p
;
unsigned
char
*
vstr
;
unsigned
int
vlen
;
long
long
vlong
;
int
pos
=
(
where
==
REDIS_HEAD
)
?
0
:
-
1
;
p
=
ziplistIndex
(
subject
->
ptr
,
pos
);
if
(
ziplistGet
(
p
,
&
vstr
,
&
vlen
,
&
vlong
))
{
if
(
vstr
)
{
value
=
createStringObject
((
char
*
)
vstr
,
vlen
);
}
else
{
value
=
createStringObjectFromLongLong
(
vlong
);
}
/* We only need to delete an element when it exists */
subject
->
ptr
=
ziplistDelete
(
subject
->
ptr
,
&
p
);
}
}
else
if
(
subject
->
encoding
==
REDIS_ENCODING_LIST
)
{
list
*
list
=
subject
->
ptr
;
listNode
*
ln
;
if
(
where
==
REDIS_HEAD
)
{
ln
=
listFirst
(
list
);
}
else
{
ln
=
listLast
(
list
);
}
if
(
ln
!=
NULL
)
{
value
=
listNodeValue
(
ln
);
incrRefCount
(
value
);
listDelNode
(
list
,
ln
);
}
}
else
{
redisPanic
(
"Unknown list encoding"
);
}
return
value
;
}
static
unsigned
long
listTypeLength
(
robj
*
subject
)
{
if
(
subject
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
return
ziplistLen
(
subject
->
ptr
);
}
else
if
(
subject
->
encoding
==
REDIS_ENCODING_LIST
)
{
return
listLength
((
list
*
)
subject
->
ptr
);
}
else
{
redisPanic
(
"Unknown list encoding"
);
}
}
/* Structure to hold set iteration abstraction. */
typedef
struct
{
robj
*
subject
;
unsigned
char
encoding
;
unsigned
char
direction
;
/* Iteration direction */
unsigned
char
*
zi
;
listNode
*
ln
;
}
listTypeIterator
;
/* Structure for an entry while iterating over a list. */
typedef
struct
{
listTypeIterator
*
li
;
unsigned
char
*
zi
;
/* Entry in ziplist */
listNode
*
ln
;
/* Entry in linked list */
}
listTypeEntry
;
/* Initialize an iterator at the specified index. */
static
listTypeIterator
*
listTypeInitIterator
(
robj
*
subject
,
int
index
,
unsigned
char
direction
)
{
listTypeIterator
*
li
=
zmalloc
(
sizeof
(
listTypeIterator
));
li
->
subject
=
subject
;
li
->
encoding
=
subject
->
encoding
;
li
->
direction
=
direction
;
if
(
li
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
li
->
zi
=
ziplistIndex
(
subject
->
ptr
,
index
);
}
else
if
(
li
->
encoding
==
REDIS_ENCODING_LIST
)
{
li
->
ln
=
listIndex
(
subject
->
ptr
,
index
);
}
else
{
redisPanic
(
"Unknown list encoding"
);
}
return
li
;
}
/* Clean up the iterator. */
static
void
listTypeReleaseIterator
(
listTypeIterator
*
li
)
{
zfree
(
li
);
}
/* Stores pointer to current the entry in the provided entry structure
* and advances the position of the iterator. Returns 1 when the current
* entry is in fact an entry, 0 otherwise. */
static
int
listTypeNext
(
listTypeIterator
*
li
,
listTypeEntry
*
entry
)
{
/* Protect from converting when iterating */
redisAssert
(
li
->
subject
->
encoding
==
li
->
encoding
);
entry
->
li
=
li
;
if
(
li
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
entry
->
zi
=
li
->
zi
;
if
(
entry
->
zi
!=
NULL
)
{
if
(
li
->
direction
==
REDIS_TAIL
)
li
->
zi
=
ziplistNext
(
li
->
subject
->
ptr
,
li
->
zi
);
else
li
->
zi
=
ziplistPrev
(
li
->
subject
->
ptr
,
li
->
zi
);
return
1
;
}
}
else
if
(
li
->
encoding
==
REDIS_ENCODING_LIST
)
{
entry
->
ln
=
li
->
ln
;
if
(
entry
->
ln
!=
NULL
)
{
if
(
li
->
direction
==
REDIS_TAIL
)
li
->
ln
=
li
->
ln
->
next
;
else
li
->
ln
=
li
->
ln
->
prev
;
return
1
;
}
}
else
{
redisPanic
(
"Unknown list encoding"
);
}
return
0
;
}
/* Return entry or NULL at the current position of the iterator. */
static
robj
*
listTypeGet
(
listTypeEntry
*
entry
)
{
listTypeIterator
*
li
=
entry
->
li
;
robj
*
value
=
NULL
;
if
(
li
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
unsigned
char
*
vstr
;
unsigned
int
vlen
;
long
long
vlong
;
redisAssert
(
entry
->
zi
!=
NULL
);
if
(
ziplistGet
(
entry
->
zi
,
&
vstr
,
&
vlen
,
&
vlong
))
{
if
(
vstr
)
{
value
=
createStringObject
((
char
*
)
vstr
,
vlen
);
}
else
{
value
=
createStringObjectFromLongLong
(
vlong
);
}
}
}
else
if
(
li
->
encoding
==
REDIS_ENCODING_LIST
)
{
redisAssert
(
entry
->
ln
!=
NULL
);
value
=
listNodeValue
(
entry
->
ln
);
incrRefCount
(
value
);
}
else
{
redisPanic
(
"Unknown list encoding"
);
}
return
value
;
}
/* Compare the given object with the entry at the current position. */
static
int
listTypeEqual
(
listTypeEntry
*
entry
,
robj
*
o
)
{
listTypeIterator
*
li
=
entry
->
li
;
if
(
li
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
redisAssert
(
o
->
encoding
==
REDIS_ENCODING_RAW
);
return
ziplistCompare
(
entry
->
zi
,
o
->
ptr
,
sdslen
(
o
->
ptr
));
}
else
if
(
li
->
encoding
==
REDIS_ENCODING_LIST
)
{
return
equalStringObjects
(
o
,
listNodeValue
(
entry
->
ln
));
}
else
{
redisPanic
(
"Unknown list encoding"
);
}
}
/* Delete the element pointed to. */
static
void
listTypeDelete
(
listTypeEntry
*
entry
)
{
listTypeIterator
*
li
=
entry
->
li
;
if
(
li
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
unsigned
char
*
p
=
entry
->
zi
;
li
->
subject
->
ptr
=
ziplistDelete
(
li
->
subject
->
ptr
,
&
p
);
/* Update position of the iterator depending on the direction */
if
(
li
->
direction
==
REDIS_TAIL
)
li
->
zi
=
p
;
else
li
->
zi
=
ziplistPrev
(
li
->
subject
->
ptr
,
p
);
}
else
if
(
entry
->
li
->
encoding
==
REDIS_ENCODING_LIST
)
{
listNode
*
next
;
if
(
li
->
direction
==
REDIS_TAIL
)
next
=
entry
->
ln
->
next
;
else
next
=
entry
->
ln
->
prev
;
listDelNode
(
li
->
subject
->
ptr
,
entry
->
ln
);
li
->
ln
=
next
;
}
else
{
redisPanic
(
"Unknown list encoding"
);
}
}
static
void
listTypeConvert
(
robj
*
subject
,
int
enc
)
{
listTypeIterator
*
li
;
listTypeEntry
entry
;
redisAssert
(
subject
->
type
==
REDIS_LIST
);
if
(
enc
==
REDIS_ENCODING_LIST
)
{
list
*
l
=
listCreate
();
/* listTypeGet returns a robj with incremented refcount */
li
=
listTypeInitIterator
(
subject
,
0
,
REDIS_TAIL
);
while
(
listTypeNext
(
li
,
&
entry
))
listAddNodeTail
(
l
,
listTypeGet
(
&
entry
));
listTypeReleaseIterator
(
li
);
subject
->
encoding
=
REDIS_ENCODING_LIST
;
zfree
(
subject
->
ptr
);
subject
->
ptr
=
l
;
}
else
{
redisPanic
(
"Unsupported list conversion"
);
}
}
static
void
pushGenericCommand
(
redisClient
*
c
,
int
where
)
{
robj
*
lobj
=
lookupKeyWrite
(
c
->
db
,
c
->
argv
[
1
]);
if
(
lobj
==
NULL
)
{
if
(
handleClientsWaitingListPush
(
c
,
c
->
argv
[
1
],
c
->
argv
[
2
]))
{
addReply
(
c
,
shared
.
cone
);
return
;
}
lobj
=
createListObject
();
list
=
lobj
->
ptr
;
if
(
where
==
REDIS_HEAD
)
{
listAddNodeHead
(
list
,
c
->
argv
[
2
]);
}
else
{
listAddNodeTail
(
list
,
c
->
argv
[
2
]);
}
incrRefCount
(
c
->
argv
[
2
]);
lobj
=
createZiplistObject
();
dbAdd
(
c
->
db
,
c
->
argv
[
1
],
lobj
);
}
else
{
if
(
lobj
->
type
!=
REDIS_LIST
)
{
...
...
@@ -4861,16 +5160,10 @@ static void pushGenericCommand(redisClient *c, int where) {
addReply
(
c
,
shared
.
cone
);
return
;
}
list
=
lobj
->
ptr
;
if
(
where
==
REDIS_HEAD
)
{
listAddNodeHead
(
list
,
c
->
argv
[
2
]);
}
else
{
listAddNodeTail
(
list
,
c
->
argv
[
2
]);
}
incrRefCount
(
c
->
argv
[
2
]);
}
listTypePush
(
lobj
,
c
->
argv
[
2
],
where
);
addReplyLongLong
(
c
,
listTypeLength
(
lobj
));
server
.
dirty
++
;
addReplyLongLong
(
c
,
listLength
(
list
));
}
static
void
lpushCommand
(
redisClient
*
c
)
{
...
...
@@ -4882,80 +5175,94 @@ static void rpushCommand(redisClient *c) {
}
static
void
llenCommand
(
redisClient
*
c
)
{
robj
*
o
;
list
*
l
;
if
((
o
=
lookupKeyReadOrReply
(
c
,
c
->
argv
[
1
],
shared
.
czero
))
==
NULL
||
checkType
(
c
,
o
,
REDIS_LIST
))
return
;
l
=
o
->
ptr
;
addReplyUlong
(
c
,
listLength
(
l
));
robj
*
o
=
lookupKeyReadOrReply
(
c
,
c
->
argv
[
1
],
shared
.
czero
);
if
(
o
==
NULL
||
checkType
(
c
,
o
,
REDIS_LIST
))
return
;
addReplyUlong
(
c
,
listTypeLength
(
o
));
}
static
void
lindexCommand
(
redisClient
*
c
)
{
robj
*
o
;
robj
*
o
=
lookupKeyReadOrReply
(
c
,
c
->
argv
[
1
],
shared
.
nullbulk
);
if
(
o
==
NULL
||
checkType
(
c
,
o
,
REDIS_LIST
))
return
;
int
index
=
atoi
(
c
->
argv
[
2
]
->
ptr
);
list
*
list
;
listNode
*
ln
;
if
((
o
=
lookupKeyReadOrReply
(
c
,
c
->
argv
[
1
],
shared
.
nullbulk
))
==
NULL
||
checkType
(
c
,
o
,
REDIS_LIST
))
return
;
list
=
o
->
ptr
;
robj
*
value
=
NULL
;
ln
=
listIndex
(
list
,
index
);
if
(
ln
==
NULL
)
{
addReply
(
c
,
shared
.
nullbulk
);
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
unsigned
char
*
p
;
unsigned
char
*
vstr
;
unsigned
int
vlen
;
long
long
vlong
;
p
=
ziplistIndex
(
o
->
ptr
,
index
);
if
(
ziplistGet
(
p
,
&
vstr
,
&
vlen
,
&
vlong
))
{
if
(
vstr
)
{
value
=
createStringObject
((
char
*
)
vstr
,
vlen
);
}
else
{
value
=
createStringObjectFromLongLong
(
vlong
);
}
addReplyBulk
(
c
,
value
);
decrRefCount
(
value
);
}
else
{
addReply
(
c
,
shared
.
nullbulk
);
}
}
else
if
(
o
->
encoding
==
REDIS_ENCODING_LIST
)
{
listNode
*
ln
=
listIndex
(
o
->
ptr
,
index
);
if
(
ln
!=
NULL
)
{
value
=
listNodeValue
(
ln
);
addReplyBulk
(
c
,
value
);
}
else
{
addReply
(
c
,
shared
.
nullbulk
);
}
}
else
{
robj
*
ele
=
listNodeValue
(
ln
);
addReplyBulk
(
c
,
ele
);
redisPanic
(
"Unknown list encoding"
);
}
}
static
void
lsetCommand
(
redisClient
*
c
)
{
robj
*
o
;
robj
*
o
=
lookupKeyWriteOrReply
(
c
,
c
->
argv
[
1
],
shared
.
nokeyerr
);
if
(
o
==
NULL
||
checkType
(
c
,
o
,
REDIS_LIST
))
return
;
int
index
=
atoi
(
c
->
argv
[
2
]
->
ptr
);
list
*
list
;
listNode
*
ln
;
if
((
o
=
lookupKeyWriteOrReply
(
c
,
c
->
argv
[
1
],
shared
.
nokeyerr
))
==
NULL
||
checkType
(
c
,
o
,
REDIS_LIST
))
return
;
list
=
o
->
ptr
;
ln
=
listIndex
(
list
,
index
);
if
(
ln
==
NULL
)
{
addReply
(
c
,
shared
.
outofrangeerr
);
robj
*
value
=
c
->
argv
[
3
];
listTypeTryConversion
(
o
,
value
);
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
unsigned
char
*
p
,
*
zl
=
o
->
ptr
;
p
=
ziplistIndex
(
zl
,
index
);
if
(
p
==
NULL
)
{
addReply
(
c
,
shared
.
outofrangeerr
);
}
else
{
o
->
ptr
=
ziplistDelete
(
o
->
ptr
,
&
p
);
value
=
getDecodedObject
(
value
);
o
->
ptr
=
ziplistInsert
(
o
->
ptr
,
p
,
value
->
ptr
,
sdslen
(
value
->
ptr
));
decrRefCount
(
value
);
addReply
(
c
,
shared
.
ok
);
server
.
dirty
++
;
}
}
else
if
(
o
->
encoding
==
REDIS_ENCODING_LIST
)
{
listNode
*
ln
=
listIndex
(
o
->
ptr
,
index
);
if
(
ln
==
NULL
)
{
addReply
(
c
,
shared
.
outofrangeerr
);
}
else
{
decrRefCount
((
robj
*
)
listNodeValue
(
ln
));
listNodeValue
(
ln
)
=
value
;
incrRefCount
(
value
);
addReply
(
c
,
shared
.
ok
);
server
.
dirty
++
;
}
}
else
{
robj
*
ele
=
listNodeValue
(
ln
);
decrRefCount
(
ele
);
listNodeValue
(
ln
)
=
c
->
argv
[
3
];
incrRefCount
(
c
->
argv
[
3
]);
addReply
(
c
,
shared
.
ok
);
server
.
dirty
++
;
redisPanic
(
"Unknown list encoding"
);
}
}
static
void
popGenericCommand
(
redisClient
*
c
,
int
where
)
{
robj
*
o
;
list
*
list
;
listNode
*
ln
;
if
((
o
=
lookupKeyWriteOrReply
(
c
,
c
->
argv
[
1
],
shared
.
nullbulk
))
==
NULL
||
checkType
(
c
,
o
,
REDIS_LIST
))
return
;
list
=
o
->
ptr
;
if
(
where
==
REDIS_HEAD
)
ln
=
listFirst
(
list
);
else
ln
=
listLast
(
list
);
robj
*
o
=
lookupKeyWriteOrReply
(
c
,
c
->
argv
[
1
],
shared
.
nullbulk
);
if
(
o
==
NULL
||
checkType
(
c
,
o
,
REDIS_LIST
))
return
;
if
(
ln
==
NULL
)
{
robj
*
value
=
listTypePop
(
o
,
where
);
if
(
value
==
NULL
)
{
addReply
(
c
,
shared
.
nullbulk
);
}
else
{
robj
*
ele
=
listNodeValue
(
ln
);
addReplyBulk
(
c
,
ele
);
listDelNode
(
list
,
ln
);
if
(
listLength
(
list
)
==
0
)
dbDelete
(
c
->
db
,
c
->
argv
[
1
]);
addReplyBulk
(
c
,
value
);
decrRefCount
(
value
);
if
(
listTypeLength
(
o
)
==
0
)
dbDelete
(
c
->
db
,
c
->
argv
[
1
]);
server
.
dirty
++
;
}
}
...
...
@@ -4969,19 +5276,16 @@ static void rpopCommand(redisClient *c) {
}
static
void
lrangeCommand
(
redisClient
*
c
)
{
robj
*
o
;
robj
*
o
,
*
value
;
int
start
=
atoi
(
c
->
argv
[
2
]
->
ptr
);
int
end
=
atoi
(
c
->
argv
[
3
]
->
ptr
);
int
llen
;
int
rangelen
,
j
;
list
*
list
;
listNode
*
ln
;
robj
*
ele
;
listTypeEntry
entry
;
if
((
o
=
lookupKeyReadOrReply
(
c
,
c
->
argv
[
1
],
shared
.
emptymultibulk
))
==
NULL
||
checkType
(
c
,
o
,
REDIS_LIST
))
return
;
list
=
o
->
ptr
;
llen
=
listLength
(
list
);
llen
=
listTypeLength
(
o
);
/* convert negative indexes */
if
(
start
<
0
)
start
=
llen
+
start
;
...
...
@@ -4999,13 +5303,15 @@ static void lrangeCommand(redisClient *c) {
rangelen
=
(
end
-
start
)
+
1
;
/* Return the result in form of a multi-bulk reply */
ln
=
listIndex
(
list
,
start
);
addReplySds
(
c
,
sdscatprintf
(
sdsempty
(),
"*%d
\r\n
"
,
rangelen
));
listTypeIterator
*
li
=
listTypeInitIterator
(
o
,
start
,
REDIS_TAIL
);
for
(
j
=
0
;
j
<
rangelen
;
j
++
)
{
ele
=
listNodeValue
(
ln
);
addReplyBulk
(
c
,
ele
);
ln
=
ln
->
next
;
redisAssert
(
listTypeNext
(
li
,
&
entry
));
value
=
listTypeGet
(
&
entry
);
addReplyBulk
(
c
,
value
);
decrRefCount
(
value
);
}
listTypeReleaseIterator
(
li
);
}
static
void
ltrimCommand
(
redisClient
*
c
)
{
...
...
@@ -5019,8 +5325,7 @@ static void ltrimCommand(redisClient *c) {
if
((
o
=
lookupKeyWriteOrReply
(
c
,
c
->
argv
[
1
],
shared
.
ok
))
==
NULL
||
checkType
(
c
,
o
,
REDIS_LIST
))
return
;
list
=
o
->
ptr
;
llen
=
listLength
(
list
);
llen
=
listTypeLength
(
o
);
/* convert negative indexes */
if
(
start
<
0
)
start
=
llen
+
start
;
...
...
@@ -5040,49 +5345,63 @@ static void ltrimCommand(redisClient *c) {
}
/* Remove list elements to perform the trim */
for
(
j
=
0
;
j
<
ltrim
;
j
++
)
{
ln
=
listFirst
(
list
);
listDelNode
(
list
,
ln
);
}
for
(
j
=
0
;
j
<
rtrim
;
j
++
)
{
ln
=
listLast
(
list
);
listDelNode
(
list
,
ln
);
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
o
->
ptr
=
ziplistDeleteRange
(
o
->
ptr
,
0
,
ltrim
);
o
->
ptr
=
ziplistDeleteRange
(
o
->
ptr
,
-
rtrim
,
rtrim
);
}
else
if
(
o
->
encoding
==
REDIS_ENCODING_LIST
)
{
list
=
o
->
ptr
;
for
(
j
=
0
;
j
<
ltrim
;
j
++
)
{
ln
=
listFirst
(
list
);
listDelNode
(
list
,
ln
);
}
for
(
j
=
0
;
j
<
rtrim
;
j
++
)
{
ln
=
listLast
(
list
);
listDelNode
(
list
,
ln
);
}
}
else
{
redisPanic
(
"Unknown list encoding"
);
}
if
(
listLength
(
list
)
==
0
)
dbDelete
(
c
->
db
,
c
->
argv
[
1
]);
if
(
list
Type
Length
(
o
)
==
0
)
dbDelete
(
c
->
db
,
c
->
argv
[
1
]);
server
.
dirty
++
;
addReply
(
c
,
shared
.
ok
);
}
static
void
lremCommand
(
redisClient
*
c
)
{
robj
*
o
;
list
*
list
;
listNode
*
ln
,
*
next
;
robj
*
subject
,
*
obj
=
c
->
argv
[
3
];
int
toremove
=
atoi
(
c
->
argv
[
2
]
->
ptr
);
int
removed
=
0
;
int
fromtail
=
0
;
listTypeEntry
entry
;
if
((
o
=
lookupKeyWriteOrReply
(
c
,
c
->
argv
[
1
],
shared
.
czero
))
==
NULL
||
checkType
(
c
,
o
,
REDIS_LIST
))
return
;
list
=
o
->
ptr
;
subject
=
lookupKeyWriteOrReply
(
c
,
c
->
argv
[
1
],
shared
.
czero
);
if
(
subject
==
NULL
||
checkType
(
c
,
subject
,
REDIS_LIST
))
return
;
/* Make sure obj is raw when we're dealing with a ziplist */
if
(
subject
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
obj
=
getDecodedObject
(
obj
);
listTypeIterator
*
li
;
if
(
toremove
<
0
)
{
toremove
=
-
toremove
;
fromtail
=
1
;
li
=
listTypeInitIterator
(
subject
,
-
1
,
REDIS_HEAD
);
}
else
{
li
=
listTypeInitIterator
(
subject
,
0
,
REDIS_TAIL
);
}
ln
=
fromtail
?
list
->
tail
:
list
->
head
;
while
(
ln
)
{
robj
*
ele
=
listNodeValue
(
ln
);
next
=
fromtail
?
ln
->
prev
:
ln
->
next
;
if
(
equalStringObjects
(
ele
,
c
->
argv
[
3
]
))
{
list
DelNode
(
list
,
ln
);
while
(
listTypeNext
(
li
,
&
entry
))
{
if
(
listTypeEqual
(
&
entry
,
obj
))
{
list
TypeDelete
(
&
entry
);
server
.
dirty
++
;
removed
++
;
if
(
toremove
&&
removed
==
toremove
)
break
;
}
ln
=
next
;
}
if
(
listLength
(
list
)
==
0
)
dbDelete
(
c
->
db
,
c
->
argv
[
1
]);
listTypeReleaseIterator
(
li
);
/* Clean up raw encoded object */
if
(
subject
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
decrRefCount
(
obj
);
if
(
listTypeLength
(
subject
)
==
0
)
dbDelete
(
c
->
db
,
c
->
argv
[
1
]);
addReplySds
(
c
,
sdscatprintf
(
sdsempty
(),
":%d
\r\n
"
,
removed
));
}
...
...
@@ -5102,46 +5421,36 @@ static void lremCommand(redisClient *c) {
* as well. This command was originally proposed by Ezra Zygmuntowicz.
*/
static
void
rpoplpushcommand
(
redisClient
*
c
)
{
robj
*
sobj
;
list
*
srclist
;
listNode
*
ln
;
robj
*
sobj
,
*
value
;
if
((
sobj
=
lookupKeyWriteOrReply
(
c
,
c
->
argv
[
1
],
shared
.
nullbulk
))
==
NULL
||
checkType
(
c
,
sobj
,
REDIS_LIST
))
return
;
srclist
=
sobj
->
ptr
;
ln
=
listLast
(
srclist
);
if
(
l
n
==
NULL
)
{
if
(
l
istTypeLength
(
sobj
)
==
0
)
{
addReply
(
c
,
shared
.
nullbulk
);
}
else
{
robj
*
dobj
=
lookupKeyWrite
(
c
->
db
,
c
->
argv
[
2
]);
robj
*
ele
=
listNodeValue
(
ln
);
list
*
dstlist
;
if
(
dobj
&&
dobj
->
type
!=
REDIS_LIST
)
{
addReply
(
c
,
shared
.
wrongtypeerr
);
return
;
}
if
(
dobj
&&
checkType
(
c
,
dobj
,
REDIS_LIST
))
return
;
value
=
listTypePop
(
sobj
,
REDIS_TAIL
);
/* Add the element to the target list (unless it's directly
* passed to some BLPOP-ing client */
if
(
!
handleClientsWaitingListPush
(
c
,
c
->
argv
[
2
],
el
e
))
{
if
(
dobj
==
NULL
)
{
/* Create the list if the key does not exist */
dobj
=
create
L
istObject
();
if
(
!
handleClientsWaitingListPush
(
c
,
c
->
argv
[
2
],
valu
e
))
{
/* Create the list if the key does not exist */
if
(
!
dobj
)
{
dobj
=
create
Zipl
istObject
();
dbAdd
(
c
->
db
,
c
->
argv
[
2
],
dobj
);
}
dstlist
=
dobj
->
ptr
;
listAddNodeHead
(
dstlist
,
ele
);
incrRefCount
(
ele
);
listTypePush
(
dobj
,
value
,
REDIS_HEAD
);
}
/* Send the element to the client as reply as well */
addReplyBulk
(
c
,
el
e
);
addReplyBulk
(
c
,
valu
e
);
/* Finally remove the element from the source list */
listDelNode
(
srclist
,
ln
);
if
(
listLength
(
srclist
)
==
0
)
dbDelete
(
c
->
db
,
c
->
argv
[
1
]);
/* listTypePop returns an object with its refcount incremented */
decrRefCount
(
value
);
/* Delete the source list when it is empty */
if
(
listTypeLength
(
sobj
)
==
0
)
dbDelete
(
c
->
db
,
c
->
argv
[
1
]);
server
.
dirty
++
;
}
}
...
...
@@ -5764,7 +6073,7 @@ static zskiplistNode *zslFirstWithScore(zskiplist *zsl, double score) {
* Returns 0 when the element cannot be found, rank otherwise.
* Note that the rank is 1-based due to the span of zsl->header to the
* first element. */
static
unsigned
long
zslGetRank
(
zskiplist
*
zsl
,
double
score
,
robj
*
o
)
{
static
unsigned
long
zsl
istType
GetRank
(
zskiplist
*
zsl
,
double
score
,
robj
*
o
)
{
zskiplistNode
*
x
;
unsigned
long
rank
=
0
;
int
i
;
...
...
@@ -5788,7 +6097,7 @@ static unsigned long zslGetRank(zskiplist *zsl, double score, robj *o) {
}
/* Finds an element by its rank. The rank argument needs to be 1-based. */
zskiplistNode
*
zslGetElementByRank
(
zskiplist
*
zsl
,
unsigned
long
rank
)
{
zskiplistNode
*
zsl
istType
GetElementByRank
(
zskiplist
*
zsl
,
unsigned
long
rank
)
{
zskiplistNode
*
x
;
unsigned
long
traversed
=
0
;
int
i
;
...
...
@@ -6254,10 +6563,10 @@ static void zrangeGenericCommand(redisClient *c, int reverse) {
/* check if starting point is trivial, before searching
* the element in log(N) time */
if
(
reverse
)
{
ln
=
start
==
0
?
zsl
->
tail
:
zslGetElementByRank
(
zsl
,
llen
-
start
);
ln
=
start
==
0
?
zsl
->
tail
:
zsl
istType
GetElementByRank
(
zsl
,
llen
-
start
);
}
else
{
ln
=
start
==
0
?
zsl
->
header
->
forward
[
0
]
:
zslGetElementByRank
(
zsl
,
start
+
1
);
zsl
->
header
->
forward
[
0
]
:
zsl
istType
GetElementByRank
(
zsl
,
start
+
1
);
}
/* Return the result in form of a multi-bulk reply */
...
...
@@ -6454,7 +6763,7 @@ static void zrankGenericCommand(redisClient *c, int reverse) {
}
score
=
dictGetEntryVal
(
de
);
rank
=
zslGetRank
(
zsl
,
*
score
,
c
->
argv
[
2
]);
rank
=
zsl
istType
GetRank
(
zsl
,
*
score
,
c
->
argv
[
2
]);
if
(
rank
)
{
if
(
reverse
)
{
addReplyLongLong
(
c
,
zsl
->
length
-
rank
);
...
...
@@ -7031,7 +7340,7 @@ static int sortCompare(const void *s1, const void *s2) {
* is optimized for speed and a bit less for readability */
static
void
sortCommand
(
redisClient
*
c
)
{
list
*
operations
;
int
outputlen
=
0
;
unsigned
int
outputlen
=
0
;
int
desc
=
0
,
alpha
=
0
;
int
limit_start
=
0
,
limit_count
=
-
1
,
start
,
end
;
int
j
,
dontsort
=
0
,
vectorlen
;
...
...
@@ -7101,7 +7410,7 @@ static void sortCommand(redisClient *c) {
/* Load the sorting vector with all the objects to sort */
switch
(
sortval
->
type
)
{
case
REDIS_LIST
:
vectorlen
=
listLength
(
(
list
*
)
sortval
->
ptr
);
break
;
case
REDIS_LIST
:
vectorlen
=
list
Type
Length
(
sortval
);
break
;
case
REDIS_SET
:
vectorlen
=
dictSize
((
dict
*
)
sortval
->
ptr
);
break
;
case
REDIS_ZSET
:
vectorlen
=
dictSize
(((
zset
*
)
sortval
->
ptr
)
->
dict
);
break
;
default:
vectorlen
=
0
;
redisPanic
(
"Bad SORT type"
);
/* Avoid GCC warning */
...
...
@@ -7110,18 +7419,15 @@ static void sortCommand(redisClient *c) {
j
=
0
;
if
(
sortval
->
type
==
REDIS_LIST
)
{
list
*
list
=
sortval
->
ptr
;
listNode
*
ln
;
listIter
li
;
listRewind
(
list
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
robj
*
ele
=
ln
->
value
;
vector
[
j
].
obj
=
ele
;
listTypeIterator
*
li
=
listTypeInitIterator
(
sortval
,
0
,
REDIS_TAIL
);
listTypeEntry
entry
;
while
(
listTypeNext
(
li
,
&
entry
))
{
vector
[
j
].
obj
=
listTypeGet
(
&
entry
);
vector
[
j
].
u
.
score
=
0
;
vector
[
j
].
u
.
cmpobj
=
NULL
;
j
++
;
}
listTypeReleaseIterator
(
li
);
}
else
{
dict
*
set
;
dictIterator
*
di
;
...
...
@@ -7231,8 +7537,7 @@ static void sortCommand(redisClient *c) {
}
}
}
else
{
robj
*
listObject
=
createListObject
();
list
*
listPtr
=
(
list
*
)
listObject
->
ptr
;
robj
*
sobj
=
createZiplistObject
();
/* STORE option specified, set the sorting result as a List object */
for
(
j
=
start
;
j
<=
end
;
j
++
)
{
...
...
@@ -7240,31 +7545,30 @@ static void sortCommand(redisClient *c) {
listIter
li
;
if
(
!
getop
)
{
listAddNodeTail
(
listPtr
,
vector
[
j
].
obj
);
incrRefCount
(
vector
[
j
].
obj
);
}
listRewind
(
operations
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
redisSortOperation
*
sop
=
ln
->
value
;
robj
*
val
=
lookupKeyByPattern
(
c
->
db
,
sop
->
pattern
,
vector
[
j
].
obj
);
listTypePush
(
sobj
,
vector
[
j
].
obj
,
REDIS_TAIL
);
}
else
{
listRewind
(
operations
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
redisSortOperation
*
sop
=
ln
->
value
;
robj
*
val
=
lookupKeyByPattern
(
c
->
db
,
sop
->
pattern
,
vector
[
j
].
obj
);
if
(
sop
->
type
==
REDIS_SORT_GET
)
{
if
(
!
val
)
{
listAddNodeTail
(
listPtr
,
createStringObject
(
""
,
0
));
if
(
sop
->
type
==
REDIS_SORT_GET
)
{
if
(
!
val
)
val
=
createStringObject
(
""
,
0
);
/* listTypePush does an incrRefCount, so we should take care
* care of the incremented refcount caused by either
* lookupKeyByPattern or createStringObject("",0) */
listTypePush
(
sobj
,
val
,
REDIS_TAIL
);
decrRefCount
(
val
);
}
else
{
/* We should do a incrRefCount on val because it is
* added to the list, but also a decrRefCount because
* it is returned by lookupKeyByPattern. This results
* in doing nothing at all. */
listAddNodeTail
(
listPtr
,
val
);
/* always fails */
redisAssert
(
sop
->
type
==
REDIS_SORT_GET
);
}
}
else
{
redisAssert
(
sop
->
type
==
REDIS_SORT_GET
);
/* always fails */
}
}
}
dbReplace
(
c
->
db
,
storekey
,
listObject
);
dbReplace
(
c
->
db
,
storekey
,
sobj
);
/* Note: we add 1 because the DB is dirty anyway since even if the
* SORT result is empty a new key is set and maybe the old content
* replaced. */
...
...
@@ -7273,6 +7577,9 @@ static void sortCommand(redisClient *c) {
}
/* Cleanup */
if
(
sortval
->
type
==
REDIS_LIST
)
for
(
j
=
0
;
j
<
vectorlen
;
j
++
)
decrRefCount
(
vector
[
j
].
obj
);
decrRefCount
(
sortval
);
listRelease
(
operations
);
for
(
j
=
0
;
j
<
vectorlen
;
j
++
)
{
...
...
@@ -8597,40 +8904,17 @@ fmterr:
exit
(
1
);
}
/* Write an object into a file in the bulk format $<count>\r\n<payload>\r\n */
static
int
fwriteBulkObject
(
FILE
*
fp
,
robj
*
obj
)
{
char
buf
[
128
];
int
decrrc
=
0
;
/* Avoid the incr/decr ref count business if possible to help
* copy-on-write (we are often in a child process when this function
* is called).
* Also makes sure that key objects don't get incrRefCount-ed when VM
* is enabled */
if
(
obj
->
encoding
!=
REDIS_ENCODING_RAW
)
{
obj
=
getDecodedObject
(
obj
);
decrrc
=
1
;
}
snprintf
(
buf
,
sizeof
(
buf
),
"$%ld
\r\n
"
,(
long
)
sdslen
(
obj
->
ptr
));
if
(
fwrite
(
buf
,
strlen
(
buf
),
1
,
fp
)
==
0
)
goto
err
;
if
(
sdslen
(
obj
->
ptr
)
&&
fwrite
(
obj
->
ptr
,
sdslen
(
obj
->
ptr
),
1
,
fp
)
==
0
)
goto
err
;
if
(
fwrite
(
"
\r\n
"
,
2
,
1
,
fp
)
==
0
)
goto
err
;
if
(
decrrc
)
decrRefCount
(
obj
);
return
1
;
err:
if
(
decrrc
)
decrRefCount
(
obj
);
return
0
;
}
/* Write binary-safe string into a file in the bulkformat
* $<count>\r\n<payload>\r\n */
static
int
fwriteBulkString
(
FILE
*
fp
,
char
*
s
,
unsigned
long
len
)
{
char
buf
[
128
];
snprintf
(
buf
,
sizeof
(
buf
),
"$%ld
\r\n
"
,(
unsigned
long
)
len
);
if
(
fwrite
(
buf
,
strlen
(
buf
),
1
,
fp
)
==
0
)
return
0
;
if
(
len
&&
fwrite
(
s
,
len
,
1
,
fp
)
==
0
)
return
0
;
char
cbuf
[
128
];
int
clen
;
cbuf
[
0
]
=
'$'
;
clen
=
1
+
ll2string
(
cbuf
+
1
,
sizeof
(
cbuf
)
-
1
,
len
);
cbuf
[
clen
++
]
=
'\r'
;
cbuf
[
clen
++
]
=
'\n'
;
if
(
fwrite
(
cbuf
,
clen
,
1
,
fp
)
==
0
)
return
0
;
if
(
len
>
0
&&
fwrite
(
s
,
len
,
1
,
fp
)
==
0
)
return
0
;
if
(
fwrite
(
"
\r\n
"
,
2
,
1
,
fp
)
==
0
)
return
0
;
return
1
;
}
...
...
@@ -8647,16 +8931,28 @@ static int fwriteBulkDouble(FILE *fp, double d) {
}
/* Write a long value in bulk format $<count>\r\n<payload>\r\n */
static
int
fwriteBulkLong
(
FILE
*
fp
,
long
l
)
{
char
buf
[
128
],
lbuf
[
128
];
snprintf
(
lbuf
,
sizeof
(
lbuf
),
"%ld
\r\n
"
,
l
);
snprintf
(
buf
,
sizeof
(
buf
),
"$%lu
\r\n
"
,(
unsigned
long
)
strlen
(
lbuf
)
-
2
);
if
(
fwrite
(
buf
,
strlen
(
buf
),
1
,
fp
)
==
0
)
return
0
;
if
(
fwrite
(
lbuf
,
strlen
(
lbuf
),
1
,
fp
)
==
0
)
return
0
;
static
int
fwriteBulkLongLong
(
FILE
*
fp
,
long
long
l
)
{
char
bbuf
[
128
],
lbuf
[
128
];
unsigned
int
blen
,
llen
;
llen
=
ll2string
(
lbuf
,
32
,
l
);
blen
=
snprintf
(
bbuf
,
sizeof
(
bbuf
),
"$%u
\r\n
%s
\r\n
"
,
llen
,
lbuf
);
if
(
fwrite
(
bbuf
,
blen
,
1
,
fp
)
==
0
)
return
0
;
return
1
;
}
/* Delegate writing an object to writing a bulk string or bulk long long. */
static
int
fwriteBulkObject
(
FILE
*
fp
,
robj
*
obj
)
{
/* Avoid using getDecodedObject to help copy-on-write (we are often
* in a child process when this function is called). */
if
(
obj
->
encoding
==
REDIS_ENCODING_INT
)
{
return
fwriteBulkLongLong
(
fp
,(
long
)
obj
->
ptr
);
}
else
if
(
obj
->
encoding
==
REDIS_ENCODING_RAW
)
{
return
fwriteBulkString
(
fp
,
obj
->
ptr
,
sdslen
(
obj
->
ptr
));
}
else
{
redisPanic
(
"Unknown string encoding"
);
}
}
/* Write a sequence of commands able to fully rebuild the dataset into
* "filename". Used both by REWRITEAOF and BGREWRITEAOF. */
static
int
rewriteAppendOnlyFile
(
char
*
filename
)
{
...
...
@@ -8688,7 +8984,7 @@ static int rewriteAppendOnlyFile(char *filename) {
/* SELECT the new DB */
if
(
fwrite
(
selectcmd
,
sizeof
(
selectcmd
)
-
1
,
1
,
fp
)
==
0
)
goto
werr
;
if
(
fwriteBulkLong
(
fp
,
j
)
==
0
)
goto
werr
;
if
(
fwriteBulkLong
Long
(
fp
,
j
)
==
0
)
goto
werr
;
/* Iterate this DB writing every entry */
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
...
...
@@ -8723,18 +9019,41 @@ static int rewriteAppendOnlyFile(char *filename) {
if
(
fwriteBulkObject
(
fp
,
o
)
==
0
)
goto
werr
;
}
else
if
(
o
->
type
==
REDIS_LIST
)
{
/* Emit the RPUSHes needed to rebuild the list */
list
*
list
=
o
->
ptr
;
listNode
*
ln
;
listIter
li
;
char
cmd
[]
=
"*3
\r\n
$5
\r\n
RPUSH
\r\n
"
;
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
unsigned
char
*
zl
=
o
->
ptr
;
unsigned
char
*
p
=
ziplistIndex
(
zl
,
0
);
unsigned
char
*
vstr
;
unsigned
int
vlen
;
long
long
vlong
;
while
(
ziplistGet
(
p
,
&
vstr
,
&
vlen
,
&
vlong
))
{
if
(
fwrite
(
cmd
,
sizeof
(
cmd
)
-
1
,
1
,
fp
)
==
0
)
goto
werr
;
if
(
fwriteBulkObject
(
fp
,
&
key
)
==
0
)
goto
werr
;
if
(
vstr
)
{
if
(
fwriteBulkString
(
fp
,(
char
*
)
vstr
,
vlen
)
==
0
)
goto
werr
;
}
else
{
if
(
fwriteBulkLongLong
(
fp
,
vlong
)
==
0
)
goto
werr
;
}
p
=
ziplistNext
(
zl
,
p
);
}
}
else
if
(
o
->
encoding
==
REDIS_ENCODING_LIST
)
{
list
*
list
=
o
->
ptr
;
listNode
*
ln
;
listIter
li
;
listRewind
(
list
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
char
cmd
[]
=
"*3
\r\n
$5
\r\n
RPUSH
\r\n
"
;
robj
*
eleobj
=
listNodeValue
(
ln
);
listRewind
(
list
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
robj
*
eleobj
=
listNodeValue
(
ln
);
if
(
fwrite
(
cmd
,
sizeof
(
cmd
)
-
1
,
1
,
fp
)
==
0
)
goto
werr
;
if
(
fwriteBulkObject
(
fp
,
&
key
)
==
0
)
goto
werr
;
if
(
fwriteBulkObject
(
fp
,
eleobj
)
==
0
)
goto
werr
;
if
(
fwrite
(
cmd
,
sizeof
(
cmd
)
-
1
,
1
,
fp
)
==
0
)
goto
werr
;
if
(
fwriteBulkObject
(
fp
,
&
key
)
==
0
)
goto
werr
;
if
(
fwriteBulkObject
(
fp
,
eleobj
)
==
0
)
goto
werr
;
}
}
else
{
redisPanic
(
"Unknown list encoding"
);
}
}
else
if
(
o
->
type
==
REDIS_SET
)
{
/* Emit the SADDs needed to rebuild the set */
...
...
@@ -8810,7 +9129,7 @@ static int rewriteAppendOnlyFile(char *filename) {
if
(
expiretime
<
now
)
continue
;
if
(
fwrite
(
cmd
,
sizeof
(
cmd
)
-
1
,
1
,
fp
)
==
0
)
goto
werr
;
if
(
fwriteBulkObject
(
fp
,
&
key
)
==
0
)
goto
werr
;
if
(
fwriteBulkLong
(
fp
,
expiretime
)
==
0
)
goto
werr
;
if
(
fwriteBulkLong
Long
(
fp
,
expiretime
)
==
0
)
goto
werr
;
}
if
(
swapped
)
decrRefCount
(
o
);
}
...
...
@@ -10759,16 +11078,14 @@ static void computeDatasetDigest(unsigned char *final) {
if
(
o
->
type
==
REDIS_STRING
)
{
mixObjectDigest
(
digest
,
o
);
}
else
if
(
o
->
type
==
REDIS_LIST
)
{
list
*
list
=
o
->
ptr
;
listNode
*
ln
;
listIter
li
;
listRewind
(
list
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
robj
*
eleobj
=
listNodeValue
(
ln
);
listTypeIterator
*
li
=
listTypeInitIterator
(
o
,
0
,
REDIS_TAIL
);
listTypeEntry
entry
;
while
(
listTypeNext
(
li
,
&
entry
))
{
robj
*
eleobj
=
listTypeGet
(
&
entry
);
mixObjectDigest
(
digest
,
eleobj
);
decrRefCount
(
eleobj
);
}
listTypeReleaseIterator
(
li
);
}
else
if
(
o
->
type
==
REDIS_SET
)
{
dict
*
set
=
o
->
ptr
;
dictIterator
*
di
=
dictGetIterator
(
set
);
...
...
staticsymbols.h
View file @
422cf21f
...
...
@@ -100,7 +100,7 @@ static struct redisFunctionSym symsTable[] = {
{
"freeStringObject"
,(
unsigned
long
)
freeStringObject
},
{
"freeZsetObject"
,(
unsigned
long
)
freeZsetObject
},
{
"fwriteBulkDouble"
,(
unsigned
long
)
fwriteBulkDouble
},
{
"fwriteBulkLong"
,(
unsigned
long
)
fwriteBulkLong
},
{
"fwriteBulkLong
Long
"
,(
unsigned
long
)
fwriteBulkLong
Long
},
{
"fwriteBulkObject"
,(
unsigned
long
)
fwriteBulkObject
},
{
"fwriteBulkString"
,(
unsigned
long
)
fwriteBulkString
},
{
"genRedisInfoString"
,(
unsigned
long
)
genRedisInfoString
},
...
...
ziplist.c
0 → 100644
View file @
422cf21f
/* Memory layout of a ziplist, containing "foo", "bar", "quux":
* <zlbytes><zllen><len>"foo"<len>"bar"<len>"quux"
*
* <zlbytes> is an unsigned integer to hold the number of bytes that
* the ziplist occupies. This is stored to not have to traverse the ziplist
* to know the new length when pushing.
*
* <zllen> is the number of items in the ziplist. When this value is
* greater than 254, we need to traverse the entire list to know
* how many items it holds.
*
* <len> is the number of bytes occupied by a single entry. When this
* number is greater than 253, the length will occupy 5 bytes, where
* the extra bytes contain an unsigned integer to hold the length.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
#include <limits.h>
#include "zmalloc.h"
#include "sds.h"
#include "ziplist.h"
/* Important note: the ZIP_END value is used to depict the end of the
* ziplist structure. When a pointer contains an entry, the first couple
* of bytes contain the encoded length of the previous entry. This length
* is encoded as ZIP_ENC_RAW length, so the first two bits will contain 00
* and the byte will therefore never have a value of 255. */
#define ZIP_END 255
#define ZIP_BIGLEN 254
/* Entry encoding */
#define ZIP_ENC_RAW 0
#define ZIP_ENC_INT16 1
#define ZIP_ENC_INT32 2
#define ZIP_ENC_INT64 3
#define ZIP_ENCODING(p) ((p)[0] >> 6)
/* Length encoding for raw entries */
#define ZIP_LEN_INLINE 0
#define ZIP_LEN_UINT16 1
#define ZIP_LEN_UINT32 2
/* Utility macros */
#define ZIPLIST_BYTES(zl) (*((uint32_t*)(zl)))
#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))
#define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))
#define ZIPLIST_HEADER_SIZE (sizeof(uint32_t)*2+sizeof(uint16_t))
#define ZIPLIST_ENTRY_HEAD(zl) ((zl)+ZIPLIST_HEADER_SIZE)
#define ZIPLIST_ENTRY_TAIL(zl) ((zl)+ZIPLIST_TAIL_OFFSET(zl))
#define ZIPLIST_ENTRY_END(zl) ((zl)+ZIPLIST_BYTES(zl)-1)
/* We know a positive increment can only be 1 because entries can only be
* pushed one at a time. */
#define ZIPLIST_INCR_LENGTH(zl,incr) { \
if (ZIPLIST_LENGTH(zl) < UINT16_MAX) ZIPLIST_LENGTH(zl)+=incr; }
typedef
struct
zlentry
{
unsigned
int
prevrawlensize
,
prevrawlen
;
unsigned
int
lensize
,
len
;
unsigned
int
headersize
;
unsigned
char
encoding
;
unsigned
char
*
p
;
}
zlentry
;
/* Return bytes needed to store integer encoded by 'encoding' */
static
unsigned
int
zipEncodingSize
(
unsigned
char
encoding
)
{
if
(
encoding
==
ZIP_ENC_INT16
)
{
return
sizeof
(
int16_t
);
}
else
if
(
encoding
==
ZIP_ENC_INT32
)
{
return
sizeof
(
int32_t
);
}
else
if
(
encoding
==
ZIP_ENC_INT64
)
{
return
sizeof
(
int64_t
);
}
assert
(
NULL
);
}
/* Decode the encoded length pointed by 'p'. If a pointer to 'lensize' is
* provided, it is set to the number of bytes required to encode the length. */
static
unsigned
int
zipDecodeLength
(
unsigned
char
*
p
,
unsigned
int
*
lensize
)
{
unsigned
char
encoding
=
ZIP_ENCODING
(
p
),
lenenc
;
unsigned
int
len
;
if
(
encoding
==
ZIP_ENC_RAW
)
{
lenenc
=
(
p
[
0
]
>>
4
)
&
0x3
;
if
(
lenenc
==
ZIP_LEN_INLINE
)
{
len
=
p
[
0
]
&
0xf
;
if
(
lensize
)
*
lensize
=
1
;
}
else
if
(
lenenc
==
ZIP_LEN_UINT16
)
{
len
=
p
[
1
]
|
(
p
[
2
]
<<
8
);
if
(
lensize
)
*
lensize
=
3
;
}
else
{
len
=
p
[
1
]
|
(
p
[
2
]
<<
8
)
|
(
p
[
3
]
<<
16
)
|
(
p
[
4
]
<<
24
);
if
(
lensize
)
*
lensize
=
5
;
}
}
else
{
len
=
zipEncodingSize
(
encoding
);
if
(
lensize
)
*
lensize
=
1
;
}
return
len
;
}
/* Encode the length 'l' writing it in 'p'. If p is NULL it just returns
* the amount of bytes required to encode such a length. */
static
unsigned
int
zipEncodeLength
(
unsigned
char
*
p
,
char
encoding
,
unsigned
int
rawlen
)
{
unsigned
char
len
=
1
,
lenenc
,
buf
[
5
];
if
(
encoding
==
ZIP_ENC_RAW
)
{
if
(
rawlen
<=
0xf
)
{
if
(
!
p
)
return
len
;
lenenc
=
ZIP_LEN_INLINE
;
buf
[
0
]
=
rawlen
;
}
else
if
(
rawlen
<=
0xffff
)
{
len
+=
2
;
if
(
!
p
)
return
len
;
lenenc
=
ZIP_LEN_UINT16
;
buf
[
1
]
=
(
rawlen
)
&
0xff
;
buf
[
2
]
=
(
rawlen
>>
8
)
&
0xff
;
}
else
{
len
+=
4
;
if
(
!
p
)
return
len
;
lenenc
=
ZIP_LEN_UINT32
;
buf
[
1
]
=
(
rawlen
)
&
0xff
;
buf
[
2
]
=
(
rawlen
>>
8
)
&
0xff
;
buf
[
3
]
=
(
rawlen
>>
16
)
&
0xff
;
buf
[
4
]
=
(
rawlen
>>
24
)
&
0xff
;
}
buf
[
0
]
=
(
lenenc
<<
4
)
|
(
buf
[
0
]
&
0xf
);
}
if
(
!
p
)
return
len
;
/* Apparently we need to store the length in 'p' */
buf
[
0
]
=
(
encoding
<<
6
)
|
(
buf
[
0
]
&
0x3f
);
memcpy
(
p
,
buf
,
len
);
return
len
;
}
/* Decode the length of the previous element stored at "p". */
static
unsigned
int
zipPrevDecodeLength
(
unsigned
char
*
p
,
unsigned
int
*
lensize
)
{
unsigned
int
len
=
*
p
;
if
(
len
<
ZIP_BIGLEN
)
{
if
(
lensize
)
*
lensize
=
1
;
}
else
{
if
(
lensize
)
*
lensize
=
1
+
sizeof
(
len
);
memcpy
(
&
len
,
p
+
1
,
sizeof
(
len
));
}
return
len
;
}
/* Encode the length of the previous entry and write it to "p". Return the
* number of bytes needed to encode this length if "p" is NULL. */
static
unsigned
int
zipPrevEncodeLength
(
unsigned
char
*
p
,
unsigned
int
len
)
{
if
(
p
==
NULL
)
{
return
(
len
<
ZIP_BIGLEN
)
?
1
:
sizeof
(
len
)
+
1
;
}
else
{
if
(
len
<
ZIP_BIGLEN
)
{
p
[
0
]
=
len
;
return
1
;
}
else
{
p
[
0
]
=
ZIP_BIGLEN
;
memcpy
(
p
+
1
,
&
len
,
sizeof
(
len
));
return
1
+
sizeof
(
len
);
}
}
}
/* Return the difference in number of bytes needed to store the new length
* "len" on the entry pointed to by "p". */
static
int
zipPrevLenByteDiff
(
unsigned
char
*
p
,
unsigned
int
len
)
{
unsigned
int
prevlensize
;
zipPrevDecodeLength
(
p
,
&
prevlensize
);
return
zipPrevEncodeLength
(
NULL
,
len
)
-
prevlensize
;
}
/* Check if string pointed to by 'entry' can be encoded as an integer.
* Stores the integer value in 'v' and its encoding in 'encoding'.
* Warning: this function requires a NULL-terminated string! */
static
int
zipTryEncoding
(
unsigned
char
*
entry
,
long
long
*
v
,
unsigned
char
*
encoding
)
{
long
long
value
;
char
*
eptr
;
if
(
entry
[
0
]
==
'-'
||
(
entry
[
0
]
>=
'0'
&&
entry
[
0
]
<=
'9'
))
{
value
=
strtoll
((
char
*
)
entry
,
&
eptr
,
10
);
if
(
eptr
[
0
]
!=
'\0'
)
return
0
;
if
(
value
>=
INT16_MIN
&&
value
<=
INT16_MAX
)
{
*
encoding
=
ZIP_ENC_INT16
;
}
else
if
(
value
>=
INT32_MIN
&&
value
<=
INT32_MAX
)
{
*
encoding
=
ZIP_ENC_INT32
;
}
else
{
*
encoding
=
ZIP_ENC_INT64
;
}
*
v
=
value
;
return
1
;
}
return
0
;
}
/* Store integer 'value' at 'p', encoded as 'encoding' */
static
void
zipSaveInteger
(
unsigned
char
*
p
,
int64_t
value
,
unsigned
char
encoding
)
{
int16_t
i16
;
int32_t
i32
;
int64_t
i64
;
if
(
encoding
==
ZIP_ENC_INT16
)
{
i16
=
value
;
memcpy
(
p
,
&
i16
,
sizeof
(
i16
));
}
else
if
(
encoding
==
ZIP_ENC_INT32
)
{
i32
=
value
;
memcpy
(
p
,
&
i32
,
sizeof
(
i32
));
}
else
if
(
encoding
==
ZIP_ENC_INT64
)
{
i64
=
value
;
memcpy
(
p
,
&
i64
,
sizeof
(
i64
));
}
else
{
assert
(
NULL
);
}
}
/* Read integer encoded as 'encoding' from 'p' */
static
int64_t
zipLoadInteger
(
unsigned
char
*
p
,
unsigned
char
encoding
)
{
int16_t
i16
;
int32_t
i32
;
int64_t
i64
,
ret
;
if
(
encoding
==
ZIP_ENC_INT16
)
{
memcpy
(
&
i16
,
p
,
sizeof
(
i16
));
ret
=
i16
;
}
else
if
(
encoding
==
ZIP_ENC_INT32
)
{
memcpy
(
&
i32
,
p
,
sizeof
(
i32
));
ret
=
i32
;
}
else
if
(
encoding
==
ZIP_ENC_INT64
)
{
memcpy
(
&
i64
,
p
,
sizeof
(
i64
));
ret
=
i64
;
}
else
{
assert
(
NULL
);
}
return
ret
;
}
/* Return a struct with all information about an entry. */
static
zlentry
zipEntry
(
unsigned
char
*
p
)
{
zlentry
e
;
e
.
prevrawlen
=
zipPrevDecodeLength
(
p
,
&
e
.
prevrawlensize
);
e
.
len
=
zipDecodeLength
(
p
+
e
.
prevrawlensize
,
&
e
.
lensize
);
e
.
headersize
=
e
.
prevrawlensize
+
e
.
lensize
;
e
.
encoding
=
ZIP_ENCODING
(
p
+
e
.
prevrawlensize
);
e
.
p
=
p
;
return
e
;
}
/* Return the total number of bytes used by the entry at "p". */
static
unsigned
int
zipRawEntryLength
(
unsigned
char
*
p
)
{
zlentry
e
=
zipEntry
(
p
);
return
e
.
headersize
+
e
.
len
;
}
/* Create a new empty ziplist. */
unsigned
char
*
ziplistNew
(
void
)
{
unsigned
int
bytes
=
ZIPLIST_HEADER_SIZE
+
1
;
unsigned
char
*
zl
=
zmalloc
(
bytes
);
ZIPLIST_BYTES
(
zl
)
=
bytes
;
ZIPLIST_TAIL_OFFSET
(
zl
)
=
ZIPLIST_HEADER_SIZE
;
ZIPLIST_LENGTH
(
zl
)
=
0
;
zl
[
bytes
-
1
]
=
ZIP_END
;
return
zl
;
}
/* Resize the ziplist. */
static
unsigned
char
*
ziplistResize
(
unsigned
char
*
zl
,
unsigned
int
len
)
{
zl
=
zrealloc
(
zl
,
len
);
ZIPLIST_BYTES
(
zl
)
=
len
;
zl
[
len
-
1
]
=
ZIP_END
;
return
zl
;
}
/* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */
static
unsigned
char
*
__ziplistDelete
(
unsigned
char
*
zl
,
unsigned
char
*
p
,
unsigned
int
num
)
{
unsigned
int
i
,
totlen
,
deleted
=
0
;
int
nextdiff
=
0
;
zlentry
first
=
zipEntry
(
p
);
for
(
i
=
0
;
p
[
0
]
!=
ZIP_END
&&
i
<
num
;
i
++
)
{
p
+=
zipRawEntryLength
(
p
);
deleted
++
;
}
totlen
=
p
-
first
.
p
;
if
(
totlen
>
0
)
{
if
(
p
[
0
]
!=
ZIP_END
)
{
/* Tricky: storing the prevlen in this entry might reduce or
* increase the number of bytes needed, compared to the current
* prevlen. Note that we can always store this length because
* it was previously stored by an entry that is being deleted. */
nextdiff
=
zipPrevLenByteDiff
(
p
,
first
.
prevrawlen
);
zipPrevEncodeLength
(
p
-
nextdiff
,
first
.
prevrawlen
);
/* Update offset for tail */
ZIPLIST_TAIL_OFFSET
(
zl
)
-=
totlen
+
nextdiff
;
/* Move tail to the front of the ziplist */
memmove
(
first
.
p
,
p
-
nextdiff
,
ZIPLIST_BYTES
(
zl
)
-
(
p
-
zl
)
-
1
+
nextdiff
);
}
else
{
/* The entire tail was deleted. No need to move memory. */
ZIPLIST_TAIL_OFFSET
(
zl
)
=
(
first
.
p
-
zl
)
-
first
.
prevrawlen
;
}
/* Resize and update length */
zl
=
ziplistResize
(
zl
,
ZIPLIST_BYTES
(
zl
)
-
totlen
+
nextdiff
);
ZIPLIST_INCR_LENGTH
(
zl
,
-
deleted
);
}
return
zl
;
}
/* Insert item at "p". */
static
unsigned
char
*
__ziplistInsert
(
unsigned
char
*
zl
,
unsigned
char
*
p
,
unsigned
char
*
s
,
unsigned
int
slen
)
{
unsigned
int
curlen
=
ZIPLIST_BYTES
(
zl
),
reqlen
,
prevlen
=
0
;
unsigned
int
offset
,
nextdiff
=
0
;
unsigned
char
*
tail
;
unsigned
char
encoding
=
ZIP_ENC_RAW
;
long
long
value
;
zlentry
entry
;
/* Find out prevlen for the entry that is inserted. */
if
(
p
[
0
]
!=
ZIP_END
)
{
entry
=
zipEntry
(
p
);
prevlen
=
entry
.
prevrawlen
;
}
else
{
tail
=
ZIPLIST_ENTRY_TAIL
(
zl
);
if
(
tail
[
0
]
!=
ZIP_END
)
{
prevlen
=
zipRawEntryLength
(
tail
);
}
}
/* See if the entry can be encoded */
if
(
zipTryEncoding
(
s
,
&
value
,
&
encoding
))
{
reqlen
=
zipEncodingSize
(
encoding
);
}
else
{
reqlen
=
slen
;
}
/* We need space for both the length of the previous entry and
* the length of the payload. */
reqlen
+=
zipPrevEncodeLength
(
NULL
,
prevlen
);
reqlen
+=
zipEncodeLength
(
NULL
,
encoding
,
slen
);
/* When the insert position is not equal to the tail, we need to
* make sure that the next entry can hold this entry's length in
* its prevlen field. */
nextdiff
=
(
p
[
0
]
!=
ZIP_END
)
?
zipPrevLenByteDiff
(
p
,
reqlen
)
:
0
;
/* Store offset because a realloc may change the address of zl. */
offset
=
p
-
zl
;
zl
=
ziplistResize
(
zl
,
curlen
+
reqlen
+
nextdiff
);
p
=
zl
+
offset
;
/* Apply memory move when necessary and update tail offset. */
if
(
p
[
0
]
!=
ZIP_END
)
{
/* Subtract one because of the ZIP_END bytes */
memmove
(
p
+
reqlen
,
p
-
nextdiff
,
curlen
-
offset
-
1
+
nextdiff
);
/* Encode this entry's raw length in the next entry. */
zipPrevEncodeLength
(
p
+
reqlen
,
reqlen
);
/* Update offset for tail */
ZIPLIST_TAIL_OFFSET
(
zl
)
+=
reqlen
+
nextdiff
;
}
else
{
/* This element will be the new tail. */
ZIPLIST_TAIL_OFFSET
(
zl
)
=
p
-
zl
;
}
/* Write the entry */
p
+=
zipPrevEncodeLength
(
p
,
prevlen
);
p
+=
zipEncodeLength
(
p
,
encoding
,
slen
);
if
(
encoding
!=
ZIP_ENC_RAW
)
{
zipSaveInteger
(
p
,
value
,
encoding
);
}
else
{
memcpy
(
p
,
s
,
slen
);
}
ZIPLIST_INCR_LENGTH
(
zl
,
1
);
return
zl
;
}
unsigned
char
*
ziplistPush
(
unsigned
char
*
zl
,
unsigned
char
*
s
,
unsigned
int
slen
,
int
where
)
{
unsigned
char
*
p
;
p
=
(
where
==
ZIPLIST_HEAD
)
?
ZIPLIST_ENTRY_HEAD
(
zl
)
:
ZIPLIST_ENTRY_END
(
zl
);
return
__ziplistInsert
(
zl
,
p
,
s
,
slen
);
}
unsigned
char
*
ziplistPop
(
unsigned
char
*
zl
,
sds
*
target
,
int
where
)
{
zlentry
entry
;
unsigned
char
*
p
;
long
long
value
;
if
(
target
)
*
target
=
NULL
;
/* Get pointer to element to remove */
p
=
(
where
==
ZIPLIST_HEAD
)
?
ZIPLIST_ENTRY_HEAD
(
zl
)
:
ZIPLIST_ENTRY_TAIL
(
zl
);
if
(
*
p
==
ZIP_END
)
return
zl
;
entry
=
zipEntry
(
p
);
if
(
target
)
{
if
(
entry
.
encoding
==
ZIP_ENC_RAW
)
{
*
target
=
sdsnewlen
(
p
+
entry
.
headersize
,
entry
.
len
);
}
else
{
value
=
zipLoadInteger
(
p
+
entry
.
headersize
,
entry
.
encoding
);
*
target
=
sdscatprintf
(
sdsempty
(),
"%lld"
,
value
);
}
}
zl
=
__ziplistDelete
(
zl
,
p
,
1
);
return
zl
;
}
/* Returns an offset to use for iterating with ziplistNext. When the given
* index is negative, the list is traversed back to front. When the list
* doesn't contain an element at the provided index, NULL is returned. */
unsigned
char
*
ziplistIndex
(
unsigned
char
*
zl
,
int
index
)
{
unsigned
char
*
p
;
zlentry
entry
;
if
(
index
<
0
)
{
index
=
(
-
index
)
-
1
;
p
=
ZIPLIST_ENTRY_TAIL
(
zl
);
if
(
p
[
0
]
!=
ZIP_END
)
{
entry
=
zipEntry
(
p
);
while
(
entry
.
prevrawlen
>
0
&&
index
--
)
{
p
-=
entry
.
prevrawlen
;
entry
=
zipEntry
(
p
);
}
}
}
else
{
p
=
ZIPLIST_ENTRY_HEAD
(
zl
);
while
(
p
[
0
]
!=
ZIP_END
&&
index
--
)
{
p
+=
zipRawEntryLength
(
p
);
}
}
return
(
p
[
0
]
==
ZIP_END
||
index
>
0
)
?
NULL
:
p
;
}
/* Return pointer to next entry in ziplist. */
unsigned
char
*
ziplistNext
(
unsigned
char
*
zl
,
unsigned
char
*
p
)
{
((
void
)
zl
);
/* "p" could be equal to ZIP_END, caused by ziplistDelete,
* and we should return NULL. Otherwise, we should return NULL
* when the *next* element is ZIP_END (there is no next entry). */
if
(
p
[
0
]
==
ZIP_END
)
{
return
NULL
;
}
else
{
p
=
p
+
zipRawEntryLength
(
p
);
return
(
p
[
0
]
==
ZIP_END
)
?
NULL
:
p
;
}
}
/* Return pointer to previous entry in ziplist. */
unsigned
char
*
ziplistPrev
(
unsigned
char
*
zl
,
unsigned
char
*
p
)
{
zlentry
entry
;
/* Iterating backwards from ZIP_END should return the tail. When "p" is
* equal to the first element of the list, we're already at the head,
* and should return NULL. */
if
(
p
[
0
]
==
ZIP_END
)
{
p
=
ZIPLIST_ENTRY_TAIL
(
zl
);
return
(
p
[
0
]
==
ZIP_END
)
?
NULL
:
p
;
}
else
if
(
p
==
ZIPLIST_ENTRY_HEAD
(
zl
))
{
return
NULL
;
}
else
{
entry
=
zipEntry
(
p
);
return
p
-
entry
.
prevrawlen
;
}
}
/* Get entry pointer to by 'p' and store in either 'e' or 'v' depending
* on the encoding of the entry. 'e' is always set to NULL to be able
* to find out whether the string pointer or the integer value was set.
* Return 0 if 'p' points to the end of the zipmap, 1 otherwise. */
unsigned
int
ziplistGet
(
unsigned
char
*
p
,
unsigned
char
**
sstr
,
unsigned
int
*
slen
,
long
long
*
sval
)
{
zlentry
entry
;
if
(
p
==
NULL
||
p
[
0
]
==
ZIP_END
)
return
0
;
if
(
sstr
)
*
sstr
=
NULL
;
entry
=
zipEntry
(
p
);
if
(
entry
.
encoding
==
ZIP_ENC_RAW
)
{
if
(
sstr
)
{
*
slen
=
entry
.
len
;
*
sstr
=
p
+
entry
.
headersize
;
}
}
else
{
if
(
sval
)
{
*
sval
=
zipLoadInteger
(
p
+
entry
.
headersize
,
entry
.
encoding
);
}
}
return
1
;
}
/* Insert an entry at "p". */
unsigned
char
*
ziplistInsert
(
unsigned
char
*
zl
,
unsigned
char
*
p
,
unsigned
char
*
s
,
unsigned
int
slen
)
{
return
__ziplistInsert
(
zl
,
p
,
s
,
slen
);
}
/* Delete a single entry from the ziplist, pointed to by *p.
* Also update *p in place, to be able to iterate over the
* ziplist, while deleting entries. */
unsigned
char
*
ziplistDelete
(
unsigned
char
*
zl
,
unsigned
char
**
p
)
{
unsigned
int
offset
=
*
p
-
zl
;
zl
=
__ziplistDelete
(
zl
,
*
p
,
1
);
/* Store pointer to current element in p, because ziplistDelete will
* do a realloc which might result in a different "zl"-pointer.
* When the delete direction is back to front, we might delete the last
* entry and end up with "p" pointing to ZIP_END, so check this. */
*
p
=
zl
+
offset
;
return
zl
;
}
/* Delete a range of entries from the ziplist. */
unsigned
char
*
ziplistDeleteRange
(
unsigned
char
*
zl
,
unsigned
int
index
,
unsigned
int
num
)
{
unsigned
char
*
p
=
ziplistIndex
(
zl
,
index
);
return
(
p
==
NULL
)
?
zl
:
__ziplistDelete
(
zl
,
p
,
num
);
}
/* Compare entry pointer to by 'p' with 'entry'. Return 1 if equal. */
unsigned
int
ziplistCompare
(
unsigned
char
*
p
,
unsigned
char
*
sstr
,
unsigned
int
slen
)
{
zlentry
entry
;
unsigned
char
sencoding
;
long
long
zval
,
sval
;
if
(
p
[
0
]
==
ZIP_END
)
return
0
;
entry
=
zipEntry
(
p
);
if
(
entry
.
encoding
==
ZIP_ENC_RAW
)
{
/* Raw compare */
if
(
entry
.
len
==
slen
)
{
return
memcmp
(
p
+
entry
.
headersize
,
sstr
,
slen
)
==
0
;
}
else
{
return
0
;
}
}
else
{
/* Try to compare encoded values */
if
(
zipTryEncoding
(
sstr
,
&
sval
,
&
sencoding
))
{
if
(
entry
.
encoding
==
sencoding
)
{
zval
=
zipLoadInteger
(
p
+
entry
.
headersize
,
entry
.
encoding
);
return
zval
==
sval
;
}
}
}
return
0
;
}
/* Return length of ziplist. */
unsigned
int
ziplistLen
(
unsigned
char
*
zl
)
{
unsigned
int
len
=
0
;
if
(
ZIPLIST_LENGTH
(
zl
)
<
UINT16_MAX
)
{
len
=
ZIPLIST_LENGTH
(
zl
);
}
else
{
unsigned
char
*
p
=
zl
+
ZIPLIST_HEADER_SIZE
;
while
(
*
p
!=
ZIP_END
)
{
p
+=
zipRawEntryLength
(
p
);
len
++
;
}
/* Re-store length if small enough */
if
(
len
<
UINT16_MAX
)
ZIPLIST_LENGTH
(
zl
)
=
len
;
}
return
len
;
}
/* Return size in bytes of ziplist. */
unsigned
int
ziplistSize
(
unsigned
char
*
zl
)
{
return
ZIPLIST_BYTES
(
zl
);
}
void
ziplistRepr
(
unsigned
char
*
zl
)
{
unsigned
char
*
p
;
zlentry
entry
;
printf
(
"{total bytes %d} {length %u}
\n
"
,
ZIPLIST_BYTES
(
zl
),
ZIPLIST_LENGTH
(
zl
));
p
=
ZIPLIST_ENTRY_HEAD
(
zl
);
while
(
*
p
!=
ZIP_END
)
{
entry
=
zipEntry
(
p
);
printf
(
"{offset %ld, header %u, payload %u} "
,
p
-
zl
,
entry
.
headersize
,
entry
.
len
);
p
+=
entry
.
headersize
;
if
(
entry
.
encoding
==
ZIP_ENC_RAW
)
{
fwrite
(
p
,
entry
.
len
,
1
,
stdout
);
}
else
{
printf
(
"%lld"
,
zipLoadInteger
(
p
,
entry
.
encoding
));
}
printf
(
"
\n
"
);
p
+=
entry
.
len
;
}
printf
(
"{end}
\n\n
"
);
}
#ifdef ZIPLIST_TEST_MAIN
#include <sys/time.h>
unsigned
char
*
createList
()
{
unsigned
char
*
zl
=
ziplistNew
();
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
"foo"
,
3
,
ZIPLIST_TAIL
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
"quux"
,
4
,
ZIPLIST_TAIL
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
"hello"
,
5
,
ZIPLIST_HEAD
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
"1024"
,
4
,
ZIPLIST_TAIL
);
return
zl
;
}
unsigned
char
*
createIntList
()
{
unsigned
char
*
zl
=
ziplistNew
();
char
buf
[
32
];
sprintf
(
buf
,
"100"
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
buf
,
strlen
(
buf
),
ZIPLIST_TAIL
);
sprintf
(
buf
,
"128000"
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
buf
,
strlen
(
buf
),
ZIPLIST_TAIL
);
sprintf
(
buf
,
"-100"
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
buf
,
strlen
(
buf
),
ZIPLIST_HEAD
);
sprintf
(
buf
,
"4294967296"
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
buf
,
strlen
(
buf
),
ZIPLIST_HEAD
);
sprintf
(
buf
,
"non integer"
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
buf
,
strlen
(
buf
),
ZIPLIST_TAIL
);
sprintf
(
buf
,
"much much longer non integer"
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
buf
,
strlen
(
buf
),
ZIPLIST_TAIL
);
return
zl
;
}
long
long
usec
(
void
)
{
struct
timeval
tv
;
gettimeofday
(
&
tv
,
NULL
);
return
(((
long
long
)
tv
.
tv_sec
)
*
1000000
)
+
tv
.
tv_usec
;
}
void
stress
(
int
pos
,
int
num
,
int
maxsize
,
int
dnum
)
{
int
i
,
j
,
k
;
unsigned
char
*
zl
;
char
posstr
[
2
][
5
]
=
{
"HEAD"
,
"TAIL"
};
long
long
start
;
for
(
i
=
0
;
i
<
maxsize
;
i
+=
dnum
)
{
zl
=
ziplistNew
();
for
(
j
=
0
;
j
<
i
;
j
++
)
{
zl
=
ziplistPush
(
zl
,(
unsigned
char
*
)
"quux"
,
4
,
ZIPLIST_TAIL
);
}
/* Do num times a push+pop from pos */
start
=
usec
();
for
(
k
=
0
;
k
<
num
;
k
++
)
{
zl
=
ziplistPush
(
zl
,(
unsigned
char
*
)
"quux"
,
4
,
pos
);
zl
=
ziplistDeleteRange
(
zl
,
0
,
1
);
}
printf
(
"List size: %8d, bytes: %8d, %dx push+pop (%s): %6lld usec
\n
"
,
i
,
ZIPLIST_BYTES
(
zl
),
num
,
posstr
[
pos
],
usec
()
-
start
);
zfree
(
zl
);
}
}
int
main
(
int
argc
,
char
**
argv
)
{
unsigned
char
*
zl
,
*
p
;
unsigned
char
*
entry
;
unsigned
int
elen
;
long
long
value
;
sds
s
;
zl
=
createIntList
();
ziplistRepr
(
zl
);
zl
=
createList
();
ziplistRepr
(
zl
);
zl
=
ziplistPop
(
zl
,
&
s
,
ZIPLIST_TAIL
);
printf
(
"Pop tail: %s (length %ld)
\n
"
,
s
,
sdslen
(
s
));
ziplistRepr
(
zl
);
zl
=
ziplistPop
(
zl
,
&
s
,
ZIPLIST_HEAD
);
printf
(
"Pop head: %s (length %ld)
\n
"
,
s
,
sdslen
(
s
));
ziplistRepr
(
zl
);
zl
=
ziplistPop
(
zl
,
&
s
,
ZIPLIST_TAIL
);
printf
(
"Pop tail: %s (length %ld)
\n
"
,
s
,
sdslen
(
s
));
ziplistRepr
(
zl
);
zl
=
ziplistPop
(
zl
,
&
s
,
ZIPLIST_TAIL
);
printf
(
"Pop tail: %s (length %ld)
\n
"
,
s
,
sdslen
(
s
));
ziplistRepr
(
zl
);
printf
(
"Get element at index 3:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
3
);
if
(
!
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"ERROR: Could not access index 3
\n
"
);
return
1
;
}
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
printf
(
"
\n
"
);
}
else
{
printf
(
"%lld
\n
"
,
value
);
}
printf
(
"
\n
"
);
}
printf
(
"Get element at index 4 (out of range):
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
4
);
if
(
p
==
NULL
)
{
printf
(
"No entry
\n
"
);
}
else
{
printf
(
"ERROR: Out of range index should return NULL, returned offset: %ld
\n
"
,
p
-
zl
);
return
1
;
}
printf
(
"
\n
"
);
}
printf
(
"Get element at index -1 (last element):
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
-
1
);
if
(
!
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"ERROR: Could not access index -1
\n
"
);
return
1
;
}
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
printf
(
"
\n
"
);
}
else
{
printf
(
"%lld
\n
"
,
value
);
}
printf
(
"
\n
"
);
}
printf
(
"Get element at index -4 (first element):
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
-
4
);
if
(
!
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"ERROR: Could not access index -4
\n
"
);
return
1
;
}
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
printf
(
"
\n
"
);
}
else
{
printf
(
"%lld
\n
"
,
value
);
}
printf
(
"
\n
"
);
}
printf
(
"Get element at index -5 (reverse out of range):
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
-
5
);
if
(
p
==
NULL
)
{
printf
(
"No entry
\n
"
);
}
else
{
printf
(
"ERROR: Out of range index should return NULL, returned offset: %ld
\n
"
,
p
-
zl
);
return
1
;
}
printf
(
"
\n
"
);
}
printf
(
"Iterate list from 0 to end:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
0
);
while
(
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"Entry: "
);
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
}
else
{
printf
(
"%lld"
,
value
);
}
p
=
ziplistNext
(
zl
,
p
);
printf
(
"
\n
"
);
}
printf
(
"
\n
"
);
}
printf
(
"Iterate list from 1 to end:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
1
);
while
(
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"Entry: "
);
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
}
else
{
printf
(
"%lld"
,
value
);
}
p
=
ziplistNext
(
zl
,
p
);
printf
(
"
\n
"
);
}
printf
(
"
\n
"
);
}
printf
(
"Iterate list from 2 to end:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
2
);
while
(
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"Entry: "
);
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
}
else
{
printf
(
"%lld"
,
value
);
}
p
=
ziplistNext
(
zl
,
p
);
printf
(
"
\n
"
);
}
printf
(
"
\n
"
);
}
printf
(
"Iterate starting out of range:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
4
);
if
(
!
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"No entry
\n
"
);
}
else
{
printf
(
"ERROR
\n
"
);
}
printf
(
"
\n
"
);
}
printf
(
"Iterate from back to front:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
-
1
);
while
(
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"Entry: "
);
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
}
else
{
printf
(
"%lld"
,
value
);
}
p
=
ziplistPrev
(
zl
,
p
);
printf
(
"
\n
"
);
}
printf
(
"
\n
"
);
}
printf
(
"Iterate from back to front, deleting all items:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
-
1
);
while
(
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"Entry: "
);
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
}
else
{
printf
(
"%lld"
,
value
);
}
zl
=
ziplistDelete
(
zl
,
&
p
);
p
=
ziplistPrev
(
zl
,
p
);
printf
(
"
\n
"
);
}
printf
(
"
\n
"
);
}
printf
(
"Delete inclusive range 0,0:
\n
"
);
{
zl
=
createList
();
zl
=
ziplistDeleteRange
(
zl
,
0
,
1
);
ziplistRepr
(
zl
);
}
printf
(
"Delete inclusive range 0,1:
\n
"
);
{
zl
=
createList
();
zl
=
ziplistDeleteRange
(
zl
,
0
,
2
);
ziplistRepr
(
zl
);
}
printf
(
"Delete inclusive range 1,2:
\n
"
);
{
zl
=
createList
();
zl
=
ziplistDeleteRange
(
zl
,
1
,
2
);
ziplistRepr
(
zl
);
}
printf
(
"Delete with start index out of range:
\n
"
);
{
zl
=
createList
();
zl
=
ziplistDeleteRange
(
zl
,
5
,
1
);
ziplistRepr
(
zl
);
}
printf
(
"Delete with num overflow:
\n
"
);
{
zl
=
createList
();
zl
=
ziplistDeleteRange
(
zl
,
1
,
5
);
ziplistRepr
(
zl
);
}
printf
(
"Delete foo while iterating:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
0
);
while
(
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
if
(
entry
&&
strncmp
(
"foo"
,(
char
*
)
entry
,
elen
)
==
0
)
{
printf
(
"Delete foo
\n
"
);
zl
=
ziplistDelete
(
zl
,
&
p
);
}
else
{
printf
(
"Entry: "
);
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
}
else
{
printf
(
"%lld"
,
value
);
}
p
=
ziplistNext
(
zl
,
p
);
printf
(
"
\n
"
);
}
}
printf
(
"
\n
"
);
ziplistRepr
(
zl
);
}
printf
(
"Create long list and check indices:
\n
"
);
{
zl
=
ziplistNew
();
char
buf
[
32
];
int
i
,
len
;
for
(
i
=
0
;
i
<
1000
;
i
++
)
{
len
=
sprintf
(
buf
,
"%d"
,
i
);
zl
=
ziplistPush
(
zl
,(
unsigned
char
*
)
buf
,
len
,
ZIPLIST_TAIL
);
}
for
(
i
=
0
;
i
<
1000
;
i
++
)
{
p
=
ziplistIndex
(
zl
,
i
);
assert
(
ziplistGet
(
p
,
NULL
,
NULL
,
&
value
));
assert
(
i
==
value
);
p
=
ziplistIndex
(
zl
,
-
i
-
1
);
assert
(
ziplistGet
(
p
,
NULL
,
NULL
,
&
value
));
assert
(
999
-
i
==
value
);
}
printf
(
"SUCCESS
\n\n
"
);
}
printf
(
"Compare strings with ziplist entries:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
0
);
if
(
!
ziplistCompare
(
p
,(
unsigned
char
*
)
"hello"
,
5
))
{
printf
(
"ERROR: not
\"
hello
\"\n
"
);
return
1
;
}
if
(
ziplistCompare
(
p
,(
unsigned
char
*
)
"hella"
,
5
))
{
printf
(
"ERROR:
\"
hella
\"\n
"
);
return
1
;
}
p
=
ziplistIndex
(
zl
,
3
);
if
(
!
ziplistCompare
(
p
,(
unsigned
char
*
)
"1024"
,
4
))
{
printf
(
"ERROR: not
\"
1024
\"\n
"
);
return
1
;
}
if
(
ziplistCompare
(
p
,(
unsigned
char
*
)
"1025"
,
4
))
{
printf
(
"ERROR:
\"
1025
\"\n
"
);
return
1
;
}
printf
(
"SUCCESS
\n
"
);
}
printf
(
"Stress with variable ziplist size:
\n
"
);
{
stress
(
ZIPLIST_HEAD
,
100000
,
16384
,
256
);
stress
(
ZIPLIST_TAIL
,
100000
,
16384
,
256
);
}
return
0
;
}
#endif
ziplist.h
0 → 100644
View file @
422cf21f
#define ZIPLIST_HEAD 0
#define ZIPLIST_TAIL 1
unsigned
char
*
ziplistNew
(
void
);
unsigned
char
*
ziplistPush
(
unsigned
char
*
zl
,
unsigned
char
*
s
,
unsigned
int
slen
,
int
where
);
unsigned
char
*
ziplistPop
(
unsigned
char
*
zl
,
sds
*
target
,
int
where
);
unsigned
char
*
ziplistIndex
(
unsigned
char
*
zl
,
int
index
);
unsigned
char
*
ziplistNext
(
unsigned
char
*
zl
,
unsigned
char
*
p
);
unsigned
char
*
ziplistPrev
(
unsigned
char
*
zl
,
unsigned
char
*
p
);
unsigned
int
ziplistGet
(
unsigned
char
*
p
,
unsigned
char
**
sval
,
unsigned
int
*
slen
,
long
long
*
lval
);
unsigned
char
*
ziplistInsert
(
unsigned
char
*
zl
,
unsigned
char
*
p
,
unsigned
char
*
s
,
unsigned
int
slen
);
unsigned
char
*
ziplistDelete
(
unsigned
char
*
zl
,
unsigned
char
**
p
);
unsigned
char
*
ziplistDeleteRange
(
unsigned
char
*
zl
,
unsigned
int
index
,
unsigned
int
num
);
unsigned
int
ziplistCompare
(
unsigned
char
*
p
,
unsigned
char
*
s
,
unsigned
int
slen
);
unsigned
int
ziplistLen
(
unsigned
char
*
zl
);
unsigned
int
ziplistSize
(
unsigned
char
*
zl
);
zipmap.c
View file @
422cf21f
...
...
@@ -176,7 +176,6 @@ static unsigned long zipmapRequiredLength(unsigned int klen, unsigned int vlen)
/* Return the total amount used by a key (encoded length + payload) */
static
unsigned
int
zipmapRawKeyLength
(
unsigned
char
*
p
)
{
unsigned
int
l
=
zipmapDecodeLength
(
p
);
return
zipmapEncodeLength
(
NULL
,
l
)
+
l
;
}
...
...
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