]>
git.saurik.com Git - redis.git/blob - src/intset.c
a837592dc57b36b359bc88c9c0cbb3dcfbd34592
7 /* Note that these encodings are ordered, so:
8 * INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */
9 #define INTSET_ENC_INT16 (sizeof(int16_t))
10 #define INTSET_ENC_INT32 (sizeof(int32_t))
11 #define INTSET_ENC_INT64 (sizeof(int64_t))
13 /* Return the required encoding for the provided value. */
14 static uint8_t _intsetValueEncoding(int64_t v
) {
15 if (v
< INT32_MIN
|| v
> INT32_MAX
)
16 return INTSET_ENC_INT64
;
17 else if (v
< INT16_MIN
|| v
> INT16_MAX
)
18 return INTSET_ENC_INT32
;
19 return INTSET_ENC_INT16
;
22 /* Return the value at pos, given an encoding. */
23 static int64_t _intsetGetEncoded(intset
*is
, int pos
, uint8_t enc
) {
24 if (enc
== INTSET_ENC_INT64
)
25 return ((int64_t*)is
->contents
)[pos
];
26 else if (enc
== INTSET_ENC_INT32
)
27 return ((int32_t*)is
->contents
)[pos
];
28 return ((int16_t*)is
->contents
)[pos
];
31 /* Return the value at pos, using the configured encoding. */
32 static int64_t _intsetGet(intset
*is
, int pos
) {
33 return _intsetGetEncoded(is
,pos
,is
->encoding
);
36 /* Set the value at pos, using the configured encoding. */
37 static void _intsetSet(intset
*is
, int pos
, int64_t value
) {
38 if (is
->encoding
== INTSET_ENC_INT64
)
39 ((int64_t*)is
->contents
)[pos
] = value
;
40 else if (is
->encoding
== INTSET_ENC_INT32
)
41 ((int32_t*)is
->contents
)[pos
] = value
;
43 ((int16_t*)is
->contents
)[pos
] = value
;
46 /* Create an empty intset. */
47 intset
*intsetNew(void) {
48 intset
*is
= zmalloc(sizeof(intset
));
49 is
->encoding
= INTSET_ENC_INT16
;
54 /* Resize the intset */
55 static intset
*intsetResize(intset
*is
, uint32_t len
) {
56 uint32_t size
= len
*is
->encoding
;
57 is
= zrealloc(is
,sizeof(intset
)+size
);
61 static intset
*intsetUpgrade(intset
*is
, uint8_t newenc
, uint8_t extra
, uint8_t offset
) {
62 uint8_t curenc
= is
->encoding
;
63 int length
= is
->length
;
65 /* First set new encoding and resize */
66 is
->encoding
= newenc
;
67 is
= intsetResize(is
,is
->length
+extra
);
69 /* Upgrade back-to-front so we don't overwrite values */
71 _intsetSet(is
,length
+offset
,_intsetGetEncoded(is
,length
,curenc
));
75 /* Search for the position of "value". Return 1 when the value was found and
76 * sets "pos" to the position of the value within the intset. Return 0 when
77 * the value is not present in the intset and sets "pos" to the position
78 * where "value" can be inserted. */
79 static uint8_t intsetSearch(intset
*is
, int64_t value
, uint32_t *pos
) {
80 int min
= 0, max
= is
->length
-1, mid
= -1;
83 /* The value can never be found when the set is empty */
84 if (is
->length
== 0) {
88 /* Check for the case where we know we cannot find the value,
89 * but do know the insert position. */
90 if (value
> _intsetGet(is
,is
->length
-1)) {
91 if (pos
) *pos
= is
->length
;
93 } else if (value
< _intsetGet(is
,0)) {
101 cur
= _intsetGet(is
,mid
);
104 } else if (value
< cur
) {
120 static void intsetMoveTail(intset
*is
, uint32_t from
, uint32_t to
) {
122 uint32_t bytes
= is
->length
-from
;
123 if (is
->encoding
== INTSET_ENC_INT64
) {
124 src
= (int64_t*)is
->contents
+from
;
125 dst
= (int64_t*)is
->contents
+to
;
126 bytes
*= sizeof(int64_t);
127 } else if (is
->encoding
== INTSET_ENC_INT32
) {
128 src
= (int32_t*)is
->contents
+from
;
129 dst
= (int32_t*)is
->contents
+to
;
130 bytes
*= sizeof(int32_t);
132 src
= (int16_t*)is
->contents
+from
;
133 dst
= (int16_t*)is
->contents
+to
;
134 bytes
*= sizeof(int16_t);
136 memmove(dst
,src
,bytes
);
139 /* Insert an integer in the intset */
140 intset
*intsetAdd(intset
*is
, int64_t value
, uint8_t *success
) {
141 uint8_t valenc
= _intsetValueEncoding(value
);
142 uint32_t pos
, offset
;
143 if (success
) *success
= 1;
145 /* Upgrade encoding if necessary. If we need to upgrade, we know that
146 * this value should be either appended (if > 0) or prepended (if < 0),
147 * because it lies outside the range of existing values. */
148 if (valenc
> is
->encoding
) {
149 offset
= value
< 0 ? 1 : 0;
150 is
= intsetUpgrade(is
,valenc
,1,offset
);
151 pos
= (value
< 0) ? 0 : is
->length
;
153 /* Abort if the value is already present in the set.
154 * This call will populate "pos" with the right position to insert
155 * the value when it cannot be found. */
156 if (intsetSearch(is
,value
,&pos
)) {
157 if (success
) *success
= 0;
161 is
= intsetResize(is
,is
->length
+1);
162 if (pos
< is
->length
) intsetMoveTail(is
,pos
,pos
+1);
165 _intsetSet(is
,pos
,value
);
170 /* Delete integer from intset */
171 intset
*intsetRemove(intset
*is
, int64_t value
, uint8_t *success
) {
172 uint8_t valenc
= _intsetValueEncoding(value
);
174 if (success
) *success
= 0;
176 if (valenc
<= is
->encoding
&& intsetSearch(is
,value
,&pos
)) {
177 /* We know we can delete */
178 if (success
) *success
= 1;
180 /* Overwrite value with tail and update length */
181 if (pos
< (is
->length
-1)) intsetMoveTail(is
,pos
+1,pos
);
182 is
= intsetResize(is
,is
->length
-1);
188 /* Determine whether a value belongs to this set */
189 uint8_t intsetFind(intset
*is
, int64_t value
) {
190 uint8_t valenc
= _intsetValueEncoding(value
);
191 return valenc
<= is
->encoding
&& intsetSearch(is
,value
,NULL
);
194 /* Return random member */
195 int64_t intsetRandom(intset
*is
) {
196 return _intsetGet(is
,rand()%is
->length
);
199 /* Sets the value to the value at the given position. When this position is
200 * out of range the function returns 0, when in range it returns 1. */
201 uint8_t intsetGet(intset
*is
, uint32_t pos
, int64_t *value
) {
202 if (pos
< is
->length
) {
203 *value
= _intsetGet(is
,pos
);
209 /* Return intset length */
210 uint32_t intsetLen(intset
*is
) {
214 #ifdef INTSET_TEST_MAIN
215 #include <sys/time.h>
217 void intsetRepr(intset
*is
) {
219 for (i
= 0; i
< is
->length
; i
++) {
220 printf("%lld\n", (uint64_t)_intsetGet(is
,i
));
225 void error(char *err
) {
234 long long usec(void) {
236 gettimeofday(&tv
,NULL
);
237 return (((long long)tv
.tv_sec
)*1000000)+tv
.tv_usec
;
240 #define assert(_e) ((_e)?(void)0:(_assert(#_e,__FILE__,__LINE__),exit(1)))
241 void _assert(char *estr
, char *file
, int line
) {
242 printf("\n\n=== ASSERTION FAILED ===\n");
243 printf("==> %s:%d '%s' is not true\n",file
,line
,estr
);
246 intset
*createSet(int bits
, int size
) {
247 uint64_t mask
= (1<<bits
)-1;
249 intset
*is
= intsetNew();
251 for (i
= 0; i
< size
; i
++) {
253 value
= (rand()*rand()) & mask
;
255 value
= rand() & mask
;
257 is
= intsetAdd(is
,value
,NULL
);
262 void checkConsistency(intset
*is
) {
265 for (i
= 0; i
< (is
->length
-1); i
++) {
266 if (is
->encoding
== INTSET_ENC_INT16
) {
267 int16_t *i16
= (int16_t*)is
->contents
;
268 assert(i16
[i
] < i16
[i
+1]);
269 } else if (is
->encoding
== INTSET_ENC_INT32
) {
270 int32_t *i32
= (int32_t*)is
->contents
;
271 assert(i32
[i
] < i32
[i
+1]);
273 int64_t *i64
= (int64_t*)is
->contents
;
274 assert(i64
[i
] < i64
[i
+1]);
279 int main(int argc
, char **argv
) {
285 printf("Value encodings: "); {
286 assert(_intsetValueEncoding(-32768) == INTSET_ENC_INT16
);
287 assert(_intsetValueEncoding(+32767) == INTSET_ENC_INT16
);
288 assert(_intsetValueEncoding(-32769) == INTSET_ENC_INT32
);
289 assert(_intsetValueEncoding(+32768) == INTSET_ENC_INT32
);
290 assert(_intsetValueEncoding(-2147483648) == INTSET_ENC_INT32
);
291 assert(_intsetValueEncoding(+2147483647) == INTSET_ENC_INT32
);
292 assert(_intsetValueEncoding(-2147483649) == INTSET_ENC_INT64
);
293 assert(_intsetValueEncoding(+2147483648) == INTSET_ENC_INT64
);
294 assert(_intsetValueEncoding(-9223372036854775808ull) == INTSET_ENC_INT64
);
295 assert(_intsetValueEncoding(+9223372036854775807ull) == INTSET_ENC_INT64
);
299 printf("Basic adding: "); {
301 is
= intsetAdd(is
,5,&success
); assert(success
);
302 is
= intsetAdd(is
,6,&success
); assert(success
);
303 is
= intsetAdd(is
,4,&success
); assert(success
);
304 is
= intsetAdd(is
,4,&success
); assert(!success
);
308 printf("Large number of random adds: "); {
311 for (i
= 0; i
< 1024; i
++) {
312 is
= intsetAdd(is
,rand()%0x800
,&success
);
313 if (success
) inserts
++;
315 assert(is
->length
== inserts
);
316 checkConsistency(is
);
320 printf("Upgrade from int16 to int32: "); {
322 is
= intsetAdd(is
,32,NULL
);
323 assert(is
->encoding
== INTSET_ENC_INT16
);
324 is
= intsetAdd(is
,65535,NULL
);
325 assert(is
->encoding
== INTSET_ENC_INT32
);
326 assert(intsetFind(is
,32));
327 assert(intsetFind(is
,65535));
328 checkConsistency(is
);
331 is
= intsetAdd(is
,32,NULL
);
332 assert(is
->encoding
== INTSET_ENC_INT16
);
333 is
= intsetAdd(is
,-65535,NULL
);
334 assert(is
->encoding
== INTSET_ENC_INT32
);
335 assert(intsetFind(is
,32));
336 assert(intsetFind(is
,-65535));
337 checkConsistency(is
);
341 printf("Upgrade from int16 to int64: "); {
343 is
= intsetAdd(is
,32,NULL
);
344 assert(is
->encoding
== INTSET_ENC_INT16
);
345 is
= intsetAdd(is
,4294967295,NULL
);
346 assert(is
->encoding
== INTSET_ENC_INT64
);
347 assert(intsetFind(is
,32));
348 assert(intsetFind(is
,4294967295));
349 checkConsistency(is
);
352 is
= intsetAdd(is
,32,NULL
);
353 assert(is
->encoding
== INTSET_ENC_INT16
);
354 is
= intsetAdd(is
,-4294967295,NULL
);
355 assert(is
->encoding
== INTSET_ENC_INT64
);
356 assert(intsetFind(is
,32));
357 assert(intsetFind(is
,-4294967295));
358 checkConsistency(is
);
362 printf("Upgrade from int32 to int64: "); {
364 is
= intsetAdd(is
,65535,NULL
);
365 assert(is
->encoding
== INTSET_ENC_INT32
);
366 is
= intsetAdd(is
,4294967295,NULL
);
367 assert(is
->encoding
== INTSET_ENC_INT64
);
368 assert(intsetFind(is
,65535));
369 assert(intsetFind(is
,4294967295));
370 checkConsistency(is
);
373 is
= intsetAdd(is
,65535,NULL
);
374 assert(is
->encoding
== INTSET_ENC_INT32
);
375 is
= intsetAdd(is
,-4294967295,NULL
);
376 assert(is
->encoding
== INTSET_ENC_INT64
);
377 assert(intsetFind(is
,65535));
378 assert(intsetFind(is
,-4294967295));
379 checkConsistency(is
);
383 printf("Stress lookups: "); {
384 long num
= 100000, size
= 10000;
387 is
= createSet(bits
,size
);
388 checkConsistency(is
);
391 for (i
= 0; i
< num
; i
++) intsetSearch(is
,rand() % ((1<<bits
)-1),NULL
);
392 printf("%ld lookups, %ld element set, %lldusec\n",num
,size
,usec()-start
);
395 printf("Stress add+delete: "); {
398 for (i
= 0; i
< 0xffff; i
++) {
400 is
= intsetAdd(is
,v1
,NULL
);
401 assert(intsetFind(is
,v1
));
404 is
= intsetRemove(is
,v2
,NULL
);
405 assert(!intsetFind(is
,v2
));
407 checkConsistency(is
);