Compare commits
24
Commits
4.0.8
...
rdbchanges
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfbfd0dda7 | ||
|
|
dee2318469 | ||
|
|
734480be14 | ||
|
|
f50fa6db2f | ||
|
|
f562990237 | ||
|
|
0760c1f2f4 | ||
|
|
88b4830861 | ||
|
|
0b37730ef6 | ||
|
|
8842de1a7a | ||
|
|
32266be290 | ||
|
|
919e8c3021 | ||
|
|
b49709e64f | ||
|
|
cf5750f245 | ||
|
|
50fe414577 | ||
|
|
26fe373656 | ||
|
|
c865809ca7 | ||
|
|
8919e15341 | ||
|
|
73582497f8 | ||
|
|
23a93d5c54 | ||
|
|
cb8032d6c9 | ||
|
|
2cf51b4678 | ||
|
|
36f7ca08cd | ||
|
|
b39bfb3282 | ||
|
|
ff9e5b216f |
Vendored
+1
-1
@@ -58,7 +58,7 @@ ifeq ($(uname_S),SunOS)
|
||||
LUA_CFLAGS= -D__C99FEATURES__=1
|
||||
endif
|
||||
|
||||
LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI -DENABLE_CJSON_GLOBAL $(CFLAGS)
|
||||
LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI -DENABLE_CJSON_GLOBAL -DREDIS_STATIC='' $(CFLAGS)
|
||||
LUA_LDFLAGS+= $(LDFLAGS)
|
||||
# lua's Makefile defines AR="ar rcu", which is unusual, and makes it more
|
||||
# challenging to cross-compile lua (and redis). These defines make it easier
|
||||
|
||||
+30
-5
@@ -818,11 +818,36 @@ notify-keyspace-events ""
|
||||
hash-max-ziplist-entries 512
|
||||
hash-max-ziplist-value 64
|
||||
|
||||
# Similarly to hashes, small lists are also encoded in a special way in order
|
||||
# to save a lot of space. The special representation is only used when
|
||||
# you are under the following limits:
|
||||
list-max-ziplist-entries 512
|
||||
list-max-ziplist-value 64
|
||||
# Lists are also encoded in a special way to save a lot of space.
|
||||
# The number of entries allowed per internal list node can be specified
|
||||
# as a fixed maximum size or a maximum number of elements.
|
||||
# For a fixed maximum size, use -5 through -1, meaning:
|
||||
# -5: max size: 64 Kb <-- not recommended for normal workloads
|
||||
# -4: max size: 32 Kb <-- not recommended
|
||||
# -3: max size: 16 Kb <-- probably not recommended
|
||||
# -2: max size: 8 Kb <-- good
|
||||
# -1: max size: 4 Kb <-- good
|
||||
# Positive numbers mean store up to _exactly_ that number of elements
|
||||
# per list node.
|
||||
# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size),
|
||||
# but if your use case is unique, adjust the settings as necessary.
|
||||
list-max-ziplist-size -2
|
||||
|
||||
# Lists may also be compressed.
|
||||
# Compress depth is the number of quicklist ziplist nodes from *each* side of
|
||||
# the list to *exclude* from compression. The head and tail of the list
|
||||
# are always uncompressed for fast push/pop operations. Settings are:
|
||||
# 0: disable all list compression
|
||||
# 1: depth 1 means "don't start compressing until after 1 node into the list,
|
||||
# going from either the head or tail"
|
||||
# So: [head]->node->node->...->node->[tail]
|
||||
# [head], [tail] will always be uncompressed; inner nodes will compress.
|
||||
# 2: [head]->[next]->node->node->...->node->[prev]->[tail]
|
||||
# 2 here means: don't compress head or head->next or tail->prev or tail,
|
||||
# but compress all nodes between them.
|
||||
# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail]
|
||||
# etc.
|
||||
list-compress-depth 0
|
||||
|
||||
# Sets have a special encoding in just one case: when a set is composed
|
||||
# of just strings that happen to be integers in radix 10 in the range
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@ OPTIMIZATION?=-O2
|
||||
DEPENDENCY_TARGETS=hiredis linenoise lua
|
||||
|
||||
# Default settings
|
||||
STD=-std=c99 -pedantic
|
||||
STD=-std=c99 -pedantic -DREDIS_STATIC=''
|
||||
WARN=-Wall -W
|
||||
OPT=$(OPTIMIZATION)
|
||||
|
||||
@@ -117,7 +117,7 @@ endif
|
||||
|
||||
REDIS_SERVER_NAME=redis-server
|
||||
REDIS_SENTINEL_NAME=redis-sentinel
|
||||
REDIS_SERVER_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 release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o
|
||||
REDIS_SERVER_OBJ=adlist.o quicklist.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 release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o
|
||||
REDIS_CLI_NAME=redis-cli
|
||||
REDIS_CLI_OBJ=anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o
|
||||
REDIS_BENCHMARK_NAME=redis-benchmark
|
||||
|
||||
@@ -770,52 +770,29 @@ int rioWriteBulkObject(rio *r, robj *obj) {
|
||||
int rewriteListObject(rio *r, robj *key, robj *o) {
|
||||
long long count = 0, items = listTypeLength(o);
|
||||
|
||||
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;
|
||||
if (o->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
quicklist *list = o->ptr;
|
||||
quicklistIter *li = quicklistGetIterator(list, AL_START_HEAD);
|
||||
quicklistEntry entry;
|
||||
|
||||
while(ziplistGet(p,&vstr,&vlen,&vlong)) {
|
||||
while (quicklistNext(li,&entry)) {
|
||||
if (count == 0) {
|
||||
int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ?
|
||||
REDIS_AOF_REWRITE_ITEMS_PER_CMD : items;
|
||||
|
||||
if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0;
|
||||
if (rioWriteBulkString(r,"RPUSH",5) == 0) return 0;
|
||||
if (rioWriteBulkObject(r,key) == 0) return 0;
|
||||
}
|
||||
if (vstr) {
|
||||
if (rioWriteBulkString(r,(char*)vstr,vlen) == 0) return 0;
|
||||
|
||||
if (entry.value) {
|
||||
if (rioWriteBulkString(r,(char*)entry.value,entry.sz) == 0) return 0;
|
||||
} else {
|
||||
if (rioWriteBulkLongLong(r,vlong) == 0) return 0;
|
||||
if (rioWriteBulkLongLong(r,entry.longval) == 0) return 0;
|
||||
}
|
||||
p = ziplistNext(zl,p);
|
||||
if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0;
|
||||
items--;
|
||||
}
|
||||
} else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {
|
||||
list *list = o->ptr;
|
||||
listNode *ln;
|
||||
listIter li;
|
||||
|
||||
listRewind(list,&li);
|
||||
while((ln = listNext(&li))) {
|
||||
robj *eleobj = listNodeValue(ln);
|
||||
|
||||
if (count == 0) {
|
||||
int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ?
|
||||
REDIS_AOF_REWRITE_ITEMS_PER_CMD : items;
|
||||
|
||||
if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0;
|
||||
if (rioWriteBulkString(r,"RPUSH",5) == 0) return 0;
|
||||
if (rioWriteBulkObject(r,key) == 0) return 0;
|
||||
}
|
||||
if (rioWriteBulkObject(r,eleobj) == 0) return 0;
|
||||
if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0;
|
||||
items--;
|
||||
}
|
||||
quicklistReleaseIterator(li);
|
||||
} else {
|
||||
redisPanic("Unknown list encoding");
|
||||
}
|
||||
|
||||
+16
-12
@@ -397,9 +397,13 @@ void loadServerConfigFromString(char *config) {
|
||||
} else if (!strcasecmp(argv[0],"hash-max-ziplist-value") && argc == 2) {
|
||||
server.hash_max_ziplist_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);
|
||||
/* DEAD OPTION */
|
||||
} else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2) {
|
||||
server.list_max_ziplist_value = memtoll(argv[1], NULL);
|
||||
/* DEAD OPTION */
|
||||
} else if (!strcasecmp(argv[0],"list-max-ziplist-size") && argc == 2) {
|
||||
server.list_max_ziplist_size = atoi(argv[1]);
|
||||
} else if (!strcasecmp(argv[0],"list-compress-depth") && argc == 2) {
|
||||
server.list_compress_depth = atoi(argv[1]);
|
||||
} else if (!strcasecmp(argv[0],"set-max-intset-entries") && argc == 2) {
|
||||
server.set_max_intset_entries = memtoll(argv[1], NULL);
|
||||
} else if (!strcasecmp(argv[0],"zset-max-ziplist-entries") && argc == 2) {
|
||||
@@ -795,12 +799,12 @@ void configSetCommand(redisClient *c) {
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-value")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
|
||||
server.hash_max_ziplist_value = ll;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-entries")) {
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-size")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
|
||||
server.list_max_ziplist_entries = ll;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-value")) {
|
||||
server.list_max_ziplist_size = ll;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"list-compress-depth")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
|
||||
server.list_max_ziplist_value = ll;
|
||||
server.list_compress_depth = ll;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"set-max-intset-entries")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
|
||||
server.set_max_intset_entries = ll;
|
||||
@@ -1047,10 +1051,10 @@ void configGetCommand(redisClient *c) {
|
||||
server.hash_max_ziplist_entries);
|
||||
config_get_numerical_field("hash-max-ziplist-value",
|
||||
server.hash_max_ziplist_value);
|
||||
config_get_numerical_field("list-max-ziplist-entries",
|
||||
server.list_max_ziplist_entries);
|
||||
config_get_numerical_field("list-max-ziplist-value",
|
||||
server.list_max_ziplist_value);
|
||||
config_get_numerical_field("list-max-ziplist-size",
|
||||
server.list_max_ziplist_size);
|
||||
config_get_numerical_field("list-compress-depth",
|
||||
server.list_compress_depth);
|
||||
config_get_numerical_field("set-max-intset-entries",
|
||||
server.set_max_intset_entries);
|
||||
config_get_numerical_field("zset-max-ziplist-entries",
|
||||
@@ -1857,8 +1861,8 @@ int rewriteConfig(char *path) {
|
||||
rewriteConfigNotifykeyspaceeventsOption(state);
|
||||
rewriteConfigNumericalOption(state,"hash-max-ziplist-entries",server.hash_max_ziplist_entries,REDIS_HASH_MAX_ZIPLIST_ENTRIES);
|
||||
rewriteConfigNumericalOption(state,"hash-max-ziplist-value",server.hash_max_ziplist_value,REDIS_HASH_MAX_ZIPLIST_VALUE);
|
||||
rewriteConfigNumericalOption(state,"list-max-ziplist-entries",server.list_max_ziplist_entries,REDIS_LIST_MAX_ZIPLIST_ENTRIES);
|
||||
rewriteConfigNumericalOption(state,"list-max-ziplist-value",server.list_max_ziplist_value,REDIS_LIST_MAX_ZIPLIST_VALUE);
|
||||
rewriteConfigNumericalOption(state,"list-max-ziplist-size",server.list_max_ziplist_size,REDIS_LIST_MAX_ZIPLIST_SIZE);
|
||||
rewriteConfigNumericalOption(state,"list-compress-depth",server.list_compress_depth,REDIS_LIST_COMPRESS_DEPTH);
|
||||
rewriteConfigNumericalOption(state,"set-max-intset-entries",server.set_max_intset_entries,REDIS_SET_MAX_INTSET_ENTRIES);
|
||||
rewriteConfigNumericalOption(state,"zset-max-ziplist-entries",server.zset_max_ziplist_entries,REDIS_ZSET_MAX_ZIPLIST_ENTRIES);
|
||||
rewriteConfigNumericalOption(state,"zset-max-ziplist-value",server.zset_max_ziplist_value,REDIS_ZSET_MAX_ZIPLIST_VALUE);
|
||||
|
||||
+35
-2
@@ -301,13 +301,46 @@ void debugCommand(redisClient *c) {
|
||||
val = dictGetVal(de);
|
||||
strenc = strEncoding(val->encoding);
|
||||
|
||||
char extra[128] = {0};
|
||||
if (val->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
char *nextra = extra;
|
||||
int remaining = sizeof(extra);
|
||||
quicklist *ql = val->ptr;
|
||||
/* Add number of quicklist nodes */
|
||||
int used = snprintf(nextra, remaining, " ql_nodes:%u", ql->len);
|
||||
nextra += used;
|
||||
remaining -= used;
|
||||
/* Add average quicklist fill factor */
|
||||
double avg = (double)ql->count/ql->len;
|
||||
used = snprintf(nextra, remaining, " ql_avg_node:%.2f", avg);
|
||||
nextra += used;
|
||||
remaining -= used;
|
||||
/* Add quicklist fill level / max ziplist size */
|
||||
used = snprintf(nextra, remaining, " ql_ziplist_max:%d", ql->fill);
|
||||
nextra += used;
|
||||
remaining -= used;
|
||||
/* Add isCompressed? */
|
||||
int compressed = ql->compress != 0;
|
||||
used = snprintf(nextra, remaining, " ql_compressed:%d", compressed);
|
||||
nextra += used;
|
||||
remaining -= used;
|
||||
/* Add total uncompressed size */
|
||||
unsigned long sz = 0;
|
||||
for (quicklistNode *node = ql->head; node; node = node->next) {
|
||||
sz += node->sz;
|
||||
}
|
||||
used = snprintf(nextra, remaining, " ql_uncompressed_size:%lu", sz);
|
||||
nextra += used;
|
||||
remaining -= used;
|
||||
}
|
||||
|
||||
addReplyStatusFormat(c,
|
||||
"Value at:%p refcount:%d "
|
||||
"encoding:%s serializedlength:%lld "
|
||||
"lru:%d lru_seconds_idle:%llu",
|
||||
"lru:%d lru_seconds_idle:%llu%s",
|
||||
(void*)val, val->refcount,
|
||||
strenc, (long long) rdbSavedObjectLen(val),
|
||||
val->lru, estimateObjectIdleTime(val)/1000);
|
||||
val->lru, estimateObjectIdleTime(val)/1000, extra);
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"sdslen") && c->argc == 3) {
|
||||
dictEntry *de;
|
||||
robj *val;
|
||||
|
||||
+41
-15
@@ -49,7 +49,7 @@
|
||||
* the difference between 15 and 14 is very small
|
||||
* for small blocks (and 14 is usually a bit faster).
|
||||
* For a low-memory/faster configuration, use HLOG == 13;
|
||||
* For best compression, use 15 or 16 (or more, up to 23).
|
||||
* For best compression, use 15 or 16 (or more, up to 22).
|
||||
*/
|
||||
#ifndef HLOG
|
||||
# define HLOG 16
|
||||
@@ -94,7 +94,7 @@
|
||||
/*
|
||||
* Avoid assigning values to errno variable? for some embedding purposes
|
||||
* (linux kernel for example), this is necessary. NOTE: this breaks
|
||||
* the documentation in lzf.h.
|
||||
* the documentation in lzf.h. Avoiding errno has no speed impact.
|
||||
*/
|
||||
#ifndef AVOID_ERRNO
|
||||
# define AVOID_ERRNO 0
|
||||
@@ -121,16 +121,52 @@
|
||||
# define CHECK_INPUT 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Whether to store pointers or offsets inside the hash table. On
|
||||
* 64 bit architetcures, pointers take up twice as much space,
|
||||
* and might also be slower. Default is to autodetect.
|
||||
*/
|
||||
/*#define LZF_USER_OFFSETS autodetect */
|
||||
|
||||
/*****************************************************************************/
|
||||
/* nothing should be changed below */
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include <cstring>
|
||||
# include <climits>
|
||||
using namespace std;
|
||||
#else
|
||||
# include <string.h>
|
||||
# include <limits.h>
|
||||
#endif
|
||||
|
||||
#ifndef LZF_USE_OFFSETS
|
||||
# if defined (WIN32)
|
||||
# define LZF_USE_OFFSETS defined(_M_X64)
|
||||
# else
|
||||
# if __cplusplus > 199711L
|
||||
# include <cstdint>
|
||||
# else
|
||||
# include <stdint.h>
|
||||
# endif
|
||||
# define LZF_USE_OFFSETS (UINTPTR_MAX > 0xffffffffU)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
typedef unsigned char u8;
|
||||
|
||||
typedef const u8 *LZF_STATE[1 << (HLOG)];
|
||||
#if LZF_USE_OFFSETS
|
||||
# define LZF_HSLOT_BIAS ((const u8 *)in_data)
|
||||
typedef unsigned int LZF_HSLOT;
|
||||
#else
|
||||
# define LZF_HSLOT_BIAS 0
|
||||
typedef const u8 *LZF_HSLOT;
|
||||
#endif
|
||||
|
||||
typedef LZF_HSLOT LZF_STATE[1 << (HLOG)];
|
||||
|
||||
#if !STRICT_ALIGN
|
||||
/* for unaligned accesses we need a 16 bit datatype. */
|
||||
# include <limits.h>
|
||||
# if USHRT_MAX == 65535
|
||||
typedef unsigned short u16;
|
||||
# elif UINT_MAX == 65535
|
||||
@@ -142,17 +178,7 @@ typedef const u8 *LZF_STATE[1 << (HLOG)];
|
||||
#endif
|
||||
|
||||
#if ULTRA_FAST
|
||||
# if defined(VERY_FAST)
|
||||
# undef VERY_FAST
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if INIT_HTAB
|
||||
# ifdef __cplusplus
|
||||
# include <cstring>
|
||||
# else
|
||||
# include <string.h>
|
||||
# endif
|
||||
# undef VERY_FAST
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+19
-23
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2008 Marc Alexander Lehmann <schmorp@schmorp.de>
|
||||
* Copyright (c) 2000-2010 Marc Alexander Lehmann <schmorp@schmorp.de>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modifica-
|
||||
* tion, are permitted provided that the following conditions are met:
|
||||
@@ -40,8 +40,8 @@
|
||||
|
||||
/*
|
||||
* don't play with this unless you benchmark!
|
||||
* decompression is not dependent on the hash function
|
||||
* the hashing function might seem strange, just believe me
|
||||
* the data format is not dependent on the hash function.
|
||||
* the hash function might seem strange, just believe me,
|
||||
* it works ;)
|
||||
*/
|
||||
#ifndef FRST
|
||||
@@ -89,9 +89,9 @@
|
||||
/*
|
||||
* compressed format
|
||||
*
|
||||
* 000LLLLL <L+1> ; literal
|
||||
* LLLooooo oooooooo ; backref L
|
||||
* 111ooooo LLLLLLLL oooooooo ; backref L+7
|
||||
* 000LLLLL <L+1> ; literal, L+1=1..33 octets
|
||||
* LLLooooo oooooooo ; backref L+1=1..7 octets, o+1=1..4096 offset
|
||||
* 111ooooo LLLLLLLL oooooooo ; backref L+8 octets, o+1=1..4096 offset
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -106,7 +106,6 @@ lzf_compress (const void *const in_data, unsigned int in_len,
|
||||
#if !LZF_STATE_ARG
|
||||
LZF_STATE htab;
|
||||
#endif
|
||||
const u8 **hslot;
|
||||
const u8 *ip = (const u8 *)in_data;
|
||||
u8 *op = (u8 *)out_data;
|
||||
const u8 *in_end = ip + in_len;
|
||||
@@ -133,10 +132,6 @@ lzf_compress (const void *const in_data, unsigned int in_len,
|
||||
|
||||
#if INIT_HTAB
|
||||
memset (htab, 0, sizeof (htab));
|
||||
# if 0
|
||||
for (hslot = htab; hslot < htab + HSIZE; hslot++)
|
||||
*hslot++ = ip;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
lit = 0; op++; /* start run */
|
||||
@@ -144,24 +139,23 @@ lzf_compress (const void *const in_data, unsigned int in_len,
|
||||
hval = FRST (ip);
|
||||
while (ip < in_end - 2)
|
||||
{
|
||||
LZF_HSLOT *hslot;
|
||||
|
||||
hval = NEXT (hval, ip);
|
||||
hslot = htab + IDX (hval);
|
||||
ref = *hslot; *hslot = ip;
|
||||
ref = *hslot + LZF_HSLOT_BIAS; *hslot = ip - LZF_HSLOT_BIAS;
|
||||
|
||||
if (1
|
||||
#if INIT_HTAB
|
||||
&& ref < ip /* the next test will actually take care of this, but this is faster */
|
||||
#endif
|
||||
&& (off = ip - ref - 1) < MAX_OFF
|
||||
&& ip + 4 < in_end
|
||||
&& ref > (u8 *)in_data
|
||||
#if STRICT_ALIGN
|
||||
&& ref[0] == ip[0]
|
||||
&& ref[1] == ip[1]
|
||||
&& ref[2] == ip[2]
|
||||
#if STRICT_ALIGN
|
||||
&& ((ref[1] << 8) | ref[0]) == ((ip[1] << 8) | ip[0])
|
||||
#else
|
||||
&& *(u16 *)ref == *(u16 *)ip
|
||||
&& ref[2] == ip[2]
|
||||
#endif
|
||||
)
|
||||
{
|
||||
@@ -170,12 +164,13 @@ lzf_compress (const void *const in_data, unsigned int in_len,
|
||||
unsigned int maxlen = in_end - ip - len;
|
||||
maxlen = maxlen > MAX_REF ? MAX_REF : maxlen;
|
||||
|
||||
if (expect_false (op + 3 + 1 >= out_end)) /* first a faster conservative test */
|
||||
if (op - !lit + 3 + 1 >= out_end) /* second the exact but rare test */
|
||||
return 0;
|
||||
|
||||
op [- lit - 1] = lit - 1; /* stop run */
|
||||
op -= !lit; /* undo run if length is zero */
|
||||
|
||||
if (expect_false (op + 3 + 1 >= out_end))
|
||||
return 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (expect_true (maxlen > 16))
|
||||
@@ -222,6 +217,7 @@ lzf_compress (const void *const in_data, unsigned int in_len,
|
||||
}
|
||||
|
||||
*op++ = off;
|
||||
|
||||
lit = 0; op++; /* start run */
|
||||
|
||||
ip += len + 1;
|
||||
@@ -237,12 +233,12 @@ lzf_compress (const void *const in_data, unsigned int in_len,
|
||||
hval = FRST (ip);
|
||||
|
||||
hval = NEXT (hval, ip);
|
||||
htab[IDX (hval)] = ip;
|
||||
htab[IDX (hval)] = ip - LZF_HSLOT_BIAS;
|
||||
ip++;
|
||||
|
||||
# if VERY_FAST && !ULTRA_FAST
|
||||
hval = NEXT (hval, ip);
|
||||
htab[IDX (hval)] = ip;
|
||||
htab[IDX (hval)] = ip - LZF_HSLOT_BIAS;
|
||||
ip++;
|
||||
# endif
|
||||
#else
|
||||
@@ -251,7 +247,7 @@ lzf_compress (const void *const in_data, unsigned int in_len,
|
||||
do
|
||||
{
|
||||
hval = NEXT (hval, ip);
|
||||
htab[IDX (hval)] = ip;
|
||||
htab[IDX (hval)] = ip - LZF_HSLOT_BIAS;
|
||||
ip++;
|
||||
}
|
||||
while (len--);
|
||||
|
||||
+46
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2007 Marc Alexander Lehmann <schmorp@schmorp.de>
|
||||
* Copyright (c) 2000-2010 Marc Alexander Lehmann <schmorp@schmorp.de>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modifica-
|
||||
* tion, are permitted provided that the following conditions are met:
|
||||
@@ -43,14 +43,14 @@
|
||||
# define SET_ERRNO(n) errno = (n)
|
||||
#endif
|
||||
|
||||
/*
|
||||
#if USE_REP_MOVSB /* small win on amd, big loss on intel */
|
||||
#if (__i386 || __amd64) && __GNUC__ >= 3
|
||||
# define lzf_movsb(dst, src, len) \
|
||||
asm ("rep movsb" \
|
||||
: "=D" (dst), "=S" (src), "=c" (len) \
|
||||
: "0" (dst), "1" (src), "2" (len));
|
||||
#endif
|
||||
*/
|
||||
#endif
|
||||
|
||||
unsigned int
|
||||
lzf_decompress (const void *const in_data, unsigned int in_len,
|
||||
@@ -86,9 +86,17 @@ lzf_decompress (const void *const in_data, unsigned int in_len,
|
||||
#ifdef lzf_movsb
|
||||
lzf_movsb (op, ip, ctrl);
|
||||
#else
|
||||
do
|
||||
*op++ = *ip++;
|
||||
while (--ctrl);
|
||||
switch (ctrl)
|
||||
{
|
||||
case 32: *op++ = *ip++; case 31: *op++ = *ip++; case 30: *op++ = *ip++; case 29: *op++ = *ip++;
|
||||
case 28: *op++ = *ip++; case 27: *op++ = *ip++; case 26: *op++ = *ip++; case 25: *op++ = *ip++;
|
||||
case 24: *op++ = *ip++; case 23: *op++ = *ip++; case 22: *op++ = *ip++; case 21: *op++ = *ip++;
|
||||
case 20: *op++ = *ip++; case 19: *op++ = *ip++; case 18: *op++ = *ip++; case 17: *op++ = *ip++;
|
||||
case 16: *op++ = *ip++; case 15: *op++ = *ip++; case 14: *op++ = *ip++; case 13: *op++ = *ip++;
|
||||
case 12: *op++ = *ip++; case 11: *op++ = *ip++; case 10: *op++ = *ip++; case 9: *op++ = *ip++;
|
||||
case 8: *op++ = *ip++; case 7: *op++ = *ip++; case 6: *op++ = *ip++; case 5: *op++ = *ip++;
|
||||
case 4: *op++ = *ip++; case 3: *op++ = *ip++; case 2: *op++ = *ip++; case 1: *op++ = *ip++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else /* back reference */
|
||||
@@ -134,12 +142,39 @@ lzf_decompress (const void *const in_data, unsigned int in_len,
|
||||
len += 2;
|
||||
lzf_movsb (op, ref, len);
|
||||
#else
|
||||
*op++ = *ref++;
|
||||
*op++ = *ref++;
|
||||
switch (len)
|
||||
{
|
||||
default:
|
||||
len += 2;
|
||||
|
||||
do
|
||||
*op++ = *ref++;
|
||||
while (--len);
|
||||
if (op >= ref + len)
|
||||
{
|
||||
/* disjunct areas */
|
||||
memcpy (op, ref, len);
|
||||
op += len;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* overlapping, use octte by octte copying */
|
||||
do
|
||||
*op++ = *ref++;
|
||||
while (--len);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 9: *op++ = *ref++;
|
||||
case 8: *op++ = *ref++;
|
||||
case 7: *op++ = *ref++;
|
||||
case 6: *op++ = *ref++;
|
||||
case 5: *op++ = *ref++;
|
||||
case 4: *op++ = *ref++;
|
||||
case 3: *op++ = *ref++;
|
||||
case 2: *op++ = *ref++;
|
||||
case 1: *op++ = *ref++;
|
||||
case 0: *op++ = *ref++; /* two octets more */
|
||||
*op++ = *ref++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
+6
-10
@@ -180,11 +180,10 @@ robj *dupStringObject(robj *o) {
|
||||
}
|
||||
}
|
||||
|
||||
robj *createListObject(void) {
|
||||
list *l = listCreate();
|
||||
robj *createQuicklistObject(void) {
|
||||
quicklist *l = quicklistCreate();
|
||||
robj *o = createObject(REDIS_LIST,l);
|
||||
listSetFreeMethod(l,decrRefCountVoid);
|
||||
o->encoding = REDIS_ENCODING_LINKEDLIST;
|
||||
o->encoding = REDIS_ENCODING_QUICKLIST;
|
||||
return o;
|
||||
}
|
||||
|
||||
@@ -242,11 +241,8 @@ void freeStringObject(robj *o) {
|
||||
|
||||
void freeListObject(robj *o) {
|
||||
switch (o->encoding) {
|
||||
case REDIS_ENCODING_LINKEDLIST:
|
||||
listRelease((list*) o->ptr);
|
||||
break;
|
||||
case REDIS_ENCODING_ZIPLIST:
|
||||
zfree(o->ptr);
|
||||
case REDIS_ENCODING_QUICKLIST:
|
||||
quicklistRelease(o->ptr);
|
||||
break;
|
||||
default:
|
||||
redisPanic("Unknown list encoding type");
|
||||
@@ -678,7 +674,7 @@ char *strEncoding(int encoding) {
|
||||
case REDIS_ENCODING_RAW: return "raw";
|
||||
case REDIS_ENCODING_INT: return "int";
|
||||
case REDIS_ENCODING_HT: return "hashtable";
|
||||
case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
|
||||
case REDIS_ENCODING_QUICKLIST: return "quicklist";
|
||||
case REDIS_ENCODING_ZIPLIST: return "ziplist";
|
||||
case REDIS_ENCODING_INTSET: return "intset";
|
||||
case REDIS_ENCODING_SKIPLIST: return "skiplist";
|
||||
|
||||
+2640
File diff suppressed because it is too large
Load Diff
+169
@@ -0,0 +1,169 @@
|
||||
/* quicklist.h - A generic doubly linked quicklist implementation
|
||||
*
|
||||
* Copyright (c) 2014, Matt Stancliff <matt@genges.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this quicklist of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this quicklist of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __QUICKLIST_H__
|
||||
#define __QUICKLIST_H__
|
||||
|
||||
/* Node, quicklist, and Iterator are the only data structures used currently. */
|
||||
|
||||
/* quicklistNode is a 32 byte struct describing a ziplist for a quicklist.
|
||||
* We use bit fields keep the quicklistNode at 32 bytes.
|
||||
* count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k).
|
||||
* encoding: 2 bits, RAW=1, LZF=2.
|
||||
* container: 2 bits, NONE=1, ZIPLIST=2.
|
||||
* recompress: 1 bit, bool, true if node is temporarry decompressed for usage.
|
||||
* attempted_compress: 1 bit, boolean, used for verifying during testing.
|
||||
* extra: 12 bits, free for future use; pads out the remainder of 32 bits */
|
||||
typedef struct quicklistNode {
|
||||
struct quicklistNode *prev;
|
||||
struct quicklistNode *next;
|
||||
unsigned char *zl;
|
||||
unsigned int sz; /* ziplist size in bytes */
|
||||
unsigned int count : 16; /* count of items in ziplist */
|
||||
unsigned int encoding : 2; /* RAW==1 or LZF==2 */
|
||||
unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */
|
||||
unsigned int recompress : 1; /* was this node previous compressed? */
|
||||
unsigned int attempted_compress : 1; /* node can't compress; too small */
|
||||
unsigned int extra : 10; /* more bits to steal for future usage */
|
||||
} quicklistNode;
|
||||
|
||||
/* quicklistLZF is a 4+N byte struct holding 'sz' followed by 'compressed'.
|
||||
* 'sz' is byte length of 'compressed' field.
|
||||
* 'compressed' is LZF data with total (compressed) length 'sz'
|
||||
* NOTE: uncompressed length is stored in quicklistNode->sz.
|
||||
* When quicklistNode->zl is compressed, node->zl points to a quicklistLZF */
|
||||
typedef struct quicklistLZF {
|
||||
unsigned int sz; /* LZF size in bytes*/
|
||||
char compressed[];
|
||||
} quicklistLZF;
|
||||
|
||||
/* quicklist is a 32 byte struct (on 64-bit systems) describing a quicklist.
|
||||
* 'count' is the number of total entries.
|
||||
* 'len' is the number of quicklist nodes.
|
||||
* 'compress' is: -1 if compression disabled, otherwise it's the number
|
||||
* of quicklistNodes to leave uncompressed at ends of quicklist.
|
||||
* 'fill' is the user-requested (or default) fill factor. */
|
||||
typedef struct quicklist {
|
||||
quicklistNode *head;
|
||||
quicklistNode *tail;
|
||||
unsigned long count; /* total count of all entries in all ziplists */
|
||||
unsigned int len; /* number of quicklistNodes */
|
||||
int fill : 16; /* fill factor for individual nodes */
|
||||
unsigned int compress : 16; /* depth of end nodes not to compress;0=off */
|
||||
} quicklist;
|
||||
|
||||
typedef struct quicklistIter {
|
||||
const quicklist *quicklist;
|
||||
quicklistNode *current;
|
||||
unsigned char *zi;
|
||||
long offset; /* offset in current ziplist */
|
||||
int direction;
|
||||
} quicklistIter;
|
||||
|
||||
typedef struct quicklistEntry {
|
||||
const quicklist *quicklist;
|
||||
quicklistNode *node;
|
||||
unsigned char *zi;
|
||||
unsigned char *value;
|
||||
unsigned int sz;
|
||||
long long longval;
|
||||
int offset;
|
||||
} quicklistEntry;
|
||||
|
||||
#define QUICKLIST_HEAD 0
|
||||
#define QUICKLIST_TAIL -1
|
||||
|
||||
/* quicklist node encodings */
|
||||
#define QUICKLIST_NODE_ENCODING_RAW 1
|
||||
#define QUICKLIST_NODE_ENCODING_LZF 2
|
||||
|
||||
/* quicklist compression disable */
|
||||
#define QUICKLIST_NOCOMPRESS 0
|
||||
|
||||
/* quicklist container formats */
|
||||
#define QUICKLIST_NODE_CONTAINER_NONE 1
|
||||
#define QUICKLIST_NODE_CONTAINER_ZIPLIST 2
|
||||
|
||||
#define quicklistNodeIsCompressed(node) \
|
||||
((node)->encoding == QUICKLIST_NODE_ENCODING_LZF)
|
||||
|
||||
/* Prototypes */
|
||||
quicklist *quicklistCreate(void);
|
||||
quicklist *quicklistNew(int fill, int compress);
|
||||
void quicklistSetCompressDepth(quicklist *quicklist, int depth);
|
||||
void quicklistSetFill(quicklist *quicklist, int fill);
|
||||
void quicklistSetOptions(quicklist *quicklist, int fill, int depth);
|
||||
void quicklistRelease(quicklist *quicklist);
|
||||
int quicklistPushHead(quicklist *quicklist, void *value, const size_t sz);
|
||||
int quicklistPushTail(quicklist *quicklist, void *value, const size_t sz);
|
||||
void quicklistPush(quicklist *quicklist, void *value, const size_t sz,
|
||||
int where);
|
||||
void quicklistAppendZiplist(quicklist *quicklist, unsigned char *zl);
|
||||
quicklist *quicklistAppendValuesFromZiplist(quicklist *quicklist,
|
||||
unsigned char *zl);
|
||||
quicklist *quicklistCreateFromZiplist(int fill, int compress,
|
||||
unsigned char *zl);
|
||||
void quicklistInsertAfter(quicklist *quicklist, quicklistEntry *node,
|
||||
void *value, const size_t sz);
|
||||
void quicklistInsertBefore(quicklist *quicklist, quicklistEntry *node,
|
||||
void *value, const size_t sz);
|
||||
void quicklistDelEntry(quicklistIter *iter, quicklistEntry *entry);
|
||||
int quicklistReplaceAtIndex(quicklist *quicklist, long index, void *data,
|
||||
int sz);
|
||||
int quicklistDelRange(quicklist *quicklist, const long start, const long stop);
|
||||
quicklistIter *quicklistGetIterator(const quicklist *quicklist, int direction);
|
||||
quicklistIter *quicklistGetIteratorAtIdx(const quicklist *quicklist,
|
||||
int direction, const long long idx);
|
||||
int quicklistNext(quicklistIter *iter, quicklistEntry *node);
|
||||
void quicklistReleaseIterator(quicklistIter *iter);
|
||||
quicklist *quicklistDup(quicklist *orig);
|
||||
int quicklistIndex(const quicklist *quicklist, const long long index,
|
||||
quicklistEntry *entry);
|
||||
void quicklistRewind(quicklist *quicklist, quicklistIter *li);
|
||||
void quicklistRewindTail(quicklist *quicklist, quicklistIter *li);
|
||||
void quicklistRotate(quicklist *quicklist);
|
||||
int quicklistPopCustom(quicklist *quicklist, int where, unsigned char **data,
|
||||
unsigned int *sz, long long *sval,
|
||||
void *(*saver)(unsigned char *data, unsigned int sz));
|
||||
int quicklistPop(quicklist *quicklist, int where, unsigned char **data,
|
||||
unsigned int *sz, long long *slong);
|
||||
unsigned int quicklistCount(quicklist *ql);
|
||||
int quicklistCompare(unsigned char *p1, unsigned char *p2, int p2_len);
|
||||
size_t quicklistGetLzf(const quicklistNode *node, void **data);
|
||||
|
||||
#ifdef REDIS_TEST
|
||||
int quicklistTest(int argc, char *argv[]);
|
||||
#endif
|
||||
|
||||
/* Directions for iterators */
|
||||
#define AL_START_HEAD 0
|
||||
#define AL_START_TAIL 1
|
||||
|
||||
#endif /* __QUICKLIST_H__ */
|
||||
@@ -40,6 +40,10 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define RDB_LOAD_NONE 0
|
||||
#define RDB_LOAD_ENC (1<<0)
|
||||
#define RDB_LOAD_PLAIN (1<<1)
|
||||
|
||||
static int rdbWriteRaw(rio *rdb, void *p, size_t len) {
|
||||
if (rdb && rioWrite(rdb,p,len) == 0)
|
||||
return -1;
|
||||
@@ -161,9 +165,11 @@ int rdbEncodeInteger(long long value, unsigned char *enc) {
|
||||
}
|
||||
|
||||
/* Loads an integer-encoded object with the specified encoding type "enctype".
|
||||
* If the "encode" argument is set the function may return an integer-encoded
|
||||
* string object, otherwise it always returns a raw string object. */
|
||||
robj *rdbLoadIntegerObject(rio *rdb, int enctype, int encode) {
|
||||
* The returned value changes according to the flags, see
|
||||
* rdbGenerincLoadStringObject() for more info. */
|
||||
void *rdbLoadIntegerObject(rio *rdb, int enctype, int flags) {
|
||||
int plain = flags & RDB_LOAD_PLAIN;
|
||||
int encode = flags & RDB_LOAD_ENC;
|
||||
unsigned char enc[4];
|
||||
long long val;
|
||||
|
||||
@@ -184,10 +190,17 @@ robj *rdbLoadIntegerObject(rio *rdb, int enctype, int encode) {
|
||||
val = 0; /* anti-warning */
|
||||
redisPanic("Unknown RDB integer encoding type");
|
||||
}
|
||||
if (encode)
|
||||
if (plain) {
|
||||
char buf[REDIS_LONGSTR_SIZE], *p;
|
||||
int len = ll2string(buf,sizeof(buf),val);
|
||||
p = zmalloc(len);
|
||||
memcpy(p,buf,len);
|
||||
return p;
|
||||
} else if (encode) {
|
||||
return createStringObjectFromLongLong(val);
|
||||
else
|
||||
} else {
|
||||
return createObject(REDIS_STRING,sdsfromlonglong(val));
|
||||
}
|
||||
}
|
||||
|
||||
/* String objects in the form "2391" "-100" without any space and with a
|
||||
@@ -209,10 +222,33 @@ int rdbTryIntegerEncoding(char *s, size_t len, unsigned char *enc) {
|
||||
return rdbEncodeInteger(value,enc);
|
||||
}
|
||||
|
||||
int rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {
|
||||
size_t comprlen, outlen;
|
||||
int rdbSaveLzfBlob(rio *rdb, void *data, size_t compress_len,
|
||||
size_t original_len) {
|
||||
unsigned char byte;
|
||||
int n, nwritten = 0;
|
||||
|
||||
/* Data compressed! Let's save it on disk */
|
||||
byte = (REDIS_RDB_ENCVAL<<6)|REDIS_RDB_ENC_LZF;
|
||||
if ((n = rdbWriteRaw(rdb,&byte,1)) == -1) goto writeerr;
|
||||
nwritten += n;
|
||||
|
||||
if ((n = rdbSaveLen(rdb,compress_len)) == -1) goto writeerr;
|
||||
nwritten += n;
|
||||
|
||||
if ((n = rdbSaveLen(rdb,original_len)) == -1) goto writeerr;
|
||||
nwritten += n;
|
||||
|
||||
if ((n = rdbWriteRaw(rdb,data,compress_len)) == -1) goto writeerr;
|
||||
nwritten += n;
|
||||
|
||||
return nwritten;
|
||||
|
||||
writeerr:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {
|
||||
size_t comprlen, outlen;
|
||||
void *out;
|
||||
|
||||
/* We require at least four bytes compression for this to be worth it */
|
||||
@@ -224,29 +260,16 @@ int rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {
|
||||
zfree(out);
|
||||
return 0;
|
||||
}
|
||||
/* Data compressed! Let's save it on disk */
|
||||
byte = (REDIS_RDB_ENCVAL<<6)|REDIS_RDB_ENC_LZF;
|
||||
if ((n = rdbWriteRaw(rdb,&byte,1)) == -1) goto writeerr;
|
||||
nwritten += n;
|
||||
|
||||
if ((n = rdbSaveLen(rdb,comprlen)) == -1) goto writeerr;
|
||||
nwritten += n;
|
||||
|
||||
if ((n = rdbSaveLen(rdb,len)) == -1) goto writeerr;
|
||||
nwritten += n;
|
||||
|
||||
if ((n = rdbWriteRaw(rdb,out,comprlen)) == -1) goto writeerr;
|
||||
nwritten += n;
|
||||
|
||||
size_t nwritten = rdbSaveLzfBlob(rdb, out, comprlen, len);
|
||||
zfree(out);
|
||||
return nwritten;
|
||||
|
||||
writeerr:
|
||||
zfree(out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
robj *rdbLoadLzfStringObject(rio *rdb) {
|
||||
/* Load an LZF compressed string in RDB format. The returned value
|
||||
* changes according to 'flags'. For more info check the
|
||||
* rdbGenericLoadStringObject() function. */
|
||||
void *rdbLoadLzfStringObject(rio *rdb, int flags) {
|
||||
int plain = flags & RDB_LOAD_PLAIN;
|
||||
unsigned int len, clen;
|
||||
unsigned char *c = NULL;
|
||||
sds val = NULL;
|
||||
@@ -254,14 +277,29 @@ robj *rdbLoadLzfStringObject(rio *rdb) {
|
||||
if ((clen = rdbLoadLen(rdb,NULL)) == REDIS_RDB_LENERR) return NULL;
|
||||
if ((len = rdbLoadLen(rdb,NULL)) == REDIS_RDB_LENERR) return NULL;
|
||||
if ((c = zmalloc(clen)) == NULL) goto err;
|
||||
if ((val = sdsnewlen(NULL,len)) == NULL) goto err;
|
||||
|
||||
/* Allocate our target according to the uncompressed size. */
|
||||
if (plain) {
|
||||
val = zmalloc(len);
|
||||
} else {
|
||||
if ((val = sdsnewlen(NULL,len)) == NULL) goto err;
|
||||
}
|
||||
|
||||
/* Load the compressed representation and uncompress it to target. */
|
||||
if (rioRead(rdb,c,clen) == 0) goto err;
|
||||
if (lzf_decompress(c,clen,val,len) == 0) goto err;
|
||||
zfree(c);
|
||||
return createObject(REDIS_STRING,val);
|
||||
|
||||
if (plain)
|
||||
return val;
|
||||
else
|
||||
return createObject(REDIS_STRING,val);
|
||||
err:
|
||||
zfree(c);
|
||||
sdsfree(val);
|
||||
if (plain)
|
||||
zfree(val);
|
||||
else
|
||||
sdsfree(val);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -330,10 +368,21 @@ int rdbSaveStringObject(rio *rdb, robj *obj) {
|
||||
}
|
||||
}
|
||||
|
||||
robj *rdbGenericLoadStringObject(rio *rdb, int encode) {
|
||||
/* Load a string object from an RDB file according to flags:
|
||||
*
|
||||
* RDB_LOAD_NONE (no flags): load an RDB object, unencoded.
|
||||
* RDB_LOAD_ENC: If the returned type is a Redis object, try to
|
||||
* encode it in a special way to be more memory
|
||||
* efficient. When this flag is passed the function
|
||||
* no longer guarantees that obj->ptr is an SDS string.
|
||||
* RDB_LOAD_PLAIN: Return a plain string allocated with zmalloc()
|
||||
* instead of a Redis object.
|
||||
*/
|
||||
void *rdbGenericLoadStringObject(rio *rdb, int flags) {
|
||||
int encode = flags & RDB_LOAD_ENC;
|
||||
int plain = flags & RDB_LOAD_PLAIN;
|
||||
int isencoded;
|
||||
uint32_t len;
|
||||
robj *o;
|
||||
|
||||
len = rdbLoadLen(rdb,&isencoded);
|
||||
if (isencoded) {
|
||||
@@ -341,30 +390,39 @@ robj *rdbGenericLoadStringObject(rio *rdb, int encode) {
|
||||
case REDIS_RDB_ENC_INT8:
|
||||
case REDIS_RDB_ENC_INT16:
|
||||
case REDIS_RDB_ENC_INT32:
|
||||
return rdbLoadIntegerObject(rdb,len,encode);
|
||||
return rdbLoadIntegerObject(rdb,len,flags);
|
||||
case REDIS_RDB_ENC_LZF:
|
||||
return rdbLoadLzfStringObject(rdb);
|
||||
return rdbLoadLzfStringObject(rdb,flags);
|
||||
default:
|
||||
redisPanic("Unknown RDB encoding type");
|
||||
}
|
||||
}
|
||||
|
||||
if (len == REDIS_RDB_LENERR) return NULL;
|
||||
o = encode ? createStringObject(NULL,len) :
|
||||
createRawStringObject(NULL,len);
|
||||
if (len && rioRead(rdb,o->ptr,len) == 0) {
|
||||
decrRefCount(o);
|
||||
return NULL;
|
||||
if (!plain) {
|
||||
robj *o = encode ? createStringObject(NULL,len) :
|
||||
createRawStringObject(NULL,len);
|
||||
if (len && rioRead(rdb,o->ptr,len) == 0) {
|
||||
decrRefCount(o);
|
||||
return NULL;
|
||||
}
|
||||
return o;
|
||||
} else {
|
||||
void *buf = zmalloc(len);
|
||||
if (len && rioRead(rdb,buf,len) == 0) {
|
||||
zfree(buf);
|
||||
return NULL;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
robj *rdbLoadStringObject(rio *rdb) {
|
||||
return rdbGenericLoadStringObject(rdb,0);
|
||||
return rdbGenericLoadStringObject(rdb,RDB_LOAD_NONE);
|
||||
}
|
||||
|
||||
robj *rdbLoadEncodedStringObject(rio *rdb) {
|
||||
return rdbGenericLoadStringObject(rdb,1);
|
||||
return rdbGenericLoadStringObject(rdb,RDB_LOAD_ENC);
|
||||
}
|
||||
|
||||
/* Save a double value. Doubles are saved as strings prefixed by an unsigned
|
||||
@@ -433,10 +491,8 @@ int rdbSaveObjectType(rio *rdb, robj *o) {
|
||||
case REDIS_STRING:
|
||||
return rdbSaveType(rdb,REDIS_RDB_TYPE_STRING);
|
||||
case REDIS_LIST:
|
||||
if (o->encoding == REDIS_ENCODING_ZIPLIST)
|
||||
return rdbSaveType(rdb,REDIS_RDB_TYPE_LIST_ZIPLIST);
|
||||
else if (o->encoding == REDIS_ENCODING_LINKEDLIST)
|
||||
return rdbSaveType(rdb,REDIS_RDB_TYPE_LIST);
|
||||
if (o->encoding == REDIS_ENCODING_QUICKLIST)
|
||||
return rdbSaveType(rdb,REDIS_RDB_TYPE_LIST_QUICKLIST);
|
||||
else
|
||||
redisPanic("Unknown list encoding");
|
||||
case REDIS_SET:
|
||||
@@ -477,7 +533,7 @@ int rdbLoadObjectType(rio *rdb) {
|
||||
|
||||
/* Save a Redis object. Returns -1 on error, number of bytes written on success. */
|
||||
int rdbSaveObject(rio *rdb, robj *o) {
|
||||
int n, nwritten = 0;
|
||||
int n = 0, nwritten = 0;
|
||||
|
||||
if (o->type == REDIS_STRING) {
|
||||
/* Save a string value */
|
||||
@@ -485,25 +541,24 @@ int rdbSaveObject(rio *rdb, robj *o) {
|
||||
nwritten += n;
|
||||
} else if (o->type == REDIS_LIST) {
|
||||
/* Save a list value */
|
||||
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
|
||||
size_t l = ziplistBlobLen((unsigned char*)o->ptr);
|
||||
if (o->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
quicklist *ql = o->ptr;
|
||||
quicklistNode *node = ql->head;
|
||||
|
||||
if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1;
|
||||
nwritten += n;
|
||||
} else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {
|
||||
list *list = o->ptr;
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
|
||||
if ((n = rdbSaveLen(rdb,listLength(list))) == -1) return -1;
|
||||
if ((n = rdbSaveLen(rdb,ql->len)) == -1) return -1;
|
||||
nwritten += n;
|
||||
|
||||
listRewind(list,&li);
|
||||
while((ln = listNext(&li))) {
|
||||
robj *eleobj = listNodeValue(ln);
|
||||
if ((n = rdbSaveStringObject(rdb,eleobj)) == -1) return -1;
|
||||
nwritten += n;
|
||||
}
|
||||
do {
|
||||
if (quicklistNodeIsCompressed(node)) {
|
||||
void *data;
|
||||
size_t compress_len = quicklistGetLzf(node, &data);
|
||||
if ((n = rdbSaveLzfBlob(rdb,data,compress_len,node->sz)) == -1) return -1;
|
||||
nwritten += n;
|
||||
} else {
|
||||
if ((n = rdbSaveRawString(rdb,node->zl,node->sz)) == -1) return -1;
|
||||
nwritten += n;
|
||||
}
|
||||
} while ((node = node->next));
|
||||
} else {
|
||||
redisPanic("Unknown list encoding");
|
||||
}
|
||||
@@ -627,6 +682,39 @@ int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Save an AUX field. */
|
||||
int rdbSaveAuxField(rio *rdb, void *key, size_t keylen, void *val, size_t vallen) {
|
||||
if (rdbSaveType(rdb,REDIS_RDB_OPCODE_AUX) == -1) return -1;
|
||||
if (rdbSaveRawString(rdb,key,keylen) == -1) return -1;
|
||||
if (rdbSaveRawString(rdb,val,vallen) == -1) return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Wrapper for rdbSaveAuxField() used when key/val length can be obtained
|
||||
* with strlen(). */
|
||||
int rdbSaveAuxFieldStrStr(rio *rdb, char *key, char *val) {
|
||||
return rdbSaveAuxField(rdb,key,strlen(key),val,strlen(val));
|
||||
}
|
||||
|
||||
/* Wrapper for strlen(key) + integer type (up to long long range). */
|
||||
int rdbSaveAuxFieldStrInt(rio *rdb, char *key, long long val) {
|
||||
char buf[REDIS_LONGSTR_SIZE];
|
||||
int vlen = ll2string(buf,sizeof(buf),val);
|
||||
return rdbSaveAuxField(rdb,key,strlen(key),buf,vlen);
|
||||
}
|
||||
|
||||
/* Save a few default AUX fields with information about the RDB generated. */
|
||||
int rdbSaveInfoAuxFields(rio *rdb) {
|
||||
int redis_bits = (sizeof(void*) == 8) ? 64 : 32;
|
||||
|
||||
/* Add a few fiels about the state when the RDB was created. */
|
||||
if (rdbSaveAuxFieldStrStr(rdb,"redis-ver",REDIS_VERSION) == -1) return -1;
|
||||
if (rdbSaveAuxFieldStrInt(rdb,"redis-bits",redis_bits) == -1) return -1;
|
||||
if (rdbSaveAuxFieldStrInt(rdb,"ctime",time(NULL)) == -1) return -1;
|
||||
if (rdbSaveAuxFieldStrInt(rdb,"used-mem",zmalloc_used_memory()) == -1) return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Produces a dump of the database in RDB format sending it to the specified
|
||||
* Redis I/O channel. On success REDIS_OK is returned, otherwise REDIS_ERR
|
||||
* is returned and part of the output, or all the output, can be
|
||||
@@ -647,6 +735,7 @@ int rdbSaveRio(rio *rdb, int *error) {
|
||||
rdb->update_cksum = rioGenericUpdateChecksum;
|
||||
snprintf(magic,sizeof(magic),"REDIS%04d",REDIS_RDB_VERSION);
|
||||
if (rdbWriteRaw(rdb,magic,9) == -1) goto werr;
|
||||
if (rdbSaveInfoAuxFields(rdb) == -1) goto werr;
|
||||
|
||||
for (j = 0; j < server.dbnum; j++) {
|
||||
redisDb *db = server.db+j;
|
||||
@@ -659,6 +748,21 @@ int rdbSaveRio(rio *rdb, int *error) {
|
||||
if (rdbSaveType(rdb,REDIS_RDB_OPCODE_SELECTDB) == -1) goto werr;
|
||||
if (rdbSaveLen(rdb,j) == -1) goto werr;
|
||||
|
||||
/* Write the RESIZE DB opcode. We trim the size to UINT32_MAX, which
|
||||
* is currently the largest type we are able to represent in RDB sizes.
|
||||
* However this does not limit the actual size of the DB to load since
|
||||
* these sizes are just hints to resize the hash tables. */
|
||||
uint32_t db_size, expires_size;
|
||||
db_size = (dictSize(db->dict) <= UINT32_MAX) ?
|
||||
dictSize(db->dict) :
|
||||
UINT32_MAX;
|
||||
expires_size = (dictSize(db->dict) <= UINT32_MAX) ?
|
||||
dictSize(db->expires) :
|
||||
UINT32_MAX;
|
||||
if (rdbSaveType(rdb,REDIS_RDB_OPCODE_RESIZEDB) == -1) goto werr;
|
||||
if (rdbSaveLen(rdb,db_size) == -1) goto werr;
|
||||
if (rdbSaveLen(rdb,expires_size) == -1) goto werr;
|
||||
|
||||
/* Iterate this DB writing every entry */
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
sds keystr = dictGetKey(de);
|
||||
@@ -831,33 +935,18 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
|
||||
/* Read list value */
|
||||
if ((len = rdbLoadLen(rdb,NULL)) == REDIS_RDB_LENERR) return NULL;
|
||||
|
||||
/* Use a real list when there are too many entries */
|
||||
if (len > server.list_max_ziplist_entries) {
|
||||
o = createListObject();
|
||||
} else {
|
||||
o = createZiplistObject();
|
||||
}
|
||||
o = createQuicklistObject();
|
||||
quicklistSetOptions(o->ptr, server.list_max_ziplist_size,
|
||||
server.list_compress_depth);
|
||||
|
||||
/* Load every single element of the list */
|
||||
while(len--) {
|
||||
if ((ele = rdbLoadEncodedStringObject(rdb)) == 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 &&
|
||||
sdsEncodedObject(ele) &&
|
||||
sdslen(ele->ptr) > server.list_max_ziplist_value)
|
||||
listTypeConvert(o,REDIS_ENCODING_LINKEDLIST);
|
||||
|
||||
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);
|
||||
}
|
||||
dec = getDecodedObject(ele);
|
||||
size_t len = sdslen(dec->ptr);
|
||||
quicklistPushTail(o->ptr, dec->ptr, len);
|
||||
decrRefCount(dec);
|
||||
decrRefCount(ele);
|
||||
}
|
||||
} else if (rdbtype == REDIS_RDB_TYPE_SET) {
|
||||
/* Read list/set value */
|
||||
@@ -994,20 +1083,26 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
|
||||
|
||||
/* All pairs should be read by now */
|
||||
redisAssert(len == 0);
|
||||
} else if (rdbtype == REDIS_RDB_TYPE_LIST_QUICKLIST) {
|
||||
if ((len = rdbLoadLen(rdb,NULL)) == REDIS_RDB_LENERR) return NULL;
|
||||
o = createQuicklistObject();
|
||||
quicklistSetOptions(o->ptr, server.list_max_ziplist_size,
|
||||
server.list_compress_depth);
|
||||
|
||||
while (len--) {
|
||||
unsigned char *zl = rdbGenericLoadStringObject(rdb,RDB_LOAD_PLAIN);
|
||||
if (zl == NULL) return NULL;
|
||||
quicklistAppendZiplist(o->ptr, zl);
|
||||
}
|
||||
} else if (rdbtype == REDIS_RDB_TYPE_HASH_ZIPMAP ||
|
||||
rdbtype == REDIS_RDB_TYPE_LIST_ZIPLIST ||
|
||||
rdbtype == REDIS_RDB_TYPE_SET_INTSET ||
|
||||
rdbtype == REDIS_RDB_TYPE_ZSET_ZIPLIST ||
|
||||
rdbtype == REDIS_RDB_TYPE_HASH_ZIPLIST)
|
||||
{
|
||||
robj *aux = rdbLoadStringObject(rdb);
|
||||
|
||||
if (aux == NULL) return NULL;
|
||||
o = createObject(REDIS_STRING,NULL); /* string is just placeholder */
|
||||
o->ptr = zmalloc(sdslen(aux->ptr));
|
||||
memcpy(o->ptr,aux->ptr,sdslen(aux->ptr));
|
||||
decrRefCount(aux);
|
||||
unsigned char *encoded = rdbGenericLoadStringObject(rdb,RDB_LOAD_PLAIN);
|
||||
if (encoded == NULL) return NULL;
|
||||
o = createObject(REDIS_STRING,encoded); /* Obj type fixed below. */
|
||||
|
||||
/* Fix the object encoding, and make sure to convert the encoded
|
||||
* data type into the base type if accordingly to the current
|
||||
@@ -1048,8 +1143,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
|
||||
case REDIS_RDB_TYPE_LIST_ZIPLIST:
|
||||
o->type = REDIS_LIST;
|
||||
o->encoding = REDIS_ENCODING_ZIPLIST;
|
||||
if (ziplistLen(o->ptr) > server.list_max_ziplist_entries)
|
||||
listTypeConvert(o,REDIS_ENCODING_LINKEDLIST);
|
||||
listTypeConvert(o,REDIS_ENCODING_QUICKLIST);
|
||||
break;
|
||||
case REDIS_RDB_TYPE_SET_INTSET:
|
||||
o->type = REDIS_SET;
|
||||
@@ -1163,7 +1257,12 @@ int rdbLoad(char *filename) {
|
||||
|
||||
/* Read type. */
|
||||
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
|
||||
|
||||
/* Handle special types. */
|
||||
if (type == REDIS_RDB_OPCODE_EXPIRETIME) {
|
||||
/* EXPIRETIME: load an expire associated with the next key
|
||||
* to load. Note that after loading an expire we need to
|
||||
* load the actual type, and continue. */
|
||||
if ((expiretime = rdbLoadTime(&rdb)) == -1) goto eoferr;
|
||||
/* We read the time so we need to read the object type again. */
|
||||
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
|
||||
@@ -1171,27 +1270,65 @@ int rdbLoad(char *filename) {
|
||||
* into milliseconds. */
|
||||
expiretime *= 1000;
|
||||
} else if (type == REDIS_RDB_OPCODE_EXPIRETIME_MS) {
|
||||
/* Milliseconds precision expire times introduced with RDB
|
||||
* version 3. */
|
||||
/* EXPIRETIME_MS: milliseconds precision expire times introduced
|
||||
* with RDB v3. Like EXPIRETIME but no with more precision. */
|
||||
if ((expiretime = rdbLoadMillisecondTime(&rdb)) == -1) goto eoferr;
|
||||
/* We read the time so we need to read the object type again. */
|
||||
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
|
||||
}
|
||||
|
||||
if (type == REDIS_RDB_OPCODE_EOF)
|
||||
} else if (type == REDIS_RDB_OPCODE_EOF) {
|
||||
/* EOF: End of file, exit the main loop. */
|
||||
break;
|
||||
|
||||
/* Handle SELECT DB opcode as a special case */
|
||||
if (type == REDIS_RDB_OPCODE_SELECTDB) {
|
||||
} else if (type == REDIS_RDB_OPCODE_SELECTDB) {
|
||||
/* SELECTDB: Select the specified database. */
|
||||
if ((dbid = rdbLoadLen(&rdb,NULL)) == REDIS_RDB_LENERR)
|
||||
goto eoferr;
|
||||
if (dbid >= (unsigned)server.dbnum) {
|
||||
redisLog(REDIS_WARNING,"FATAL: Data file was created with a Redis server configured to handle more than %d databases. Exiting\n", server.dbnum);
|
||||
redisLog(REDIS_WARNING,
|
||||
"FATAL: Data file was created with a Redis "
|
||||
"server configured to handle more than %d "
|
||||
"databases. Exiting\n", server.dbnum);
|
||||
exit(1);
|
||||
}
|
||||
db = server.db+dbid;
|
||||
continue;
|
||||
continue; /* Read type again. */
|
||||
} else if (type == REDIS_RDB_OPCODE_RESIZEDB) {
|
||||
/* RESIZEDB: Hint about the size of the keys in the currently
|
||||
* selected data base, in order to avoid useless rehashing. */
|
||||
uint32_t db_size, expires_size;
|
||||
if ((db_size = rdbLoadLen(&rdb,NULL)) == REDIS_RDB_LENERR)
|
||||
goto eoferr;
|
||||
if ((expires_size = rdbLoadLen(&rdb,NULL)) == REDIS_RDB_LENERR)
|
||||
goto eoferr;
|
||||
dictExpand(db->dict,db_size);
|
||||
dictExpand(db->expires,expires_size);
|
||||
continue; /* Read type again. */
|
||||
} else if (type == REDIS_RDB_OPCODE_AUX) {
|
||||
/* AUX: generic string-string fields. Use to add state to RDB
|
||||
* which is backward compatible. Implementations of RDB loading
|
||||
* are requierd to skip AUX fields they don't understand.
|
||||
*
|
||||
* An AUX field is composed of two strings: key and value. */
|
||||
robj *auxkey, *auxval;
|
||||
if ((auxkey = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
|
||||
if ((auxval = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
|
||||
|
||||
if (((char*)auxkey->ptr)[0] == '%') {
|
||||
/* All the fields with a name staring with '%' are considered
|
||||
* information fields and are logged at startup with a log
|
||||
* level of NOTICE. */
|
||||
redisLog(REDIS_NOTICE,"RDB '%s': %s", auxkey->ptr, auxval->ptr);
|
||||
} else {
|
||||
/* We ignore fields we don't understand, as by AUX field
|
||||
* contract. */
|
||||
redisLog(REDIS_DEBUG,"Unrecognized RDB AUX field: '%s'",
|
||||
auxkey->ptr);
|
||||
}
|
||||
|
||||
zfree(auxkey);
|
||||
zfree(auxval);
|
||||
continue; /* Read type again. */
|
||||
}
|
||||
|
||||
/* Read key */
|
||||
if ((key = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
|
||||
/* Read value */
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
/* The current RDB version. When the format changes in a way that is no longer
|
||||
* backward compatible this number gets incremented. */
|
||||
#define REDIS_RDB_VERSION 6
|
||||
#define REDIS_RDB_VERSION 7
|
||||
|
||||
/* Defines related to the dump file format. To store 32 bits lengths for short
|
||||
* keys requires a lot of space, so we check the most significant 2 bits of
|
||||
@@ -74,6 +74,7 @@
|
||||
#define REDIS_RDB_TYPE_SET 2
|
||||
#define REDIS_RDB_TYPE_ZSET 3
|
||||
#define REDIS_RDB_TYPE_HASH 4
|
||||
/* NOTE: WHEN ADDING NEW RDB TYPE, UPDATE rdbIsObjectType() BELOW */
|
||||
|
||||
/* Object types for encoded objects. */
|
||||
#define REDIS_RDB_TYPE_HASH_ZIPMAP 9
|
||||
@@ -81,11 +82,15 @@
|
||||
#define REDIS_RDB_TYPE_SET_INTSET 11
|
||||
#define REDIS_RDB_TYPE_ZSET_ZIPLIST 12
|
||||
#define REDIS_RDB_TYPE_HASH_ZIPLIST 13
|
||||
#define REDIS_RDB_TYPE_LIST_QUICKLIST 14
|
||||
/* NOTE: WHEN ADDING NEW RDB TYPE, UPDATE rdbIsObjectType() BELOW */
|
||||
|
||||
/* Test if a type is an object type. */
|
||||
#define rdbIsObjectType(t) ((t >= 0 && t <= 4) || (t >= 9 && t <= 13))
|
||||
#define rdbIsObjectType(t) ((t >= 0 && t <= 4) || (t >= 9 && t <= 14))
|
||||
|
||||
/* Special RDB opcodes (saved/loaded with rdbSaveType/rdbLoadType). */
|
||||
#define REDIS_RDB_OPCODE_AUX 250
|
||||
#define REDIS_RDB_OPCODE_RESIZEDB 251
|
||||
#define REDIS_RDB_OPCODE_EXPIRETIME_MS 252
|
||||
#define REDIS_RDB_OPCODE_EXPIRETIME 253
|
||||
#define REDIS_RDB_OPCODE_SELECTDB 254
|
||||
|
||||
@@ -738,12 +738,24 @@ int main(int argc, const char **argv) {
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
if (test_is_selected("rpush")) {
|
||||
len = redisFormatCommand(&cmd,"RPUSH mylist %s",data);
|
||||
benchmark("RPUSH",cmd,len);
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
if (test_is_selected("lpop")) {
|
||||
len = redisFormatCommand(&cmd,"LPOP mylist");
|
||||
benchmark("LPOP",cmd,len);
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
if (test_is_selected("rpop")) {
|
||||
len = redisFormatCommand(&cmd,"RPOP mylist");
|
||||
benchmark("RPOP",cmd,len);
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
if (test_is_selected("sadd")) {
|
||||
len = redisFormatCommand(&cmd,
|
||||
"SADD myset element:__rand_int__");
|
||||
|
||||
+4
-2
@@ -1449,8 +1449,8 @@ void initServerConfig(void) {
|
||||
server.maxmemory_samples = REDIS_DEFAULT_MAXMEMORY_SAMPLES;
|
||||
server.hash_max_ziplist_entries = REDIS_HASH_MAX_ZIPLIST_ENTRIES;
|
||||
server.hash_max_ziplist_value = REDIS_HASH_MAX_ZIPLIST_VALUE;
|
||||
server.list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES;
|
||||
server.list_max_ziplist_value = REDIS_LIST_MAX_ZIPLIST_VALUE;
|
||||
server.list_max_ziplist_size = REDIS_LIST_MAX_ZIPLIST_SIZE;
|
||||
server.list_compress_depth = REDIS_LIST_COMPRESS_DEPTH;
|
||||
server.set_max_intset_entries = REDIS_SET_MAX_INTSET_ENTRIES;
|
||||
server.zset_max_ziplist_entries = REDIS_ZSET_MAX_ZIPLIST_ENTRIES;
|
||||
server.zset_max_ziplist_value = REDIS_ZSET_MAX_ZIPLIST_VALUE;
|
||||
@@ -3659,6 +3659,8 @@ int main(int argc, char **argv) {
|
||||
if (argc == 3 && !strcasecmp(argv[1], "test")) {
|
||||
if (!strcasecmp(argv[2], "ziplist")) {
|
||||
return ziplistTest(argc, argv);
|
||||
} else if (!strcasecmp(argv[2], "quicklist")) {
|
||||
quicklistTest(argc, argv);
|
||||
} else if (!strcasecmp(argv[2], "intset")) {
|
||||
return intsetTest(argc, argv);
|
||||
} else if (!strcasecmp(argv[2], "zipmap")) {
|
||||
|
||||
+14
-10
@@ -65,6 +65,7 @@ typedef long long mstime_t; /* millisecond time type. */
|
||||
#include "util.h" /* Misc functions useful in many places */
|
||||
#include "latency.h" /* Latency monitor API */
|
||||
#include "sparkline.h" /* ASII graphs API */
|
||||
#include "quicklist.h"
|
||||
|
||||
/* Following includes allow test functions to be called from Redis main() */
|
||||
#include "zipmap.h"
|
||||
@@ -204,6 +205,7 @@ typedef long long mstime_t; /* millisecond time type. */
|
||||
#define REDIS_ENCODING_INTSET 6 /* Encoded as intset */
|
||||
#define REDIS_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
|
||||
#define REDIS_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
|
||||
#define REDIS_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
|
||||
|
||||
/* Defines related to the dump file format. To store 32 bits lengths for short
|
||||
* keys requires a lot of space, so we check the most significant 2 bits of
|
||||
@@ -329,12 +331,14 @@ typedef long long mstime_t; /* millisecond time type. */
|
||||
/* Zip structure related defaults */
|
||||
#define REDIS_HASH_MAX_ZIPLIST_ENTRIES 512
|
||||
#define REDIS_HASH_MAX_ZIPLIST_VALUE 64
|
||||
#define REDIS_LIST_MAX_ZIPLIST_ENTRIES 512
|
||||
#define REDIS_LIST_MAX_ZIPLIST_VALUE 64
|
||||
#define REDIS_SET_MAX_INTSET_ENTRIES 512
|
||||
#define REDIS_ZSET_MAX_ZIPLIST_ENTRIES 128
|
||||
#define REDIS_ZSET_MAX_ZIPLIST_VALUE 64
|
||||
|
||||
/* List defaults */
|
||||
#define REDIS_LIST_MAX_ZIPLIST_SIZE -2
|
||||
#define REDIS_LIST_COMPRESS_DEPTH 0
|
||||
|
||||
/* HyperLogLog defines */
|
||||
#define REDIS_DEFAULT_HLL_SPARSE_MAX_BYTES 3000
|
||||
|
||||
@@ -869,12 +873,14 @@ struct redisServer {
|
||||
/* Zip structure config, see redis.conf for more information */
|
||||
size_t hash_max_ziplist_entries;
|
||||
size_t hash_max_ziplist_value;
|
||||
size_t list_max_ziplist_entries;
|
||||
size_t list_max_ziplist_value;
|
||||
size_t set_max_intset_entries;
|
||||
size_t zset_max_ziplist_entries;
|
||||
size_t zset_max_ziplist_value;
|
||||
size_t hll_sparse_max_bytes;
|
||||
/* List parameters */
|
||||
int list_max_ziplist_size;
|
||||
int list_compress_depth;
|
||||
/* time cache */
|
||||
time_t unixtime; /* Unix time sampled every cron cycle. */
|
||||
long long mstime; /* Like 'unixtime' but with milliseconds resolution. */
|
||||
/* Pubsub */
|
||||
@@ -964,15 +970,13 @@ typedef struct {
|
||||
robj *subject;
|
||||
unsigned char encoding;
|
||||
unsigned char direction; /* Iteration direction */
|
||||
unsigned char *zi;
|
||||
listNode *ln;
|
||||
quicklistIter *iter;
|
||||
} 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 */
|
||||
quicklistEntry entry; /* Entry in quicklist */
|
||||
} listTypeEntry;
|
||||
|
||||
/* Structure to hold set iteration abstraction. */
|
||||
@@ -1099,7 +1103,7 @@ int listTypeNext(listTypeIterator *li, listTypeEntry *entry);
|
||||
robj *listTypeGet(listTypeEntry *entry);
|
||||
void listTypeInsert(listTypeEntry *entry, robj *value, int where);
|
||||
int listTypeEqual(listTypeEntry *entry, robj *o);
|
||||
void listTypeDelete(listTypeEntry *entry);
|
||||
void listTypeDelete(listTypeIterator *iter, listTypeEntry *entry);
|
||||
void listTypeConvert(robj *subject, int enc);
|
||||
void unblockClientWaitingData(redisClient *c);
|
||||
void handleClientsBlockedOnLists(void);
|
||||
@@ -1137,7 +1141,7 @@ robj *getDecodedObject(robj *o);
|
||||
size_t stringObjectLen(robj *o);
|
||||
robj *createStringObjectFromLongLong(long long value);
|
||||
robj *createStringObjectFromLongDouble(long double value, int humanfriendly);
|
||||
robj *createListObject(void);
|
||||
robj *createQuicklistObject(void);
|
||||
robj *createZiplistObject(void);
|
||||
robj *createSetObject(void);
|
||||
robj *createIntsetObject(void);
|
||||
|
||||
+3
-2
@@ -220,7 +220,7 @@ void sortCommand(redisClient *c) {
|
||||
if (sortval)
|
||||
incrRefCount(sortval);
|
||||
else
|
||||
sortval = createListObject();
|
||||
sortval = createQuicklistObject();
|
||||
|
||||
/* The SORT command has an SQL-alike syntax, parse it */
|
||||
while(j < c->argc) {
|
||||
@@ -420,6 +420,7 @@ void sortCommand(redisClient *c) {
|
||||
} else {
|
||||
redisPanic("Unknown type");
|
||||
}
|
||||
printf("j: %d; vectorlen: %d\n", j, vectorlen);
|
||||
redisAssertWithInfo(c,sortval,j == vectorlen);
|
||||
|
||||
/* Now it's time to load the right scores in the sorting vector */
|
||||
@@ -509,7 +510,7 @@ void sortCommand(redisClient *c) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
robj *sobj = createZiplistObject();
|
||||
robj *sobj = createQuicklistObject();
|
||||
|
||||
/* STORE option specified, set the sorting result as a List object */
|
||||
for (j = start; j <= end; j++) {
|
||||
|
||||
+98
-260
@@ -33,75 +33,37 @@
|
||||
* List API
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
/* 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. */
|
||||
void listTypeTryConversion(robj *subject, robj *value) {
|
||||
if (subject->encoding != REDIS_ENCODING_ZIPLIST) return;
|
||||
if (sdsEncodedObject(value) &&
|
||||
sdslen(value->ptr) > server.list_max_ziplist_value)
|
||||
listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST);
|
||||
}
|
||||
|
||||
/* The function pushes an element to the specified list object 'subject',
|
||||
* at head or tail position as specified by 'where'.
|
||||
*
|
||||
* There is no need for the caller to increment the refcount of 'value' as
|
||||
* the function takes care of it if needed. */
|
||||
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_LINKEDLIST);
|
||||
|
||||
if (subject->encoding == REDIS_ENCODING_ZIPLIST) {
|
||||
int pos = (where == REDIS_HEAD) ? ZIPLIST_HEAD : ZIPLIST_TAIL;
|
||||
if (subject->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
int pos = (where == REDIS_HEAD) ? QUICKLIST_HEAD : QUICKLIST_TAIL;
|
||||
value = getDecodedObject(value);
|
||||
subject->ptr = ziplistPush(subject->ptr,value->ptr,sdslen(value->ptr),pos);
|
||||
size_t len = sdslen(value->ptr);
|
||||
quicklistPush(subject->ptr, value->ptr, len, pos);
|
||||
decrRefCount(value);
|
||||
} else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) {
|
||||
if (where == REDIS_HEAD) {
|
||||
listAddNodeHead(subject->ptr,value);
|
||||
} else {
|
||||
listAddNodeTail(subject->ptr,value);
|
||||
}
|
||||
incrRefCount(value);
|
||||
} else {
|
||||
redisPanic("Unknown list encoding");
|
||||
}
|
||||
}
|
||||
|
||||
void *listPopSaver(unsigned char *data, unsigned int sz) {
|
||||
return createStringObject((char*)data,sz);
|
||||
}
|
||||
|
||||
robj *listTypePop(robj *subject, int where) {
|
||||
long long vlong;
|
||||
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 {
|
||||
|
||||
int ql_where = where == REDIS_HEAD ? QUICKLIST_HEAD : QUICKLIST_TAIL;
|
||||
if (subject->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
if (quicklistPopCustom(subject->ptr, ql_where, (unsigned char **)&value,
|
||||
NULL, &vlong, listPopSaver)) {
|
||||
if (!value)
|
||||
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_LINKEDLIST) {
|
||||
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");
|
||||
@@ -110,25 +72,28 @@ robj *listTypePop(robj *subject, int where) {
|
||||
}
|
||||
|
||||
unsigned long listTypeLength(robj *subject) {
|
||||
if (subject->encoding == REDIS_ENCODING_ZIPLIST) {
|
||||
return ziplistLen(subject->ptr);
|
||||
} else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) {
|
||||
return listLength((list*)subject->ptr);
|
||||
if (subject->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
return quicklistCount(subject->ptr);
|
||||
} else {
|
||||
redisPanic("Unknown list encoding");
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize an iterator at the specified index. */
|
||||
listTypeIterator *listTypeInitIterator(robj *subject, long index, unsigned char direction) {
|
||||
listTypeIterator *listTypeInitIterator(robj *subject, long 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_LINKEDLIST) {
|
||||
li->ln = listIndex(subject->ptr,index);
|
||||
li->iter = NULL;
|
||||
/* REDIS_HEAD means start at TAIL and move *towards* head.
|
||||
* REDIS_TAIL means start at HEAD and move *towards tail. */
|
||||
int iter_direction =
|
||||
direction == REDIS_HEAD ? AL_START_TAIL : AL_START_HEAD;
|
||||
if (li->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
li->iter = quicklistGetIteratorAtIdx(li->subject->ptr,
|
||||
iter_direction, index);
|
||||
} else {
|
||||
redisPanic("Unknown list encoding");
|
||||
}
|
||||
@@ -137,6 +102,7 @@ listTypeIterator *listTypeInitIterator(robj *subject, long index, unsigned char
|
||||
|
||||
/* Clean up the iterator. */
|
||||
void listTypeReleaseIterator(listTypeIterator *li) {
|
||||
zfree(li->iter);
|
||||
zfree(li);
|
||||
}
|
||||
|
||||
@@ -148,24 +114,8 @@ int listTypeNext(listTypeIterator *li, listTypeEntry *entry) {
|
||||
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_LINKEDLIST) {
|
||||
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;
|
||||
}
|
||||
if (li->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
return quicklistNext(li->iter, &entry->entry);
|
||||
} else {
|
||||
redisPanic("Unknown list encoding");
|
||||
}
|
||||
@@ -174,24 +124,14 @@ int listTypeNext(listTypeIterator *li, listTypeEntry *entry) {
|
||||
|
||||
/* Return entry or NULL at the current position of the iterator. */
|
||||
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);
|
||||
}
|
||||
if (entry->li->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
if (entry->entry.value) {
|
||||
value = createStringObject((char *)entry->entry.value,
|
||||
entry->entry.sz);
|
||||
} else {
|
||||
value = createStringObjectFromLongLong(entry->entry.longval);
|
||||
}
|
||||
} else if (li->encoding == REDIS_ENCODING_LINKEDLIST) {
|
||||
redisAssert(entry->ln != NULL);
|
||||
value = listNodeValue(entry->ln);
|
||||
incrRefCount(value);
|
||||
} else {
|
||||
redisPanic("Unknown list encoding");
|
||||
}
|
||||
@@ -199,30 +139,18 @@ robj *listTypeGet(listTypeEntry *entry) {
|
||||
}
|
||||
|
||||
void listTypeInsert(listTypeEntry *entry, robj *value, int where) {
|
||||
robj *subject = entry->li->subject;
|
||||
if (entry->li->encoding == REDIS_ENCODING_ZIPLIST) {
|
||||
if (entry->li->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
value = getDecodedObject(value);
|
||||
sds str = value->ptr;
|
||||
size_t len = sdslen(str);
|
||||
if (where == REDIS_TAIL) {
|
||||
unsigned char *next = ziplistNext(subject->ptr,entry->zi);
|
||||
|
||||
/* When we insert after the current element, but the current element
|
||||
* is the tail of the list, we need to do a push. */
|
||||
if (next == NULL) {
|
||||
subject->ptr = ziplistPush(subject->ptr,value->ptr,sdslen(value->ptr),REDIS_TAIL);
|
||||
} else {
|
||||
subject->ptr = ziplistInsert(subject->ptr,next,value->ptr,sdslen(value->ptr));
|
||||
}
|
||||
} else {
|
||||
subject->ptr = ziplistInsert(subject->ptr,entry->zi,value->ptr,sdslen(value->ptr));
|
||||
quicklistInsertAfter((quicklist *)entry->entry.quicklist,
|
||||
&entry->entry, str, len);
|
||||
} else if (where == REDIS_HEAD) {
|
||||
quicklistInsertBefore((quicklist *)entry->entry.quicklist,
|
||||
&entry->entry, str, len);
|
||||
}
|
||||
decrRefCount(value);
|
||||
} else if (entry->li->encoding == REDIS_ENCODING_LINKEDLIST) {
|
||||
if (where == REDIS_TAIL) {
|
||||
listInsertNode(subject->ptr,entry->ln,value,AL_START_TAIL);
|
||||
} else {
|
||||
listInsertNode(subject->ptr,entry->ln,value,AL_START_HEAD);
|
||||
}
|
||||
incrRefCount(value);
|
||||
} else {
|
||||
redisPanic("Unknown list encoding");
|
||||
}
|
||||
@@ -230,59 +158,33 @@ void listTypeInsert(listTypeEntry *entry, robj *value, int where) {
|
||||
|
||||
/* Compare the given object with the entry at the current position. */
|
||||
int listTypeEqual(listTypeEntry *entry, robj *o) {
|
||||
listTypeIterator *li = entry->li;
|
||||
if (li->encoding == REDIS_ENCODING_ZIPLIST) {
|
||||
if (entry->li->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
redisAssertWithInfo(NULL,o,sdsEncodedObject(o));
|
||||
return ziplistCompare(entry->zi,o->ptr,sdslen(o->ptr));
|
||||
} else if (li->encoding == REDIS_ENCODING_LINKEDLIST) {
|
||||
return equalStringObjects(o,listNodeValue(entry->ln));
|
||||
return quicklistCompare(entry->entry.zi,o->ptr,sdslen(o->ptr));
|
||||
} else {
|
||||
redisPanic("Unknown list encoding");
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete the element pointed to. */
|
||||
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_LINKEDLIST) {
|
||||
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;
|
||||
void listTypeDelete(listTypeIterator *iter, listTypeEntry *entry) {
|
||||
if (entry->li->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
quicklistDelEntry(iter->iter, &entry->entry);
|
||||
} else {
|
||||
redisPanic("Unknown list encoding");
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a quicklist from a single ziplist */
|
||||
void listTypeConvert(robj *subject, int enc) {
|
||||
listTypeIterator *li;
|
||||
listTypeEntry entry;
|
||||
redisAssertWithInfo(NULL,subject,subject->type == REDIS_LIST);
|
||||
redisAssertWithInfo(NULL,subject,subject->type==REDIS_LIST);
|
||||
redisAssertWithInfo(NULL,subject,subject->encoding==REDIS_ENCODING_ZIPLIST);
|
||||
|
||||
if (enc == REDIS_ENCODING_LINKEDLIST) {
|
||||
list *l = listCreate();
|
||||
listSetFreeMethod(l,decrRefCountVoid);
|
||||
|
||||
/* 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_LINKEDLIST;
|
||||
zfree(subject->ptr);
|
||||
subject->ptr = l;
|
||||
if (enc == REDIS_ENCODING_QUICKLIST) {
|
||||
size_t zlen = server.list_max_ziplist_size;
|
||||
int depth = server.list_compress_depth;
|
||||
subject->ptr = quicklistCreateFromZiplist(zlen, depth, subject->ptr);
|
||||
subject->encoding = REDIS_ENCODING_QUICKLIST;
|
||||
} else {
|
||||
redisPanic("Unsupported list conversion");
|
||||
}
|
||||
@@ -304,7 +206,9 @@ void pushGenericCommand(redisClient *c, int where) {
|
||||
for (j = 2; j < c->argc; j++) {
|
||||
c->argv[j] = tryObjectEncoding(c->argv[j]);
|
||||
if (!lobj) {
|
||||
lobj = createZiplistObject();
|
||||
lobj = createQuicklistObject();
|
||||
quicklistSetOptions(lobj->ptr, server.list_max_ziplist_size,
|
||||
server.list_compress_depth);
|
||||
dbAdd(c->db,c->argv[1],lobj);
|
||||
}
|
||||
listTypePush(lobj,c->argv[j],where);
|
||||
@@ -338,13 +242,6 @@ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) {
|
||||
checkType(c,subject,REDIS_LIST)) return;
|
||||
|
||||
if (refval != NULL) {
|
||||
/* We're not sure if this value can be inserted yet, but we cannot
|
||||
* convert the list inside the iterator. We don't want to loop over
|
||||
* the list twice (once to see if the value can be inserted and once
|
||||
* to do the actual insert), so we assume this value can be inserted
|
||||
* and convert the ziplist to a regular list if necessary. */
|
||||
listTypeTryConversion(subject,val);
|
||||
|
||||
/* Seek refval from head to tail */
|
||||
iter = listTypeInitIterator(subject,0,REDIS_TAIL);
|
||||
while (listTypeNext(iter,&entry)) {
|
||||
@@ -357,10 +254,6 @@ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) {
|
||||
listTypeReleaseIterator(iter);
|
||||
|
||||
if (inserted) {
|
||||
/* Check if the length exceeds the ziplist length threshold. */
|
||||
if (subject->encoding == REDIS_ENCODING_ZIPLIST &&
|
||||
ziplistLen(subject->ptr) > server.list_max_ziplist_entries)
|
||||
listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"linsert",
|
||||
c->argv[1],c->db->id);
|
||||
@@ -418,31 +311,19 @@ void lindexCommand(redisClient *c) {
|
||||
if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != REDIS_OK))
|
||||
return;
|
||||
|
||||
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);
|
||||
if (o->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
quicklistEntry entry;
|
||||
if (quicklistIndex(o->ptr, index, &entry)) {
|
||||
if (entry.value) {
|
||||
value = createStringObject((char*)entry.value,entry.sz);
|
||||
} else {
|
||||
value = createStringObjectFromLongLong(vlong);
|
||||
value = createStringObjectFromLongLong(entry.longval);
|
||||
}
|
||||
addReplyBulk(c,value);
|
||||
decrRefCount(value);
|
||||
} else {
|
||||
addReply(c,shared.nullbulk);
|
||||
}
|
||||
} else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {
|
||||
listNode *ln = listIndex(o->ptr,index);
|
||||
if (ln != NULL) {
|
||||
value = listNodeValue(ln);
|
||||
addReplyBulk(c,value);
|
||||
} else {
|
||||
addReply(c,shared.nullbulk);
|
||||
}
|
||||
} else {
|
||||
redisPanic("Unknown list encoding");
|
||||
}
|
||||
@@ -452,35 +333,18 @@ void lsetCommand(redisClient *c) {
|
||||
robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr);
|
||||
if (o == NULL || checkType(c,o,REDIS_LIST)) return;
|
||||
long index;
|
||||
robj *value = (c->argv[3] = tryObjectEncoding(c->argv[3]));
|
||||
robj *value = c->argv[3];
|
||||
|
||||
if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != REDIS_OK))
|
||||
return;
|
||||
|
||||
listTypeTryConversion(o,value);
|
||||
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
|
||||
unsigned char *p, *zl = o->ptr;
|
||||
p = ziplistIndex(zl,index);
|
||||
if (p == NULL) {
|
||||
if (o->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
quicklist *ql = o->ptr;
|
||||
int replaced = quicklistReplaceAtIndex(ql, index,
|
||||
value->ptr, sdslen(value->ptr));
|
||||
if (!replaced) {
|
||||
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);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"lset",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
}
|
||||
} else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {
|
||||
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);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"lset",c->argv[1],c->db->id);
|
||||
@@ -549,43 +413,28 @@ void lrangeCommand(redisClient *c) {
|
||||
|
||||
/* Return the result in form of a multi-bulk reply */
|
||||
addReplyMultiBulkLen(c,rangelen);
|
||||
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
|
||||
unsigned char *p = ziplistIndex(o->ptr,start);
|
||||
unsigned char *vstr;
|
||||
unsigned int vlen;
|
||||
long long vlong;
|
||||
if (o->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
listTypeIterator *iter = listTypeInitIterator(o, start, REDIS_TAIL);
|
||||
|
||||
while(rangelen--) {
|
||||
ziplistGet(p,&vstr,&vlen,&vlong);
|
||||
if (vstr) {
|
||||
addReplyBulkCBuffer(c,vstr,vlen);
|
||||
listTypeEntry entry;
|
||||
listTypeNext(iter, &entry);
|
||||
quicklistEntry *qe = &entry.entry;
|
||||
if (qe->value) {
|
||||
addReplyBulkCBuffer(c,qe->value,qe->sz);
|
||||
} else {
|
||||
addReplyBulkLongLong(c,vlong);
|
||||
addReplyBulkLongLong(c,qe->longval);
|
||||
}
|
||||
p = ziplistNext(o->ptr,p);
|
||||
}
|
||||
} else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {
|
||||
listNode *ln;
|
||||
|
||||
/* If we are nearest to the end of the list, reach the element
|
||||
* starting from tail and going backward, as it is faster. */
|
||||
if (start > llen/2) start -= llen;
|
||||
ln = listIndex(o->ptr,start);
|
||||
|
||||
while(rangelen--) {
|
||||
addReplyBulk(c,ln->value);
|
||||
ln = ln->next;
|
||||
}
|
||||
listTypeReleaseIterator(iter);
|
||||
} else {
|
||||
redisPanic("List encoding is not LINKEDLIST nor ZIPLIST!");
|
||||
redisPanic("List encoding is not QUICKLIST!");
|
||||
}
|
||||
}
|
||||
|
||||
void ltrimCommand(redisClient *c) {
|
||||
robj *o;
|
||||
long start, end, llen, j, ltrim, rtrim;
|
||||
list *list;
|
||||
listNode *ln;
|
||||
long start, end, llen, ltrim, rtrim;
|
||||
|
||||
if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) ||
|
||||
(getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return;
|
||||
@@ -612,19 +461,9 @@ void ltrimCommand(redisClient *c) {
|
||||
}
|
||||
|
||||
/* Remove list elements to perform the trim */
|
||||
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_LINKEDLIST) {
|
||||
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);
|
||||
}
|
||||
if (o->encoding == REDIS_ENCODING_QUICKLIST) {
|
||||
quicklistDelRange(o->ptr,0,ltrim);
|
||||
quicklistDelRange(o->ptr,-rtrim,rtrim);
|
||||
} else {
|
||||
redisPanic("Unknown list encoding");
|
||||
}
|
||||
@@ -641,10 +480,9 @@ void ltrimCommand(redisClient *c) {
|
||||
|
||||
void lremCommand(redisClient *c) {
|
||||
robj *subject, *obj;
|
||||
obj = c->argv[3] = tryObjectEncoding(c->argv[3]);
|
||||
obj = c->argv[3];
|
||||
long toremove;
|
||||
long removed = 0;
|
||||
listTypeEntry entry;
|
||||
|
||||
if ((getLongFromObjectOrReply(c, c->argv[2], &toremove, NULL) != REDIS_OK))
|
||||
return;
|
||||
@@ -652,10 +490,6 @@ void lremCommand(redisClient *c) {
|
||||
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;
|
||||
@@ -664,9 +498,10 @@ void lremCommand(redisClient *c) {
|
||||
li = listTypeInitIterator(subject,0,REDIS_TAIL);
|
||||
}
|
||||
|
||||
listTypeEntry entry;
|
||||
while (listTypeNext(li,&entry)) {
|
||||
if (listTypeEqual(&entry,obj)) {
|
||||
listTypeDelete(&entry);
|
||||
listTypeDelete(li, &entry);
|
||||
server.dirty++;
|
||||
removed++;
|
||||
if (toremove && removed == toremove) break;
|
||||
@@ -674,11 +509,10 @@ void lremCommand(redisClient *c) {
|
||||
}
|
||||
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]);
|
||||
}
|
||||
|
||||
if (listTypeLength(subject) == 0) dbDelete(c->db,c->argv[1]);
|
||||
addReplyLongLong(c,removed);
|
||||
if (removed) signalModifiedKey(c->db,c->argv[1]);
|
||||
}
|
||||
@@ -702,7 +536,9 @@ void lremCommand(redisClient *c) {
|
||||
void rpoplpushHandlePush(redisClient *c, robj *dstkey, robj *dstobj, robj *value) {
|
||||
/* Create the list if the key does not exist */
|
||||
if (!dstobj) {
|
||||
dstobj = createZiplistObject();
|
||||
dstobj = createQuicklistObject();
|
||||
quicklistSetOptions(dstobj->ptr, server.list_max_ziplist_size,
|
||||
server.list_compress_depth);
|
||||
dbAdd(c->db,dstkey,dstobj);
|
||||
}
|
||||
signalModifiedKey(c->db,dstkey);
|
||||
@@ -1010,7 +846,9 @@ void handleClientsBlockedOnLists(void) {
|
||||
}
|
||||
}
|
||||
|
||||
if (listTypeLength(o) == 0) dbDelete(rl->db,rl->key);
|
||||
if (listTypeLength(o) == 0) {
|
||||
dbDelete(rl->db,rl->key);
|
||||
}
|
||||
/* We don't call signalModifiedKey() as it was already called
|
||||
* when an element was pushed on the list. */
|
||||
}
|
||||
|
||||
+218
@@ -143,6 +143,7 @@
|
||||
#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_END_SIZE (sizeof(uint8_t))
|
||||
#define ZIPLIST_ENTRY_HEAD(zl) ((zl)+ZIPLIST_HEADER_SIZE)
|
||||
#define ZIPLIST_ENTRY_TAIL(zl) ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))
|
||||
#define ZIPLIST_ENTRY_END(zl) ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1)
|
||||
@@ -176,6 +177,8 @@ typedef struct zlentry {
|
||||
if ((encoding) < ZIP_STR_MASK) (encoding) &= ZIP_STR_MASK; \
|
||||
} while(0)
|
||||
|
||||
void ziplistRepr(unsigned char *zl);
|
||||
|
||||
/* Return bytes needed to store integer encoded by 'encoding' */
|
||||
static unsigned int zipIntSize(unsigned char encoding) {
|
||||
switch(encoding) {
|
||||
@@ -670,6 +673,121 @@ static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsig
|
||||
return zl;
|
||||
}
|
||||
|
||||
/* Merge ziplists 'first' and 'second' by appending 'second' to 'first'.
|
||||
*
|
||||
* NOTE: The larger ziplist is reallocated to contain the new merged ziplist.
|
||||
* Either 'first' or 'second' can be used for the result. The parameter not
|
||||
* used will be free'd and set to NULL.
|
||||
*
|
||||
* After calling this function, the input parameters are no longer valid since
|
||||
* they are changed and free'd in-place.
|
||||
*
|
||||
* The result ziplist is the contents of 'first' followed by 'second'.
|
||||
*
|
||||
* On failure: returns NULL if the merge is impossible.
|
||||
* On success: returns the merged ziplist (which is expanded version of either
|
||||
* 'first' or 'second', also frees the other unused input ziplist, and sets the
|
||||
* input ziplist argument equal to newly reallocated ziplist return value. */
|
||||
unsigned char *ziplistMerge(unsigned char **first, unsigned char **second) {
|
||||
/* If any params are null, we can't merge, so NULL. */
|
||||
if (first == NULL || *first == NULL || second == NULL || *second == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Can't merge same list into itself. */
|
||||
if (*first == *second)
|
||||
return NULL;
|
||||
|
||||
size_t first_bytes = intrev32ifbe(ZIPLIST_BYTES(*first));
|
||||
size_t first_len = intrev16ifbe(ZIPLIST_LENGTH(*first));
|
||||
|
||||
size_t second_bytes = intrev32ifbe(ZIPLIST_BYTES(*second));
|
||||
size_t second_len = intrev16ifbe(ZIPLIST_LENGTH(*second));
|
||||
|
||||
int append;
|
||||
unsigned char *source, *target;
|
||||
size_t target_bytes, source_bytes;
|
||||
/* Pick the largest ziplist so we can resize easily in-place.
|
||||
* We must also track if we are now appending or prepending to
|
||||
* the target ziplist. */
|
||||
if (first_len >= second_len) {
|
||||
/* retain first, append second to first. */
|
||||
target = *first;
|
||||
target_bytes = first_bytes;
|
||||
source = *second;
|
||||
source_bytes = second_bytes;
|
||||
append = 1;
|
||||
} else {
|
||||
/* else, retain second, prepend first to second. */
|
||||
target = *second;
|
||||
target_bytes = second_bytes;
|
||||
source = *first;
|
||||
source_bytes = first_bytes;
|
||||
append = 0;
|
||||
}
|
||||
|
||||
/* Calculate final bytes (subtract one pair of metadata) */
|
||||
size_t zlbytes = first_bytes + second_bytes -
|
||||
ZIPLIST_HEADER_SIZE - ZIPLIST_END_SIZE;
|
||||
size_t zllength = first_len + second_len;
|
||||
|
||||
/* Combined zl length should be limited within UINT16_MAX */
|
||||
zllength = zllength < UINT16_MAX ? zllength : UINT16_MAX;
|
||||
|
||||
/* Save offset positions before we start ripping memory apart. */
|
||||
size_t first_offset = intrev32ifbe(ZIPLIST_TAIL_OFFSET(*first));
|
||||
size_t second_offset = intrev32ifbe(ZIPLIST_TAIL_OFFSET(*second));
|
||||
|
||||
/* Extend target to new zlbytes then append or prepend source. */
|
||||
target = zrealloc(target, zlbytes);
|
||||
if (append) {
|
||||
/* append == appending to target */
|
||||
/* Copy source after target (copying over original [END]):
|
||||
* [TARGET - END, SOURCE - HEADER] */
|
||||
memcpy(target + target_bytes - ZIPLIST_END_SIZE,
|
||||
source + ZIPLIST_HEADER_SIZE,
|
||||
source_bytes - ZIPLIST_HEADER_SIZE);
|
||||
} else {
|
||||
/* !append == prepending to target */
|
||||
/* Move target *contents* exactly size of (source - [END]),
|
||||
* then copy source into vacataed space (source - [END]):
|
||||
* [SOURCE - END, TARGET - HEADER] */
|
||||
memmove(target + source_bytes - ZIPLIST_END_SIZE,
|
||||
target + ZIPLIST_HEADER_SIZE,
|
||||
target_bytes - ZIPLIST_HEADER_SIZE);
|
||||
memcpy(target, source, source_bytes - ZIPLIST_END_SIZE);
|
||||
}
|
||||
|
||||
/* Update header metadata. */
|
||||
ZIPLIST_BYTES(target) = intrev32ifbe(zlbytes);
|
||||
ZIPLIST_LENGTH(target) = intrev16ifbe(zllength);
|
||||
/* New tail offset is:
|
||||
* + N bytes of first ziplist
|
||||
* - 1 byte for [END] of first ziplist
|
||||
* + M bytes for the offset of the original tail of the second ziplist
|
||||
* - J bytes for HEADER because second_offset keeps no header. */
|
||||
ZIPLIST_TAIL_OFFSET(target) = intrev32ifbe(
|
||||
(first_bytes - ZIPLIST_END_SIZE) +
|
||||
(second_offset - ZIPLIST_HEADER_SIZE));
|
||||
|
||||
/* __ziplistCascadeUpdate just fixes the prev length values until it finds a
|
||||
* correct prev length value (then it assumes the rest of the list is okay).
|
||||
* We tell CascadeUpdate to start at the first ziplist's tail element to fix
|
||||
* the merge seam. */
|
||||
target = __ziplistCascadeUpdate(target, target+first_offset);
|
||||
|
||||
/* Now free and NULL out what we didn't realloc */
|
||||
if (append) {
|
||||
zfree(*second);
|
||||
*second = NULL;
|
||||
*first = target;
|
||||
} else {
|
||||
zfree(*first);
|
||||
*first = NULL;
|
||||
*second = target;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -1104,6 +1222,8 @@ int ziplistTest(int argc, char **argv) {
|
||||
zl = createIntList();
|
||||
ziplistRepr(zl);
|
||||
|
||||
zfree(zl);
|
||||
|
||||
zl = createList();
|
||||
ziplistRepr(zl);
|
||||
|
||||
@@ -1119,6 +1239,8 @@ int ziplistTest(int argc, char **argv) {
|
||||
zl = pop(zl,ZIPLIST_TAIL);
|
||||
ziplistRepr(zl);
|
||||
|
||||
zfree(zl);
|
||||
|
||||
printf("Get element at index 3:\n");
|
||||
{
|
||||
zl = createList();
|
||||
@@ -1134,6 +1256,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
printf("%lld\n", value);
|
||||
}
|
||||
printf("\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Get element at index 4 (out of range):\n");
|
||||
@@ -1147,6 +1270,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
printf("\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Get element at index -1 (last element):\n");
|
||||
@@ -1164,6 +1288,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
printf("%lld\n", value);
|
||||
}
|
||||
printf("\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Get element at index -4 (first element):\n");
|
||||
@@ -1181,6 +1306,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
printf("%lld\n", value);
|
||||
}
|
||||
printf("\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Get element at index -5 (reverse out of range):\n");
|
||||
@@ -1194,6 +1320,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
printf("\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Iterate list from 0 to end:\n");
|
||||
@@ -1211,6 +1338,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Iterate list from 1 to end:\n");
|
||||
@@ -1228,6 +1356,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Iterate list from 2 to end:\n");
|
||||
@@ -1245,6 +1374,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Iterate starting out of range:\n");
|
||||
@@ -1257,6 +1387,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
printf("ERROR\n");
|
||||
}
|
||||
printf("\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Iterate from back to front:\n");
|
||||
@@ -1274,6 +1405,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Iterate from back to front, deleting all items:\n");
|
||||
@@ -1292,6 +1424,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Delete inclusive range 0,0:\n");
|
||||
@@ -1299,6 +1432,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
zl = createList();
|
||||
zl = ziplistDeleteRange(zl, 0, 1);
|
||||
ziplistRepr(zl);
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Delete inclusive range 0,1:\n");
|
||||
@@ -1306,6 +1440,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
zl = createList();
|
||||
zl = ziplistDeleteRange(zl, 0, 2);
|
||||
ziplistRepr(zl);
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Delete inclusive range 1,2:\n");
|
||||
@@ -1313,6 +1448,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
zl = createList();
|
||||
zl = ziplistDeleteRange(zl, 1, 2);
|
||||
ziplistRepr(zl);
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Delete with start index out of range:\n");
|
||||
@@ -1320,6 +1456,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
zl = createList();
|
||||
zl = ziplistDeleteRange(zl, 5, 1);
|
||||
ziplistRepr(zl);
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Delete with num overflow:\n");
|
||||
@@ -1327,6 +1464,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
zl = createList();
|
||||
zl = ziplistDeleteRange(zl, 1, 5);
|
||||
ziplistRepr(zl);
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Delete foo while iterating:\n");
|
||||
@@ -1351,6 +1489,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
}
|
||||
printf("\n");
|
||||
ziplistRepr(zl);
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Regression test for >255 byte strings:\n");
|
||||
@@ -1370,6 +1509,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
assert(ziplistGet(p,&entry,&elen,&value));
|
||||
assert(strncmp(v2,(char*)entry,elen) == 0);
|
||||
printf("SUCCESS\n\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Regression test deleting next to last entries:\n");
|
||||
@@ -1408,6 +1548,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
assert(e[1].prevrawlensize == 5);
|
||||
|
||||
printf("SUCCESS\n\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Create long list and check indices:\n");
|
||||
@@ -1429,6 +1570,7 @@ int ziplistTest(int argc, char **argv) {
|
||||
assert(999-i == value);
|
||||
}
|
||||
printf("SUCCESS\n\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Compare strings with ziplist entries:\n");
|
||||
@@ -1454,6 +1596,82 @@ int ziplistTest(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
printf("SUCCESS\n\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Merge test:\n");
|
||||
{
|
||||
/* create list gives us: [hello, foo, quux, 1024] */
|
||||
zl = createList();
|
||||
unsigned char *zl2 = createList();
|
||||
|
||||
unsigned char *zl3 = ziplistNew();
|
||||
unsigned char *zl4 = ziplistNew();
|
||||
|
||||
if (ziplistMerge(&zl4, &zl4)) {
|
||||
printf("ERROR: Allowed merging of one ziplist into itself.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Merge two empty ziplists, get empty result back. */
|
||||
zl4 = ziplistMerge(&zl3, &zl4);
|
||||
ziplistRepr(zl4);
|
||||
if (ziplistLen(zl4)) {
|
||||
printf("ERROR: Merging two empty ziplists created entries.\n");
|
||||
return 1;
|
||||
}
|
||||
zfree(zl4);
|
||||
|
||||
zl2 = ziplistMerge(&zl, &zl2);
|
||||
/* merge gives us: [hello, foo, quux, 1024, hello, foo, quux, 1024] */
|
||||
ziplistRepr(zl2);
|
||||
|
||||
if (ziplistLen(zl2) != 8) {
|
||||
printf("ERROR: Merged length not 8, but: %u\n", ziplistLen(zl2));
|
||||
return 1;
|
||||
}
|
||||
|
||||
p = ziplistIndex(zl2,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(zl2,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;
|
||||
}
|
||||
|
||||
p = ziplistIndex(zl2,4);
|
||||
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(zl2,7);
|
||||
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\n");
|
||||
zfree(zl);
|
||||
}
|
||||
|
||||
printf("Stress with random payloads of different encoding:\n");
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#define ZIPLIST_TAIL 1
|
||||
|
||||
unsigned char *ziplistNew(void);
|
||||
unsigned char *ziplistMerge(unsigned char **first, unsigned char **second);
|
||||
unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where);
|
||||
unsigned char *ziplistIndex(unsigned char *zl, int index);
|
||||
unsigned char *ziplistNext(unsigned char *zl, unsigned char *p);
|
||||
|
||||
@@ -19,9 +19,12 @@ proc assert_match {pattern value} {
|
||||
}
|
||||
}
|
||||
|
||||
proc assert_equal {expected value} {
|
||||
proc assert_equal {expected value {detail ""}} {
|
||||
if {$expected ne $value} {
|
||||
error "assertion:Expected '$value' to be equal to '$expected'"
|
||||
if {$detail ne ""} {
|
||||
set detail " (detail: $detail)"
|
||||
}
|
||||
error "assertion:Expected '$value' to be equal to '$expected'$detail"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,10 +77,10 @@ start_server {tags {"aofrw"}} {
|
||||
}
|
||||
|
||||
foreach d {string int} {
|
||||
foreach e {ziplist linkedlist} {
|
||||
foreach e {quicklist} {
|
||||
test "AOF rewrite of list with $e encoding, $d data" {
|
||||
r flushall
|
||||
if {$e eq {ziplist}} {set len 10} else {set len 1000}
|
||||
set len 1000
|
||||
for {set j 0} {$j < $len} {incr j} {
|
||||
if {$d eq {string}} {
|
||||
set data [randstring 0 16 alpha]
|
||||
|
||||
+2
-2
@@ -157,7 +157,7 @@ start_server {tags {"dump"}} {
|
||||
test {MIGRATE can correctly transfer large values} {
|
||||
set first [srv 0 client]
|
||||
r del key
|
||||
for {set j 0} {$j < 5000} {incr j} {
|
||||
for {set j 0} {$j < 40000} {incr j} {
|
||||
r rpush key 1 2 3 4 5 6 7 8 9 10
|
||||
r rpush key "item 1" "item 2" "item 3" "item 4" "item 5" \
|
||||
"item 6" "item 7" "item 8" "item 9" "item 10"
|
||||
@@ -175,7 +175,7 @@ start_server {tags {"dump"}} {
|
||||
assert {[$first exists key] == 0}
|
||||
assert {[$second exists key] == 1}
|
||||
assert {[$second ttl key] == -1}
|
||||
assert {[$second llen key] == 5000*20}
|
||||
assert {[$second llen key] == 40000*20}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-7
@@ -1,8 +1,7 @@
|
||||
start_server {
|
||||
tags {"sort"}
|
||||
overrides {
|
||||
"list-max-ziplist-value" 16
|
||||
"list-max-ziplist-entries" 32
|
||||
"list-max-ziplist-size" 32
|
||||
"set-max-intset-entries" 32
|
||||
}
|
||||
} {
|
||||
@@ -36,9 +35,9 @@ start_server {
|
||||
}
|
||||
|
||||
foreach {num cmd enc title} {
|
||||
16 lpush ziplist "Ziplist"
|
||||
1000 lpush linkedlist "Linked list"
|
||||
10000 lpush linkedlist "Big Linked list"
|
||||
16 lpush quicklist "Old Ziplist"
|
||||
1000 lpush quicklist "Old Linked list"
|
||||
10000 lpush quicklist "Old Big Linked list"
|
||||
16 sadd intset "Intset"
|
||||
1000 sadd hashtable "Hash table"
|
||||
10000 sadd hashtable "Big Hash table"
|
||||
@@ -85,14 +84,14 @@ start_server {
|
||||
r sort tosort BY weight_* store sort-res
|
||||
assert_equal $result [r lrange sort-res 0 -1]
|
||||
assert_equal 16 [r llen sort-res]
|
||||
assert_encoding ziplist sort-res
|
||||
assert_encoding quicklist sort-res
|
||||
}
|
||||
|
||||
test "SORT BY hash field STORE" {
|
||||
r sort tosort BY wobj_*->weight store sort-res
|
||||
assert_equal $result [r lrange sort-res 0 -1]
|
||||
assert_equal 16 [r llen sort-res]
|
||||
assert_encoding ziplist sort-res
|
||||
assert_encoding quicklist sort-res
|
||||
}
|
||||
|
||||
test "SORT extracts STORE correctly" {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
start_server {
|
||||
tags {"list"}
|
||||
overrides {
|
||||
"list-max-ziplist-value" 16
|
||||
"list-max-ziplist-entries" 256
|
||||
"list-max-ziplist-size" 4
|
||||
}
|
||||
} {
|
||||
source "tests/unit/type/list-common.tcl"
|
||||
@@ -28,14 +27,18 @@ start_server {
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set min [expr {int(rand()*$startlen)}]
|
||||
set max [expr {$min+int(rand()*$startlen)}]
|
||||
set before_len [llength $mylist]
|
||||
set before_len_r [r llen mylist]
|
||||
set mylist [lrange $mylist $min $max]
|
||||
r ltrim mylist $min $max
|
||||
assert_equal $mylist [r lrange mylist 0 -1]
|
||||
assert_equal $mylist [r lrange mylist 0 -1] "failed trim"
|
||||
|
||||
set starting [r llen mylist]
|
||||
for {set j [r llen mylist]} {$j < $startlen} {incr j} {
|
||||
set str [randomInt 9223372036854775807]
|
||||
r rpush mylist $str
|
||||
lappend mylist $str
|
||||
assert_equal $mylist [r lrange mylist 0 -1] "failed append match"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
start_server {
|
||||
tags {list ziplist}
|
||||
overrides {
|
||||
"list-max-ziplist-value" 200000
|
||||
"list-max-ziplist-entries" 256
|
||||
"list-max-ziplist-size" 16
|
||||
}
|
||||
} {
|
||||
test {Explicit regression for a list bug} {
|
||||
|
||||
+54
-115
@@ -1,25 +1,24 @@
|
||||
start_server {
|
||||
tags {"list"}
|
||||
overrides {
|
||||
"list-max-ziplist-value" 16
|
||||
"list-max-ziplist-entries" 256
|
||||
"list-max-ziplist-size" 5
|
||||
}
|
||||
} {
|
||||
source "tests/unit/type/list-common.tcl"
|
||||
|
||||
test {LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist} {
|
||||
# first lpush then rpush
|
||||
assert_equal 1 [r lpush myziplist1 a]
|
||||
assert_equal 2 [r rpush myziplist1 b]
|
||||
assert_equal 3 [r rpush myziplist1 c]
|
||||
assert_equal 1 [r lpush myziplist1 aa]
|
||||
assert_equal 2 [r rpush myziplist1 bb]
|
||||
assert_equal 3 [r rpush myziplist1 cc]
|
||||
assert_equal 3 [r llen myziplist1]
|
||||
assert_equal a [r lindex myziplist1 0]
|
||||
assert_equal b [r lindex myziplist1 1]
|
||||
assert_equal c [r lindex myziplist1 2]
|
||||
assert_equal aa [r lindex myziplist1 0]
|
||||
assert_equal bb [r lindex myziplist1 1]
|
||||
assert_equal cc [r lindex myziplist1 2]
|
||||
assert_equal {} [r lindex myziplist2 3]
|
||||
assert_equal c [r rpop myziplist1]
|
||||
assert_equal a [r lpop myziplist1]
|
||||
assert_encoding ziplist myziplist1
|
||||
assert_equal cc [r rpop myziplist1]
|
||||
assert_equal aa [r lpop myziplist1]
|
||||
assert_encoding quicklist myziplist1
|
||||
|
||||
# first rpush then lpush
|
||||
assert_equal 1 [r rpush myziplist2 a]
|
||||
@@ -32,13 +31,13 @@ start_server {
|
||||
assert_equal {} [r lindex myziplist2 3]
|
||||
assert_equal a [r rpop myziplist2]
|
||||
assert_equal c [r lpop myziplist2]
|
||||
assert_encoding ziplist myziplist2
|
||||
assert_encoding quicklist myziplist2
|
||||
}
|
||||
|
||||
test {LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list} {
|
||||
# first lpush then rpush
|
||||
assert_equal 1 [r lpush mylist1 $largevalue(linkedlist)]
|
||||
assert_encoding linkedlist mylist1
|
||||
assert_encoding quicklist mylist1
|
||||
assert_equal 2 [r rpush mylist1 b]
|
||||
assert_equal 3 [r rpush mylist1 c]
|
||||
assert_equal 3 [r llen mylist1]
|
||||
@@ -51,7 +50,7 @@ start_server {
|
||||
|
||||
# first rpush then lpush
|
||||
assert_equal 1 [r rpush mylist2 $largevalue(linkedlist)]
|
||||
assert_encoding linkedlist mylist2
|
||||
assert_encoding quicklist mylist2
|
||||
assert_equal 2 [r lpush mylist2 b]
|
||||
assert_equal 3 [r lpush mylist2 c]
|
||||
assert_equal 3 [r llen mylist2]
|
||||
@@ -74,34 +73,22 @@ start_server {
|
||||
assert_equal {d c b a 0 1 2 3} [r lrange mylist 0 -1]
|
||||
}
|
||||
|
||||
test {DEL a list - ziplist} {
|
||||
assert_equal 1 [r del myziplist2]
|
||||
assert_equal 0 [r exists myziplist2]
|
||||
assert_equal 0 [r llen myziplist2]
|
||||
}
|
||||
|
||||
test {DEL a list - regular list} {
|
||||
test {DEL a list} {
|
||||
assert_equal 1 [r del mylist2]
|
||||
assert_equal 0 [r exists mylist2]
|
||||
assert_equal 0 [r llen mylist2]
|
||||
}
|
||||
|
||||
proc create_ziplist {key entries} {
|
||||
proc create_list {key entries} {
|
||||
r del $key
|
||||
foreach entry $entries { r rpush $key $entry }
|
||||
assert_encoding ziplist $key
|
||||
}
|
||||
|
||||
proc create_linkedlist {key entries} {
|
||||
r del $key
|
||||
foreach entry $entries { r rpush $key $entry }
|
||||
assert_encoding linkedlist $key
|
||||
assert_encoding quicklist $key
|
||||
}
|
||||
|
||||
foreach {type large} [array get largevalue] {
|
||||
test "BLPOP, BRPOP: single existing list - $type" {
|
||||
set rd [redis_deferring_client]
|
||||
create_$type blist "a b $large c d"
|
||||
create_list blist "a b $large c d"
|
||||
|
||||
$rd blpop blist 1
|
||||
assert_equal {blist a} [$rd read]
|
||||
@@ -116,8 +103,8 @@ start_server {
|
||||
|
||||
test "BLPOP, BRPOP: multiple existing lists - $type" {
|
||||
set rd [redis_deferring_client]
|
||||
create_$type blist1 "a $large c"
|
||||
create_$type blist2 "d $large f"
|
||||
create_list blist1 "a $large c"
|
||||
create_list blist2 "d $large f"
|
||||
|
||||
$rd blpop blist1 blist2 1
|
||||
assert_equal {blist1 a} [$rd read]
|
||||
@@ -137,7 +124,7 @@ start_server {
|
||||
test "BLPOP, BRPOP: second list has an entry - $type" {
|
||||
set rd [redis_deferring_client]
|
||||
r del blist1
|
||||
create_$type blist2 "d $large f"
|
||||
create_list blist2 "d $large f"
|
||||
|
||||
$rd blpop blist1 blist2 1
|
||||
assert_equal {blist2 d} [$rd read]
|
||||
@@ -151,7 +138,7 @@ start_server {
|
||||
r del target
|
||||
|
||||
set rd [redis_deferring_client]
|
||||
create_$type blist "a b $large c d"
|
||||
create_list blist "a b $large c d"
|
||||
|
||||
$rd brpoplpush blist target 1
|
||||
assert_equal d [$rd read]
|
||||
@@ -517,28 +504,28 @@ start_server {
|
||||
|
||||
foreach {type large} [array get largevalue] {
|
||||
test "LPUSHX, RPUSHX - $type" {
|
||||
create_$type xlist "$large c"
|
||||
create_list xlist "$large c"
|
||||
assert_equal 3 [r rpushx xlist d]
|
||||
assert_equal 4 [r lpushx xlist a]
|
||||
assert_equal "a $large c d" [r lrange xlist 0 -1]
|
||||
}
|
||||
|
||||
test "LINSERT - $type" {
|
||||
create_$type xlist "a $large c d"
|
||||
assert_equal 5 [r linsert xlist before c zz]
|
||||
assert_equal "a $large zz c d" [r lrange xlist 0 10]
|
||||
assert_equal 6 [r linsert xlist after c yy]
|
||||
assert_equal "a $large zz c yy d" [r lrange xlist 0 10]
|
||||
assert_equal 7 [r linsert xlist after d dd]
|
||||
assert_equal -1 [r linsert xlist after bad ddd]
|
||||
assert_equal "a $large zz c yy d dd" [r lrange xlist 0 10]
|
||||
assert_equal 8 [r linsert xlist before a aa]
|
||||
assert_equal -1 [r linsert xlist before bad aaa]
|
||||
assert_equal "aa a $large zz c yy d dd" [r lrange xlist 0 10]
|
||||
create_list xlist "a $large c d"
|
||||
assert_equal 5 [r linsert xlist before c zz] "before c"
|
||||
assert_equal "a $large zz c d" [r lrange xlist 0 10] "lrangeA"
|
||||
assert_equal 6 [r linsert xlist after c yy] "after c"
|
||||
assert_equal "a $large zz c yy d" [r lrange xlist 0 10] "lrangeB"
|
||||
assert_equal 7 [r linsert xlist after d dd] "after d"
|
||||
assert_equal -1 [r linsert xlist after bad ddd] "after bad"
|
||||
assert_equal "a $large zz c yy d dd" [r lrange xlist 0 10] "lrangeC"
|
||||
assert_equal 8 [r linsert xlist before a aa] "before a"
|
||||
assert_equal -1 [r linsert xlist before bad aaa] "before bad"
|
||||
assert_equal "aa a $large zz c yy d dd" [r lrange xlist 0 10] "lrangeD"
|
||||
|
||||
# check inserting integer encoded value
|
||||
assert_equal 9 [r linsert xlist before aa 42]
|
||||
assert_equal 42 [r lrange xlist 0 0]
|
||||
assert_equal 9 [r linsert xlist before aa 42] "before aa"
|
||||
assert_equal 42 [r lrange xlist 0 0] "lrangeE"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,55 +534,7 @@ start_server {
|
||||
set e
|
||||
} {*ERR*syntax*error*}
|
||||
|
||||
test {LPUSHX, RPUSHX convert from ziplist to list} {
|
||||
set large $largevalue(linkedlist)
|
||||
|
||||
# convert when a large value is pushed
|
||||
create_ziplist xlist a
|
||||
assert_equal 2 [r rpushx xlist $large]
|
||||
assert_encoding linkedlist xlist
|
||||
create_ziplist xlist a
|
||||
assert_equal 2 [r lpushx xlist $large]
|
||||
assert_encoding linkedlist xlist
|
||||
|
||||
# convert when the length threshold is exceeded
|
||||
create_ziplist xlist [lrepeat 256 a]
|
||||
assert_equal 257 [r rpushx xlist b]
|
||||
assert_encoding linkedlist xlist
|
||||
create_ziplist xlist [lrepeat 256 a]
|
||||
assert_equal 257 [r lpushx xlist b]
|
||||
assert_encoding linkedlist xlist
|
||||
}
|
||||
|
||||
test {LINSERT convert from ziplist to list} {
|
||||
set large $largevalue(linkedlist)
|
||||
|
||||
# convert when a large value is inserted
|
||||
create_ziplist xlist a
|
||||
assert_equal 2 [r linsert xlist before a $large]
|
||||
assert_encoding linkedlist xlist
|
||||
create_ziplist xlist a
|
||||
assert_equal 2 [r linsert xlist after a $large]
|
||||
assert_encoding linkedlist xlist
|
||||
|
||||
# convert when the length threshold is exceeded
|
||||
create_ziplist xlist [lrepeat 256 a]
|
||||
assert_equal 257 [r linsert xlist before a a]
|
||||
assert_encoding linkedlist xlist
|
||||
create_ziplist xlist [lrepeat 256 a]
|
||||
assert_equal 257 [r linsert xlist after a a]
|
||||
assert_encoding linkedlist xlist
|
||||
|
||||
# don't convert when the value could not be inserted
|
||||
create_ziplist xlist [lrepeat 256 a]
|
||||
assert_equal -1 [r linsert xlist before foo a]
|
||||
assert_encoding ziplist xlist
|
||||
create_ziplist xlist [lrepeat 256 a]
|
||||
assert_equal -1 [r linsert xlist after foo a]
|
||||
assert_encoding ziplist xlist
|
||||
}
|
||||
|
||||
foreach {type num} {ziplist 250 linkedlist 500} {
|
||||
foreach {type num} {quicklist 250 quicklist 500} {
|
||||
proc check_numbered_list_consistency {key} {
|
||||
set len [r llen $key]
|
||||
for {set i 0} {$i < $len} {incr i} {
|
||||
@@ -664,16 +603,16 @@ start_server {
|
||||
foreach {type large} [array get largevalue] {
|
||||
test "RPOPLPUSH base case - $type" {
|
||||
r del mylist1 mylist2
|
||||
create_$type mylist1 "a $large c d"
|
||||
create_list mylist1 "a $large c d"
|
||||
assert_equal d [r rpoplpush mylist1 mylist2]
|
||||
assert_equal c [r rpoplpush mylist1 mylist2]
|
||||
assert_equal "a $large" [r lrange mylist1 0 -1]
|
||||
assert_equal "c d" [r lrange mylist2 0 -1]
|
||||
assert_encoding ziplist mylist2
|
||||
assert_encoding quicklist mylist2
|
||||
}
|
||||
|
||||
test "RPOPLPUSH with the same list as src and dst - $type" {
|
||||
create_$type mylist "a $large c"
|
||||
create_list mylist "a $large c"
|
||||
assert_equal "a $large c" [r lrange mylist 0 -1]
|
||||
assert_equal c [r rpoplpush mylist mylist]
|
||||
assert_equal "c a $large" [r lrange mylist 0 -1]
|
||||
@@ -681,8 +620,8 @@ start_server {
|
||||
|
||||
foreach {othertype otherlarge} [array get largevalue] {
|
||||
test "RPOPLPUSH with $type source and existing target $othertype" {
|
||||
create_$type srclist "a b c $large"
|
||||
create_$othertype dstlist "$otherlarge"
|
||||
create_list srclist "a b c $large"
|
||||
create_list dstlist "$otherlarge"
|
||||
assert_equal $large [r rpoplpush srclist dstlist]
|
||||
assert_equal c [r rpoplpush srclist dstlist]
|
||||
assert_equal "a b" [r lrange srclist 0 -1]
|
||||
@@ -691,7 +630,7 @@ start_server {
|
||||
# When we rpoplpush'ed a large value, dstlist should be
|
||||
# converted to the same encoding as srclist.
|
||||
if {$type eq "linkedlist"} {
|
||||
assert_encoding linkedlist dstlist
|
||||
assert_encoding quicklist dstlist
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -713,7 +652,7 @@ start_server {
|
||||
}
|
||||
|
||||
test {RPOPLPUSH against non list dst key} {
|
||||
create_ziplist srclist {a b c d}
|
||||
create_list srclist {a b c d}
|
||||
r set dstlist x
|
||||
assert_error WRONGTYPE* {r rpoplpush srclist dstlist}
|
||||
assert_type string dstlist
|
||||
@@ -727,7 +666,7 @@ start_server {
|
||||
|
||||
foreach {type large} [array get largevalue] {
|
||||
test "Basic LPOP/RPOP - $type" {
|
||||
create_$type mylist "$large 1 2"
|
||||
create_list mylist "$large 1 2"
|
||||
assert_equal $large [r lpop mylist]
|
||||
assert_equal 2 [r rpop mylist]
|
||||
assert_equal 1 [r lpop mylist]
|
||||
@@ -745,7 +684,7 @@ start_server {
|
||||
assert_error WRONGTYPE* {r rpop notalist}
|
||||
}
|
||||
|
||||
foreach {type num} {ziplist 250 linkedlist 500} {
|
||||
foreach {type num} {quicklist 250 quicklist 500} {
|
||||
test "Mass RPOP/LPOP - $type" {
|
||||
r del mylist
|
||||
set sum1 0
|
||||
@@ -765,24 +704,24 @@ start_server {
|
||||
|
||||
foreach {type large} [array get largevalue] {
|
||||
test "LRANGE basics - $type" {
|
||||
create_$type mylist "$large 1 2 3 4 5 6 7 8 9"
|
||||
create_list mylist "$large 1 2 3 4 5 6 7 8 9"
|
||||
assert_equal {1 2 3 4 5 6 7 8} [r lrange mylist 1 -2]
|
||||
assert_equal {7 8 9} [r lrange mylist -3 -1]
|
||||
assert_equal {4} [r lrange mylist 4 4]
|
||||
}
|
||||
|
||||
test "LRANGE inverted indexes - $type" {
|
||||
create_$type mylist "$large 1 2 3 4 5 6 7 8 9"
|
||||
create_list mylist "$large 1 2 3 4 5 6 7 8 9"
|
||||
assert_equal {} [r lrange mylist 6 2]
|
||||
}
|
||||
|
||||
test "LRANGE out of range indexes including the full list - $type" {
|
||||
create_$type mylist "$large 1 2 3"
|
||||
create_list mylist "$large 1 2 3"
|
||||
assert_equal "$large 1 2 3" [r lrange mylist -1000 1000]
|
||||
}
|
||||
|
||||
test "LRANGE out of range negative end index - $type" {
|
||||
create_$type mylist "$large 1 2 3"
|
||||
create_list mylist "$large 1 2 3"
|
||||
assert_equal $large [r lrange mylist 0 -4]
|
||||
assert_equal {} [r lrange mylist 0 -5]
|
||||
}
|
||||
@@ -796,7 +735,7 @@ start_server {
|
||||
proc trim_list {type min max} {
|
||||
upvar 1 large large
|
||||
r del mylist
|
||||
create_$type mylist "1 2 3 4 $large"
|
||||
create_list mylist "1 2 3 4 $large"
|
||||
r ltrim mylist $min $max
|
||||
r lrange mylist 0 -1
|
||||
}
|
||||
@@ -825,7 +764,7 @@ start_server {
|
||||
|
||||
foreach {type large} [array get largevalue] {
|
||||
test "LSET - $type" {
|
||||
create_$type mylist "99 98 $large 96 95"
|
||||
create_list mylist "99 98 $large 96 95"
|
||||
r lset mylist 1 foo
|
||||
r lset mylist -1 bar
|
||||
assert_equal "99 foo $large 96 bar" [r lrange mylist 0 -1]
|
||||
@@ -847,7 +786,7 @@ start_server {
|
||||
|
||||
foreach {type e} [array get largevalue] {
|
||||
test "LREM remove all the occurrences - $type" {
|
||||
create_$type mylist "$e foo bar foobar foobared zap bar test foo"
|
||||
create_list mylist "$e foo bar foobar foobared zap bar test foo"
|
||||
assert_equal 2 [r lrem mylist 0 bar]
|
||||
assert_equal "$e foo foobar foobared zap test foo" [r lrange mylist 0 -1]
|
||||
}
|
||||
@@ -863,7 +802,7 @@ start_server {
|
||||
}
|
||||
|
||||
test "LREM starting from tail with negative count - $type" {
|
||||
create_$type mylist "$e foo bar foobar foobared zap bar test foo foo"
|
||||
create_list mylist "$e foo bar foobar foobared zap bar test foo foo"
|
||||
assert_equal 1 [r lrem mylist -1 bar]
|
||||
assert_equal "$e foo bar foobar foobared zap test foo foo" [r lrange mylist 0 -1]
|
||||
}
|
||||
@@ -874,7 +813,7 @@ start_server {
|
||||
}
|
||||
|
||||
test "LREM deleting objects that may be int encoded - $type" {
|
||||
create_$type myotherlist "$e 1 2 3"
|
||||
create_list myotherlist "$e 1 2 3"
|
||||
assert_equal 1 [r lrem myotherlist 1 2]
|
||||
assert_equal 3 [r llen myotherlist]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user