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
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