Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
96ffb2fe
Commit
96ffb2fe
authored
Jul 02, 2010
by
Pieter Noordhuis
Browse files
merged intset code into the split files
parents
3688d7f3
400aea2b
Changes
10
Expand all
Hide whitespace changes
Inline
Side-by-side
src/Makefile
View file @
96ffb2fe
...
...
@@ -15,7 +15,7 @@ endif
CCOPT
=
$(CFLAGS)
$(CCLINK)
$(ARCH)
$(PROF)
DEBUG
?=
-g
-rdynamic
-ggdb
OBJ
=
adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o 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 vm.o pubsub.o multi.o debug.o sort.o
OBJ
=
adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o 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 vm.o pubsub.o multi.o debug.o sort.o
intset.o
BENCHOBJ
=
ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o
CLIOBJ
=
anet.o sds.o adlist.o redis-cli.o zmalloc.o linenoise.o
CHECKDUMPOBJ
=
redis-check-dump.o lzf_c.o lzf_d.o
...
...
@@ -54,6 +54,7 @@ sds.o: sds.c sds.h zmalloc.h
sha1.o
:
sha1.c sha1.h
ziplist.o
:
ziplist.c zmalloc.h ziplist.h
zipmap.o
:
zipmap.c zmalloc.h
intset.o
:
intset.c zmalloc.h
zmalloc.o
:
zmalloc.c config.h
redis-server
:
$(OBJ)
...
...
src/config.c
View file @
96ffb2fe
...
...
@@ -199,6 +199,8 @@ void loadServerConfig(char *filename) {
server
.
list_max_ziplist_entries
=
memtoll
(
argv
[
1
],
NULL
);
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"list-max-ziplist-value"
)
&&
argc
==
2
){
server
.
list_max_ziplist_value
=
memtoll
(
argv
[
1
],
NULL
);
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"set-max-intset-entries"
)
&&
argc
==
2
){
server
.
set_max_intset_entries
=
memtoll
(
argv
[
1
],
NULL
);
}
else
{
err
=
"Bad directive or wrong number of arguments"
;
goto
loaderr
;
}
...
...
src/intset.c
0 → 100644
View file @
96ffb2fe
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "intset.h"
#include "zmalloc.h"
/* Note that these encodings are ordered, so:
* INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */
#define INTSET_ENC_INT16 (sizeof(int16_t))
#define INTSET_ENC_INT32 (sizeof(int32_t))
#define INTSET_ENC_INT64 (sizeof(int64_t))
/* Accessors for each type of encoding */
#define INTSET_VALUE_ENCODING(__val) (((__val) < INT32_MIN || (__val) > INT32_MAX) ? \
INTSET_ENC_INT64 : (((__val) < INT16_MIN || (__val) > INT16_MAX) ? \
INTSET_ENC_INT32 : INTSET_ENC_INT16))
#define INTSET_GET_ENCODED(__is,__pos,__enc) ((__enc == INTSET_ENC_INT64) ? \
((int64_t*)(__is)->contents)[__pos] : ((__enc == INTSET_ENC_INT32) ? \
((int32_t*)(__is)->contents)[__pos] : ((int16_t*)(__is)->contents)[__pos]))
#define INTSET_GET(__is,__pos) (INTSET_GET_ENCODED(__is,__pos,(__is)->encoding))
#define INTSET_SET(__is,__pos,__val) { \
if ((__is)->encoding == INTSET_ENC_INT64) \
((int64_t*)(__is)->contents)[__pos] = (__val); \
else if ((__is)->encoding == INTSET_ENC_INT32) \
((int32_t*)(__is)->contents)[__pos] = (__val); \
else \
((int16_t*)(__is)->contents)[__pos] = (__val); }
/* Create an empty intset. */
intset
*
intsetNew
(
void
)
{
intset
*
is
=
zmalloc
(
sizeof
(
intset
));
is
->
encoding
=
INTSET_ENC_INT16
;
is
->
length
=
0
;
return
is
;
}
/* Resize the intset */
static
intset
*
intsetResize
(
intset
*
is
,
uint32_t
len
)
{
uint32_t
size
=
len
*
is
->
encoding
;
is
=
zrealloc
(
is
,
sizeof
(
intset
)
+
size
);
return
is
;
}
static
intset
*
intsetUpgrade
(
intset
*
is
,
uint8_t
newenc
,
uint8_t
extra
,
uint8_t
offset
)
{
uint8_t
curenc
=
is
->
encoding
;
int
length
=
is
->
length
;
/* First set new encoding and resize */
is
->
encoding
=
newenc
;
is
=
intsetResize
(
is
,
is
->
length
+
extra
);
/* Upgrade back-to-front so we don't overwrite values */
while
(
length
--
)
INTSET_SET
(
is
,
length
+
offset
,
INTSET_GET_ENCODED
(
is
,
length
,
curenc
));
return
is
;
}
/* Search for the position of "value". Return 1 when the value was found and
* sets "pos" to the position of the value within the intset. Return 0 when
* the value is not present in the intset and sets "pos" to the position
* where "value" can be inserted. */
static
uint8_t
intsetSearch
(
intset
*
is
,
int64_t
value
,
uint32_t
*
pos
)
{
int
min
=
0
,
max
=
is
->
length
-
1
,
mid
=
-
1
;
int64_t
cur
=
-
1
;
/* The value can never be found when the set is empty */
if
(
is
->
length
==
0
)
{
if
(
pos
)
*
pos
=
0
;
return
0
;
}
else
{
/* Check for the case where we know we cannot find the value,
* but do know the insert position. */
if
(
value
>
INTSET_GET
(
is
,
is
->
length
-
1
))
{
if
(
pos
)
*
pos
=
is
->
length
;
return
0
;
}
else
if
(
value
<
INTSET_GET
(
is
,
0
))
{
if
(
pos
)
*
pos
=
0
;
return
0
;
}
}
while
(
max
>=
min
)
{
mid
=
(
min
+
max
)
/
2
;
cur
=
INTSET_GET
(
is
,
mid
);
if
(
value
>
cur
)
{
min
=
mid
+
1
;
}
else
if
(
value
<
cur
)
{
max
=
mid
-
1
;
}
else
{
break
;
}
}
if
(
value
==
cur
)
{
if
(
pos
)
*
pos
=
mid
;
return
1
;
}
else
{
if
(
pos
)
*
pos
=
min
;
return
0
;
}
}
static
void
intsetMoveTail
(
intset
*
is
,
uint32_t
from
,
uint32_t
to
)
{
void
*
src
,
*
dst
;
uint32_t
bytes
=
is
->
length
-
from
;
if
(
is
->
encoding
==
INTSET_ENC_INT64
)
{
src
=
(
int64_t
*
)
is
->
contents
+
from
;
dst
=
(
int64_t
*
)
is
->
contents
+
to
;
bytes
*=
sizeof
(
int64_t
);
}
else
if
(
is
->
encoding
==
INTSET_ENC_INT32
)
{
src
=
(
int32_t
*
)
is
->
contents
+
from
;
dst
=
(
int32_t
*
)
is
->
contents
+
to
;
bytes
*=
sizeof
(
int32_t
);
}
else
{
src
=
(
int16_t
*
)
is
->
contents
+
from
;
dst
=
(
int16_t
*
)
is
->
contents
+
to
;
bytes
*=
sizeof
(
int16_t
);
}
memmove
(
dst
,
src
,
bytes
);
}
/* Insert an integer in the intset */
intset
*
intsetAdd
(
intset
*
is
,
int64_t
value
,
uint8_t
*
success
)
{
uint8_t
valenc
=
INTSET_VALUE_ENCODING
(
value
);
uint32_t
pos
,
offset
;
if
(
success
)
*
success
=
1
;
/* Upgrade encoding if necessary. If we need to upgrade, we know that
* this value should be either appended (if > 0) or prepended (if < 0),
* because it lies outside the range of existing values. */
if
(
valenc
>
is
->
encoding
)
{
offset
=
value
<
0
?
1
:
0
;
is
=
intsetUpgrade
(
is
,
valenc
,
1
,
offset
);
pos
=
(
value
<
0
)
?
0
:
is
->
length
;
}
else
{
/* Abort if the value is already present in the set.
* This call will populate "pos" with the right position to insert
* the value when it cannot be found. */
if
(
intsetSearch
(
is
,
value
,
&
pos
))
{
if
(
success
)
*
success
=
0
;
return
is
;
}
is
=
intsetResize
(
is
,
is
->
length
+
1
);
if
(
pos
<
is
->
length
)
intsetMoveTail
(
is
,
pos
,
pos
+
1
);
}
INTSET_SET
(
is
,
pos
,
value
);
is
->
length
++
;
return
is
;
}
/* Delete integer from intset */
intset
*
intsetRemove
(
intset
*
is
,
int64_t
value
,
uint8_t
*
success
)
{
uint8_t
valenc
=
INTSET_VALUE_ENCODING
(
value
);
uint32_t
pos
;
if
(
success
)
*
success
=
0
;
if
(
valenc
<=
is
->
encoding
&&
intsetSearch
(
is
,
value
,
&
pos
))
{
/* We know we can delete */
if
(
success
)
*
success
=
1
;
/* Overwrite value with tail and update length */
if
(
pos
<
(
is
->
length
-
1
))
intsetMoveTail
(
is
,
pos
+
1
,
pos
);
is
=
intsetResize
(
is
,
is
->
length
-
1
);
is
->
length
--
;
}
return
is
;
}
/* Determine whether a value belongs to this set */
uint8_t
intsetFind
(
intset
*
is
,
int64_t
value
)
{
uint8_t
valenc
=
INTSET_VALUE_ENCODING
(
value
);
return
valenc
<=
is
->
encoding
&&
intsetSearch
(
is
,
value
,
NULL
);
}
/* Return random member */
int64_t
intsetRandom
(
intset
*
is
)
{
return
INTSET_GET
(
is
,
rand
()
%
is
->
length
);
}
/* Sets the value to the value at the given position. When this position is
* out of range the function returns 0, when in range it returns 1. */
uint8_t
intsetGet
(
intset
*
is
,
uint32_t
pos
,
int64_t
*
value
)
{
if
(
pos
<
is
->
length
)
{
*
value
=
INTSET_GET
(
is
,
pos
);
return
1
;
}
return
0
;
}
/* Return intset length */
uint32_t
intsetLen
(
intset
*
is
)
{
return
is
->
length
;
}
#ifdef INTSET_TEST_MAIN
#include <sys/time.h>
void
intsetRepr
(
intset
*
is
)
{
int
i
;
for
(
i
=
0
;
i
<
is
->
length
;
i
++
)
{
printf
(
"%lld
\n
"
,
(
uint64_t
)
INTSET_GET
(
is
,
i
));
}
printf
(
"
\n
"
);
}
void
error
(
char
*
err
)
{
printf
(
"%s
\n
"
,
err
);
exit
(
1
);
}
void
ok
(
void
)
{
printf
(
"OK
\n
"
);
}
long
long
usec
(
void
)
{
struct
timeval
tv
;
gettimeofday
(
&
tv
,
NULL
);
return
(((
long
long
)
tv
.
tv_sec
)
*
1000000
)
+
tv
.
tv_usec
;
}
#define assert(_e) ((_e)?(void)0:(_assert(#_e,__FILE__,__LINE__),exit(1)))
void
_assert
(
char
*
estr
,
char
*
file
,
int
line
)
{
printf
(
"
\n\n
=== ASSERTION FAILED ===
\n
"
);
printf
(
"==> %s:%d '%s' is not true
\n
"
,
file
,
line
,
estr
);
}
intset
*
createSet
(
int
bits
,
int
size
)
{
uint64_t
mask
=
(
1
<<
bits
)
-
1
;
uint64_t
i
,
value
;
intset
*
is
=
intsetNew
();
for
(
i
=
0
;
i
<
size
;
i
++
)
{
if
(
bits
>
32
)
{
value
=
(
rand
()
*
rand
())
&
mask
;
}
else
{
value
=
rand
()
&
mask
;
}
is
=
intsetAdd
(
is
,
value
,
NULL
);
}
return
is
;
}
void
checkConsistency
(
intset
*
is
)
{
int
i
;
for
(
i
=
0
;
i
<
(
is
->
length
-
1
);
i
++
)
{
if
(
is
->
encoding
==
INTSET_ENC_INT16
)
{
int16_t
*
i16
=
(
int16_t
*
)
is
->
contents
;
assert
(
i16
[
i
]
<
i16
[
i
+
1
]);
}
else
if
(
is
->
encoding
==
INTSET_ENC_INT32
)
{
int32_t
*
i32
=
(
int32_t
*
)
is
->
contents
;
assert
(
i32
[
i
]
<
i32
[
i
+
1
]);
}
else
{
int64_t
*
i64
=
(
int64_t
*
)
is
->
contents
;
assert
(
i64
[
i
]
<
i64
[
i
+
1
]);
}
}
}
int
main
(
int
argc
,
char
**
argv
)
{
uint8_t
success
;
int
i
;
intset
*
is
;
sranddev
();
printf
(
"Value encodings: "
);
{
assert
(
INTSET_VALUE_ENCODING
(
-
32768
)
==
INTSET_ENC_INT16
);
assert
(
INTSET_VALUE_ENCODING
(
+
32767
)
==
INTSET_ENC_INT16
);
assert
(
INTSET_VALUE_ENCODING
(
-
32769
)
==
INTSET_ENC_INT32
);
assert
(
INTSET_VALUE_ENCODING
(
+
32768
)
==
INTSET_ENC_INT32
);
assert
(
INTSET_VALUE_ENCODING
(
-
2147483648
)
==
INTSET_ENC_INT32
);
assert
(
INTSET_VALUE_ENCODING
(
+
2147483647
)
==
INTSET_ENC_INT32
);
assert
(
INTSET_VALUE_ENCODING
(
-
2147483649
)
==
INTSET_ENC_INT64
);
assert
(
INTSET_VALUE_ENCODING
(
+
2147483648
)
==
INTSET_ENC_INT64
);
assert
(
INTSET_VALUE_ENCODING
(
-
9223372036854775808ull
)
==
INTSET_ENC_INT64
);
assert
(
INTSET_VALUE_ENCODING
(
+
9223372036854775807ull
)
==
INTSET_ENC_INT64
);
ok
();
}
printf
(
"Basic adding: "
);
{
is
=
intsetNew
();
is
=
intsetAdd
(
is
,
5
,
&
success
);
assert
(
success
);
is
=
intsetAdd
(
is
,
6
,
&
success
);
assert
(
success
);
is
=
intsetAdd
(
is
,
4
,
&
success
);
assert
(
success
);
is
=
intsetAdd
(
is
,
4
,
&
success
);
assert
(
!
success
);
ok
();
}
printf
(
"Large number of random adds: "
);
{
int
inserts
=
0
;
is
=
intsetNew
();
for
(
i
=
0
;
i
<
1024
;
i
++
)
{
is
=
intsetAdd
(
is
,
rand
()
%
0x800
,
&
success
);
if
(
success
)
inserts
++
;
}
assert
(
is
->
length
==
inserts
);
checkConsistency
(
is
);
ok
();
}
printf
(
"Upgrade from int16 to int32: "
);
{
is
=
intsetNew
();
is
=
intsetAdd
(
is
,
32
,
NULL
);
assert
(
is
->
encoding
==
INTSET_ENC_INT16
);
is
=
intsetAdd
(
is
,
65535
,
NULL
);
assert
(
is
->
encoding
==
INTSET_ENC_INT32
);
assert
(
intsetFind
(
is
,
32
));
assert
(
intsetFind
(
is
,
65535
));
checkConsistency
(
is
);
is
=
intsetNew
();
is
=
intsetAdd
(
is
,
32
,
NULL
);
assert
(
is
->
encoding
==
INTSET_ENC_INT16
);
is
=
intsetAdd
(
is
,
-
65535
,
NULL
);
assert
(
is
->
encoding
==
INTSET_ENC_INT32
);
assert
(
intsetFind
(
is
,
32
));
assert
(
intsetFind
(
is
,
-
65535
));
checkConsistency
(
is
);
ok
();
}
printf
(
"Upgrade from int16 to int64: "
);
{
is
=
intsetNew
();
is
=
intsetAdd
(
is
,
32
,
NULL
);
assert
(
is
->
encoding
==
INTSET_ENC_INT16
);
is
=
intsetAdd
(
is
,
4294967295
,
NULL
);
assert
(
is
->
encoding
==
INTSET_ENC_INT64
);
assert
(
intsetFind
(
is
,
32
));
assert
(
intsetFind
(
is
,
4294967295
));
checkConsistency
(
is
);
is
=
intsetNew
();
is
=
intsetAdd
(
is
,
32
,
NULL
);
assert
(
is
->
encoding
==
INTSET_ENC_INT16
);
is
=
intsetAdd
(
is
,
-
4294967295
,
NULL
);
assert
(
is
->
encoding
==
INTSET_ENC_INT64
);
assert
(
intsetFind
(
is
,
32
));
assert
(
intsetFind
(
is
,
-
4294967295
));
checkConsistency
(
is
);
ok
();
}
printf
(
"Upgrade from int32 to int64: "
);
{
is
=
intsetNew
();
is
=
intsetAdd
(
is
,
65535
,
NULL
);
assert
(
is
->
encoding
==
INTSET_ENC_INT32
);
is
=
intsetAdd
(
is
,
4294967295
,
NULL
);
assert
(
is
->
encoding
==
INTSET_ENC_INT64
);
assert
(
intsetFind
(
is
,
65535
));
assert
(
intsetFind
(
is
,
4294967295
));
checkConsistency
(
is
);
is
=
intsetNew
();
is
=
intsetAdd
(
is
,
65535
,
NULL
);
assert
(
is
->
encoding
==
INTSET_ENC_INT32
);
is
=
intsetAdd
(
is
,
-
4294967295
,
NULL
);
assert
(
is
->
encoding
==
INTSET_ENC_INT64
);
assert
(
intsetFind
(
is
,
65535
));
assert
(
intsetFind
(
is
,
-
4294967295
));
checkConsistency
(
is
);
ok
();
}
printf
(
"Stress lookups: "
);
{
long
num
=
100000
,
size
=
10000
;
int
i
,
bits
=
20
;
long
long
start
;
is
=
createSet
(
bits
,
size
);
checkConsistency
(
is
);
start
=
usec
();
for
(
i
=
0
;
i
<
num
;
i
++
)
intsetSearch
(
is
,
rand
()
%
((
1
<<
bits
)
-
1
),
NULL
);
printf
(
"%ld lookups, %ld element set, %lldusec
\n
"
,
num
,
size
,
usec
()
-
start
);
}
printf
(
"Stress add+delete: "
);
{
int
i
,
v1
,
v2
;
is
=
intsetNew
();
for
(
i
=
0
;
i
<
0xffff
;
i
++
)
{
v1
=
rand
()
%
0xfff
;
is
=
intsetAdd
(
is
,
v1
,
NULL
);
assert
(
intsetFind
(
is
,
v1
));
v2
=
rand
()
%
0xfff
;
is
=
intsetRemove
(
is
,
v2
,
NULL
);
assert
(
!
intsetFind
(
is
,
v2
));
}
checkConsistency
(
is
);
ok
();
}
}
#endif
src/intset.h
0 → 100644
View file @
96ffb2fe
#ifndef __INTSET_H
#define __INTSET_H
#include <stdint.h>
typedef
struct
intset
{
uint32_t
encoding
;
uint32_t
length
;
int8_t
contents
[];
}
intset
;
intset
*
intsetNew
(
void
);
intset
*
intsetAdd
(
intset
*
is
,
int64_t
value
,
uint8_t
*
success
);
intset
*
intsetRemove
(
intset
*
is
,
int64_t
value
,
uint8_t
*
success
);
uint8_t
intsetFind
(
intset
*
is
,
int64_t
value
);
int64_t
intsetRandom
(
intset
*
is
);
uint8_t
intsetGet
(
intset
*
is
,
uint32_t
pos
,
int64_t
*
value
);
uint32_t
intsetLen
(
intset
*
is
);
#endif // __INTSET_H
src/object.c
View file @
96ffb2fe
...
...
@@ -73,7 +73,16 @@ robj *createZiplistObject(void) {
robj
*
createSetObject
(
void
)
{
dict
*
d
=
dictCreate
(
&
setDictType
,
NULL
);
return
createObject
(
REDIS_SET
,
d
);
robj
*
o
=
createObject
(
REDIS_SET
,
d
);
o
->
encoding
=
REDIS_ENCODING_HT
;
return
o
;
}
robj
*
createIntsetObject
(
void
)
{
intset
*
is
=
intsetNew
();
robj
*
o
=
createObject
(
REDIS_SET
,
is
);
o
->
encoding
=
REDIS_ENCODING_INTSET
;
return
o
;
}
robj
*
createHashObject
(
void
)
{
...
...
@@ -114,7 +123,16 @@ void freeListObject(robj *o) {
}
void
freeSetObject
(
robj
*
o
)
{
dictRelease
((
dict
*
)
o
->
ptr
);
switch
(
o
->
encoding
)
{
case
REDIS_ENCODING_HT
:
dictRelease
((
dict
*
)
o
->
ptr
);
break
;
case
REDIS_ENCODING_INTSET
:
zfree
(
o
->
ptr
);
break
;
default:
redisPanic
(
"Unknown set encoding type"
);
}
}
void
freeZsetObject
(
robj
*
o
)
{
...
...
@@ -356,7 +374,7 @@ int getLongLongFromObject(robj *o, long long *target) {
}
}
*
target
=
value
;
if
(
target
)
*
target
=
value
;
return
REDIS_OK
;
}
...
...
@@ -400,6 +418,7 @@ char *strEncoding(int encoding) {
case
REDIS_ENCODING_ZIPMAP
:
return
"zipmap"
;
case
REDIS_ENCODING_LINKEDLIST
:
return
"linkedlist"
;
case
REDIS_ENCODING_ZIPLIST
:
return
"ziplist"
;
case
REDIS_ENCODING_INTSET
:
return
"intset"
;
default:
return
"unknown"
;
}
}
src/rdb.c
View file @
96ffb2fe
...
...
@@ -260,17 +260,29 @@ int rdbSaveObject(FILE *fp, robj *o) {
}
}
else
if
(
o
->
type
==
REDIS_SET
)
{
/* Save a set value */
dict
*
set
=
o
->
ptr
;
dictIterator
*
di
=
dictGetIterator
(
set
);
dictEntry
*
de
;
if
(
rdbSaveLen
(
fp
,
dictSize
(
set
))
==
-
1
)
return
-
1
;
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
robj
*
eleobj
=
dictGetEntryKey
(
de
);
if
(
o
->
encoding
==
REDIS_ENCODING_HT
)
{
dict
*
set
=
o
->
ptr
;
dictIterator
*
di
=
dictGetIterator
(
set
);
dictEntry
*
de
;
if
(
rdbSaveStringObject
(
fp
,
eleobj
)
==
-
1
)
return
-
1
;
if
(
rdbSaveLen
(
fp
,
dictSize
(
set
))
==
-
1
)
return
-
1
;
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
robj
*
eleobj
=
dictGetEntryKey
(
de
);
if
(
rdbSaveStringObject
(
fp
,
eleobj
)
==
-
1
)
return
-
1
;
}
dictReleaseIterator
(
di
);
}
else
if
(
o
->
encoding
==
REDIS_ENCODING_INTSET
)
{
intset
*
is
=
o
->
ptr
;
long
long
llval
;
int
i
=
0
;
if
(
rdbSaveLen
(
fp
,
intsetLen
(
is
))
==
-
1
)
return
-
1
;
while
(
intsetGet
(
is
,
i
++
,
&
llval
))
{
if
(
rdbSaveLongLongAsStringObject
(
fp
,
llval
)
==
-
1
)
return
-
1
;
}
}
else
{
redisPanic
(
"Unknown set encoding"
);
}
dictReleaseIterator
(
di
);
}
else
if
(
o
->
type
==
REDIS_ZSET
)
{
/* Save a set value */
zset
*
zs
=
o
->
ptr
;
...
...
@@ -628,6 +640,7 @@ int rdbLoadDoubleValue(FILE *fp, double *val) {
robj
*
rdbLoadObject
(
int
type
,
FILE
*
fp
)
{
robj
*
o
,
*
ele
,
*
dec
;
size_t
len
;
unsigned
int
i
;
redisLog
(
REDIS_DEBUG
,
"LOADING OBJECT %d (at %d)
\n
"
,
type
,
ftell
(
fp
));
if
(
type
==
REDIS_STRING
)
{
...
...
@@ -669,16 +682,39 @@ robj *rdbLoadObject(int type, FILE *fp) {
}
else
if
(
type
==
REDIS_SET
)
{
/* Read list/set value */
if
((
len
=
rdbLoadLen
(
fp
,
NULL
))
==
REDIS_RDB_LENERR
)
return
NULL
;
o
=
createSetObject
();
/* It's faster to expand the dict to the right size asap in order
* to avoid rehashing */
if
(
len
>
DICT_HT_INITIAL_SIZE
)
dictExpand
(
o
->
ptr
,
len
);
/* Use a regular set when there are too many entries. */
if
(
len
>
server
.
set_max_intset_entries
)
{
o
=
createSetObject
();
/* It's faster to expand the dict to the right size asap in order
* to avoid rehashing */
if
(
len
>
DICT_HT_INITIAL_SIZE
)
dictExpand
(
o
->
ptr
,
len
);
}
else
{
o
=
createIntsetObject
();
}
/* Load every single element of the list/set */
while
(
len
--
)
{
for
(
i
=
0
;
i
<
len
;
i
++
)
{
long
long
llval
;
if
((
ele
=
rdbLoadEncodedStringObject
(
fp
))
==
NULL
)
return
NULL
;
ele
=
tryObjectEncoding
(
ele
);
dictAdd
((
dict
*
)
o
->
ptr
,
ele
,
NULL
);
if
(
o
->
encoding
==
REDIS_ENCODING_INTSET
)
{
/* Fetch integer value from element */
if
(
getLongLongFromObject
(
ele
,
&
llval
)
==
REDIS_OK
)
{
o
->
ptr
=
intsetAdd
(
o
->
ptr
,
llval
,
NULL
);
}
else
{
setTypeConvert
(
o
,
REDIS_ENCODING_HT
);
dictExpand
(
o
->
ptr
,
len
);
}
}
/* This will also be called when the set was just converted
* to regular hashtable encoded set */
if
(
o
->
encoding
==
REDIS_ENCODING_HT
)
{
dictAdd
((
dict
*
)
o
->
ptr
,
ele
,
NULL
);
}
}
}
else
if
(
type
==
REDIS_ZSET
)
{
/* Read list/set value */
...
...
src/redis.c
View file @
96ffb2fe
...
...
@@ -731,6 +731,7 @@ void initServerConfig() {
server
.
hash_max_zipmap_value
=
REDIS_HASH_MAX_ZIPMAP_VALUE
;
server
.
list_max_ziplist_entries
=
REDIS_LIST_MAX_ZIPLIST_ENTRIES
;
server
.
list_max_ziplist_value
=
REDIS_LIST_MAX_ZIPLIST_VALUE
;
server
.
set_max_intset_entries
=
REDIS_SET_MAX_INTSET_ENTRIES
;
server
.
shutdown_asap
=
0
;
resetServerSaveParams
();
...
...
src/redis.h
View file @
96ffb2fe
...
...
@@ -25,6 +25,7 @@
#include "anet.h"
/* Networking the easy way */
#include "zipmap.h"
/* Compact string -> string data structure */
#include "ziplist.h"
/* Compact list data structure */
#include "intset.h"
/* Compact integer set structure */
#include "version.h"
/* Error codes */
...
...
@@ -81,6 +82,7 @@
#define REDIS_ENCODING_ZIPMAP 3
/* Encoded as zipmap */
#define REDIS_ENCODING_LINKEDLIST 4
/* Encoded as regular linked list */
#define REDIS_ENCODING_ZIPLIST 5
/* Encoded as ziplist */
#define REDIS_ENCODING_INTSET 6
/* Encoded as intset */
/* Object types only used for dumping to disk */
#define REDIS_EXPIRETIME 253
...
...
@@ -187,6 +189,7 @@
#define REDIS_HASH_MAX_ZIPMAP_VALUE 512
#define REDIS_LIST_MAX_ZIPLIST_ENTRIES 1024
#define REDIS_LIST_MAX_ZIPLIST_VALUE 32
#define REDIS_SET_MAX_INTSET_ENTRIES 4096
/* Sets operations codes */
#define REDIS_OP_UNION 0
...
...
@@ -396,6 +399,7 @@ struct redisServer {
size_t
hash_max_zipmap_value
;
size_t
list_max_ziplist_entries
;
size_t
list_max_ziplist_value
;
size_t
set_max_intset_entries
;
/* Virtual memory state */
FILE
*
vm_fp
;
int
vm_fd
;
...
...
@@ -533,6 +537,14 @@ typedef struct {
listNode
*
ln
;
/* Entry in linked list */
}
listTypeEntry
;
/* Structure to hold set iteration abstraction. */
typedef
struct
{
robj
*
subject
;
int
encoding
;
int
ii
;
/* intset iterator */
dictIterator
*
di
;
}
setIterator
;
/* Structure to hold hash iteration abstration. Note that iteration over
* hashes involves both fields and values. Because it is possible that
* not both are required, store pointers in the iterator to avoid
...
...
@@ -631,6 +643,7 @@ robj *createStringObjectFromLongLong(long long value);
robj
*
createListObject
(
void
);
robj
*
createZiplistObject
(
void
);
robj
*
createSetObject
(
void
);
robj
*
createIntsetObject
(
void
);
robj
*
createHashObject
(
void
);
robj
*
createZsetObject
(
void
);
int
getLongFromObjectOrReply
(
redisClient
*
c
,
robj
*
o
,
long
*
target
,
const
char
*
msg
);
...
...
@@ -714,6 +727,18 @@ int dontWaitForSwappedKey(redisClient *c, robj *key);
void
handleClientsBlockedOnSwappedKey
(
redisDb
*
db
,
robj
*
key
);
vmpointer
*
vmSwapObjectBlocking
(
robj
*
val
);
/* Set data type */
robj
*
setTypeCreate
(
robj
*
value
);
int
setTypeAdd
(
robj
*
subject
,
robj
*
value
);
int
setTypeRemove
(
robj
*
subject
,
robj
*
value
);
int
setTypeIsMember
(
robj
*
subject
,
robj
*
value
);
setIterator
*
setTypeInitIterator
(
robj
*
subject
);
void
setTypeReleaseIterator
(
setIterator
*
si
);
robj
*
setTypeNext
(
setIterator
*
si
);
robj
*
setTypeRandomElement
(
robj
*
subject
);
unsigned
long
setTypeSize
(
robj
*
subject
);
void
setTypeConvert
(
robj
*
subject
,
int
enc
);
/* Hash data type */
void
convertToRealHash
(
robj
*
o
);
void
hashTypeTryConversion
(
robj
*
subject
,
robj
**
argv
,
int
start
,
int
end
);
...
...
src/t_set.c
View file @
96ffb2fe
This diff is collapsed.
Click to expand it.
tests/unit/type/set.tcl
View file @
96ffb2fe
start_server
{
tags
{
"set"
}}
{
test
{
SADD, SCARD, SISMEMBER, SMEMBERS basics
}
{
r sadd myset foo
r sadd myset bar
list
[
r scard myset
]
[
r sismember myset foo
]
\
[
r sismember myset bar
]
[
r sismember myset bla
]
\
[
lsort
[
r smembers myset
]]
}
{
2 1 1 0
{
bar foo
}}
test
{
SADD adding the same element multiple times
}
{
r sadd myset foo
r sadd myset foo
r sadd myset foo
r scard myset
}
{
2
}
start_server
{
tags
{
"set"
}
overrides
{
"set-max-intset-entries"
512
}
}
{
proc create_set
{
key entries
}
{
r del $key
foreach entry $entries
{
r sadd $key $entry
}
}
test
{
SADD, SCARD, SISMEMBER, SMEMBERS basics - regular set
}
{
create_set myset
{
foo
}
assert_encoding hashtable myset
assert_equal 1
[
r sadd myset bar
]
assert_equal 0
[
r sadd myset bar
]
assert_equal 2
[
r scard myset
]
assert_equal 1
[
r sismember myset foo
]
assert_equal 1
[
r sismember myset bar
]
assert_equal 0
[
r sismember myset bla
]
assert_equal
{
bar foo
}
[
lsort
[
r smembers myset
]]
}
test
{
SADD, SCARD, SISMEMBER, SMEMBERS basics - intset
}
{
create_set myset
{
17
}
assert_encoding intset myset
assert_equal 1
[
r sadd myset 16
]
assert_equal 0
[
r sadd myset 16
]
assert_equal 2
[
r scard myset
]
assert_equal 1
[
r sismember myset 16
]
assert_equal 1
[
r sismember myset 17
]
assert_equal 0
[
r sismember myset 18
]
assert_equal
{
16 17
}
[
lsort
[
r smembers myset
]]
}
test
{
SADD against non set
}
{
r lpush mylist foo
catch
{
r sadd mylist bar
}
err
format $err
}
{
ERR*kind*
}
test
{
SREM basics
}
{
r sadd myset ciao
r srem myset foo
lsort
[
r smembers myset
]
}
{
bar ciao
}
test
{
Mass SADD and SINTER with two sets
}
{
for
{
set i 0
}
{
$i
< 1000
}
{
incr i
}
{
assert_error ERR*kind*
{
r sadd mylist bar
}
}
test
"SADD a non-integer against an intset"
{
create_set myset
{
1 2 3
}
assert_encoding intset myset
assert_equal 1
[
r sadd myset a
]
assert_encoding hashtable myset
}
test
"SADD overflows the maximum allowed integers in an intset"
{
r del myset
for
{
set i 0
}
{
$i
< 512
}
{
incr i
}
{
r sadd myset $i
}
assert_encoding intset myset
assert_equal 1
[
r sadd myset 512
]
assert_encoding hashtable myset
}
test
"Set encoding after DEBUG RELOAD"
{
r del myintset myhashset mylargeintset
for
{
set i 0
}
{
$i
< 100
}
{
incr i
}
{
r sadd myintset $i
}
for
{
set i 0
}
{
$i
< 1280
}
{
incr i
}
{
r sadd mylargeintset $i
}
for
{
set i 0
}
{
$i
< 256
}
{
incr i
}
{
r sadd myhashset
[
format
"i%03d"
$i
]
}
assert_encoding intset myintset
assert_encoding hashtable mylargeintset
assert_encoding hashtable myhashset
r debug reload
assert_encoding intset myintset
assert_encoding hashtable mylargeintset
assert_encoding hashtable myhashset
}
test
{
SREM basics - regular set
}
{
create_set myset
{
foo bar ciao
}
assert_encoding hashtable myset
assert_equal 0
[
r srem myset qux
]
assert_equal 1
[
r srem myset foo
]
assert_equal
{
bar ciao
}
[
lsort
[
r smembers myset
]]
}
test
{
SREM basics - intset
}
{
create_set myset
{
3 4 5
}
assert_encoding intset myset
assert_equal 0
[
r srem myset 6
]
assert_equal 1
[
r srem myset 4
]
assert_equal
{
3 5
}
[
lsort
[
r smembers myset
]]
}
foreach
{
type
}
{
hashtable intset
}
{
for
{
set i 1
}
{
$i
<= 5
}
{
incr i
}
{
r del
[
format
"set%d"
$i
]
}
for
{
set i 0
}
{
$i
< 200
}
{
incr i
}
{
r sadd set1 $i
r sadd set2
[
expr $i+995
]
r sadd set2
[
expr $i+195
]
}
foreach i
{
199 195 1000 2000
}
{
r sadd set3 $i
}
for
{
set i 5
}
{
$i
< 200
}
{
incr i
}
{
r sadd set4 $i
}
lsort
[
r sinter set1 set2
]
}
{
995 996 997 998 999
}
r sadd set5 0
test
{
SUNION with two sets
}
{
lsort
[
r sunion set1 set2
]
}
[
lsort -uniq
"
[
r smembers set1
]
[
r smembers set2
]
"
]
# it is possible that a hashtable encoded only contains integers,
# because it is converted from an intset to a hashtable when a
# non-integer element is added and then removed.
if
{
$type
eq
"hashtable"
}
{
for
{
set i 1
}
{
$i
<= 5
}
{
incr i
}
{
r sadd
[
format
"set%d"
$i
]
foo
r srem
[
format
"set%d"
$i
]
foo
}
}
test
{
SINTERSTORE with two sets
}
{
r sinterstore setres set1 set2
lsort
[
r smembers setres
]
}
{
995 996 997 998 999
}
test
"Generated sets must be encoded as
$type
"
{
for
{
set i 1
}
{
$i
<= 5
}
{
incr i
}
{
assert_encoding $type
[
format
"set%d"
$i
]
}
}
test
{
SINTERSTORE with two sets, after a DEBUG RELOAD
}
{
r debug reload
r sinterstore setres set1 set2
lsort
[
r smembers setres
]
}
{
995 996 997 998 999
}
test
"SINTER with two sets -
$type
"
{
assert_equal
{
195 196 197 198 199
}
[
lsort
[
r sinter set1 set2
]]
}
test
"SINTERSTORE with two sets -
$type
"
{
r sinterstore setres set1 set2
assert_encoding intset setres
assert_equal
{
195 196 197 198 199
}
[
lsort
[
r smembers setres
]]
}
test
"SINTERSTORE with two sets, after a DEBUG RELOAD -
$type
"
{
r debug reload
r sinterstore setres set1 set2
assert_encoding intset setres
assert_equal
{
195 196 197 198 199
}
[
lsort
[
r smembers setres
]]
}
test
"SUNION with two sets -
$type
"
{
set expected
[
lsort -uniq
"
[
r smembers set1
]
[
r smembers set2
]
"
]
assert_equal $expected
[
lsort
[
r sunion set1 set2
]]
}
test
"SUNIONSTORE with two sets -
$type
"
{
r sunionstore setres set1 set2
assert_encoding intset setres
set expected
[
lsort -uniq
"
[
r smembers set1
]
[
r smembers set2
]
"
]
assert_equal $expected
[
lsort
[
r smembers setres
]]
}
test
{
SUNIONSTORE with two sets
}
{
r sunionstore setres set1 set2
lsort
[
r smembers setres
]
}
[
lsort -uniq
"
[
r smembers set1
]
[
r smembers set2
]
"
]
test
"SINTER against three sets -
$type
"
{
assert_equal
{
195 199
}
[
lsort
[
r sinter set1 set2 set3
]]
}
test
"SINTERSTORE with three sets -
$type
"
{
r sinterstore setres set1 set2 set3
assert_equal
{
195 199
}
[
r smembers setres
]
}
test
"SUNION with non existing keys -
$type
"
{
set expected
[
lsort -uniq
"
[
r smembers set1
]
[
r smembers set2
]
"
]
assert_equal $expected
[
lsort
[
r sunion nokey1 set1 set2 nokey2
]]
}
test
"SDIFF with two sets -
$type
"
{
assert_equal
{
0 1 2 3 4
}
[
lsort
[
r sdiff set1 set4
]]
}
test
{
SUNIONSTORE against non existing keys
}
{
test
"SDIFF with three sets -
$type
"
{
assert_equal
{
1 2 3 4
}
[
lsort
[
r sdiff set1 set4 set5
]]
}
test
"SDIFFSTORE with three sets -
$type
"
{
r sdiffstore setres set1 set4 set5
assert_encoding intset setres
assert_equal
{
1 2 3 4
}
[
lsort
[
r smembers setres
]]
}
}
test
"SINTER against non-set should throw error"
{
r set key1 x
assert_error
"ERR*wrong kind*"
{
r sinter key1 noset
}
}
test
"SUNION against non-set should throw error"
{
r set key1 x
assert_error
"ERR*wrong kind*"
{
r sunion key1 noset
}
}
test
"SINTERSTORE against non existing keys should delete dstkey"
{
r set setres xxx
list
[
r sunionstore setres foo111 bar222
]
[
r exists xxx
]
}
{
0 0
}
test
{
SINTER against three sets
}
{
r sadd set3 999
r sadd set3 995
r sadd set3 1000
r sadd set3 2000
lsort
[
r sinter set1 set2 set3
]
}
{
995 999
}
test
{
SINTERSTORE with three sets
}
{
r sinterstore setres set1 set2 set3
lsort
[
r smembers setres
]
}
{
995 999
}
test
{
SUNION with non existing keys
}
{
lsort
[
r sunion nokey1 set1 set2 nokey2
]
}
[
lsort -uniq
"
[
r smembers set1
]
[
r smembers set2
]
"
]
test
{
SDIFF with two sets
}
{
for
{
set i 5
}
{
$i
< 1000
}
{
incr i
}
{
r sadd set4 $i
assert_equal 0
[
r sinterstore setres foo111 bar222
]
assert_equal 0
[
r exists setres
]
}
test
"SUNIONSTORE against non existing keys should delete dstkey"
{
r set setres xxx
assert_equal 0
[
r sunionstore setres foo111 bar222
]
assert_equal 0
[
r exists setres
]
}
foreach
{
type contents
}
{
hashtable
{
a b c
}
intset
{
1 2 3
}}
{
test
"SPOP basics -
$type
"
{
create_set myset $contents
assert_encoding $type myset
assert_equal $contents
[
lsort
[
list
[
r spop myset
]
[
r spop myset
]
[
r spop myset
]]]
assert_equal 0
[
r scard myset
]
}
lsort
[
r sdiff set1 set4
]
}
{
0 1 2 3 4
}
test
{
SDIFF with three sets
}
{
r sadd set5 0
lsort
[
r sdiff set1 set4 set5
]
}
{
1 2 3 4
}
test
"SRANDMEMBER -
$type
"
{
create_set myset $contents
unset -nocomplain myset
array set myset
{}
for
{
set i 0
}
{
$i
< 100
}
{
incr i
}
{
set myset
([
r srandmember myset
])
1
}
assert_equal $contents
[
lsort
[
array names myset
]]
}
}
test
{
SDIFFSTORE with three sets
}
{
r sdiffstore sres set1 set4 set5
lsort
[
r smembers sres
]
}
{
1 2 3 4
}
proc setup_move
{}
{
r del myset3 myset4
create_set myset1
{
1 a b
}
create_set myset2
{
2 3 4
}
assert_encoding hashtable myset1
assert_encoding intset myset2
}
test
{
SPOP basics
}
{
r del myset
r sadd myset 1
r sadd myset 2
r sadd myset 3
list
[
lsort
[
list
[
r spop myset
]
[
r spop myset
]
[
r spop myset
]]]
[
r scard myset
]
}
{{
1 2 3
}
0
}
test
{
SRANDMEMBER
}
{
r del myset
r sadd myset a
r sadd myset b
r sadd myset c
unset -nocomplain myset
array set myset
{}
for
{
set i 0
}
{
$i
< 100
}
{
incr i
}
{
set myset
([
r srandmember myset
])
1
}
lsort
[
array names myset
]
}
{
a b c
}
test
{
SMOVE basics
}
{
r sadd myset1 a
r sadd myset1 b
r sadd myset1 c
r sadd myset2 x
r sadd myset2 y
r sadd myset2 z
r smove myset1 myset2 a
list
[
lsort
[
r smembers myset2
]]
[
lsort
[
r smembers myset1
]]
}
{{
a x y z
}
{
b c
}}
test
{
SMOVE non existing key
}
{
list
[
r smove myset1 myset2 foo
]
[
lsort
[
r smembers myset2
]]
[
lsort
[
r smembers myset1
]]
}
{
0
{
a x y z
}
{
b c
}}
test
{
SMOVE non existing src set
}
{
list
[
r smove noset myset2 foo
]
[
lsort
[
r smembers myset2
]]
}
{
0
{
a x y z
}}
test
{
SMOVE non existing dst set
}
{
list
[
r smove myset2 myset3 y
]
[
lsort
[
r smembers myset2
]]
[
lsort
[
r smembers myset3
]]
}
{
1
{
a x z
}
y
}
test
{
SMOVE wrong src key type
}
{
test
"SMOVE basics - from regular set to intset"
{
# move a non-integer element to an intset should convert encoding
setup_move
assert_equal 1
[
r smove myset1 myset2 a
]
assert_equal
{
1 b
}
[
lsort
[
r smembers myset1
]]
assert_equal
{
2 3 4 a
}
[
lsort
[
r smembers myset2
]]
assert_encoding hashtable myset2
# move an integer element should not convert the encoding
setup_move
assert_equal 1
[
r smove myset1 myset2 1
]
assert_equal
{
a b
}
[
lsort
[
r smembers myset1
]]
assert_equal
{
1 2 3 4
}
[
lsort
[
r smembers myset2
]]
assert_encoding intset myset2
}
test
"SMOVE basics - from intset to regular set"
{
setup_move
assert_equal 1
[
r smove myset2 myset1 2
]
assert_equal
{
1 2 a b
}
[
lsort
[
r smembers myset1
]]
assert_equal
{
3 4
}
[
lsort
[
r smembers myset2
]]
}
test
"SMOVE non existing key"
{
setup_move
assert_equal 0
[
r smove myset1 myset2 foo
]
assert_equal
{
1 a b
}
[
lsort
[
r smembers myset1
]]
assert_equal
{
2 3 4
}
[
lsort
[
r smembers myset2
]]
}
test
"SMOVE non existing src set"
{
setup_move
assert_equal 0
[
r smove noset myset2 foo
]
assert_equal
{
2 3 4
}
[
lsort
[
r smembers myset2
]]
}
test
"SMOVE from regular set to non existing destination set"
{
setup_move
assert_equal 1
[
r smove myset1 myset3 a
]
assert_equal
{
1 b
}
[
lsort
[
r smembers myset1
]]
assert_equal
{
a
}
[
lsort
[
r smembers myset3
]]
assert_encoding hashtable myset3
}
test
"SMOVE from intset to non existing destination set"
{
setup_move
assert_equal 1
[
r smove myset2 myset3 2
]
assert_equal
{
3 4
}
[
lsort
[
r smembers myset2
]]
assert_equal
{
2
}
[
lsort
[
r smembers myset3
]]
assert_encoding intset myset3
}
test
"SMOVE wrong src key type"
{
r set x 10
catch
{
r smove x myset2 foo
}
err
format $err
}
{
ERR*
}
assert_error
"ERR*wrong kind*"
{
r smove x myset2 foo
}
}
test
{
SMOVE wrong dst key type
}
{
test
"
SMOVE wrong dst key type
"
{
r set x 10
catch
{
r smove myset2 x foo
}
err
format $err
}
{
ERR*
}
assert_error
"ERR*wrong kind*"
{
r smove myset2 x foo
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment