]>
git.saurik.com Git - redis.git/blob - src/intset.c
9df7936b6ecf034eae34fc502597e62d514c3725
   8 /* Note that these encodings are ordered, so: 
   9  * INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */ 
  10 #define INTSET_ENC_INT16 (sizeof(int16_t)) 
  11 #define INTSET_ENC_INT32 (sizeof(int32_t)) 
  12 #define INTSET_ENC_INT64 (sizeof(int64_t)) 
  14 /* Return the required encoding for the provided value. */ 
  15 static uint8_t _intsetValueEncoding(int64_t v
) { 
  16     if (v 
< INT32_MIN 
|| v 
> INT32_MAX
) 
  17         return INTSET_ENC_INT64
; 
  18     else if (v 
< INT16_MIN 
|| v 
> INT16_MAX
) 
  19         return INTSET_ENC_INT32
; 
  21         return INTSET_ENC_INT16
; 
  24 /* Return the value at pos, given an encoding. */ 
  25 static int64_t _intsetGetEncoded(intset 
*is
, int pos
, uint8_t enc
) { 
  30     if (enc 
== INTSET_ENC_INT64
) { 
  31         memcpy(&v64
,((int64_t*)is
->contents
)+pos
,sizeof(v64
)); 
  34     } else if (enc 
== INTSET_ENC_INT32
) { 
  35         memcpy(&v32
,((int32_t*)is
->contents
)+pos
,sizeof(v32
)); 
  39         memcpy(&v16
,((int16_t*)is
->contents
)+pos
,sizeof(v16
)); 
  45 /* Return the value at pos, using the configured encoding. */ 
  46 static int64_t _intsetGet(intset 
*is
, int pos
) { 
  47     return _intsetGetEncoded(is
,pos
,intrev32ifbe(is
->encoding
)); 
  50 /* Set the value at pos, using the configured encoding. */ 
  51 static void _intsetSet(intset 
*is
, int pos
, int64_t value
) { 
  52     uint32_t encoding 
= intrev32ifbe(is
->encoding
); 
  54     if (encoding 
== INTSET_ENC_INT64
) { 
  55         ((int64_t*)is
->contents
)[pos
] = value
; 
  56         memrev64ifbe(((int64_t*)is
->contents
)+pos
); 
  57     } else if (encoding 
== INTSET_ENC_INT32
) { 
  58         ((int32_t*)is
->contents
)[pos
] = value
; 
  59         memrev32ifbe(((int32_t*)is
->contents
)+pos
); 
  61         ((int16_t*)is
->contents
)[pos
] = value
; 
  62         memrev16ifbe(((int16_t*)is
->contents
)+pos
); 
  66 /* Create an empty intset. */ 
  67 intset 
*intsetNew(void) { 
  68     intset 
*is 
= zmalloc(sizeof(intset
)); 
  69     is
->encoding 
= intrev32ifbe(INTSET_ENC_INT16
); 
  74 /* Resize the intset */ 
  75 static intset 
*intsetResize(intset 
*is
, uint32_t len
) { 
  76     uint32_t size 
= len
*intrev32ifbe(is
->encoding
); 
  77     is 
= zrealloc(is
,sizeof(intset
)+size
); 
  81 /* Search for the position of "value". Return 1 when the value was found and 
  82  * sets "pos" to the position of the value within the intset. Return 0 when 
  83  * the value is not present in the intset and sets "pos" to the position 
  84  * where "value" can be inserted. */ 
  85 static uint8_t intsetSearch(intset 
*is
, int64_t value
, uint32_t *pos
) { 
  86     int min 
= 0, max 
= intrev32ifbe(is
->length
)-1, mid 
= -1; 
  89     /* The value can never be found when the set is empty */ 
  90     if (intrev32ifbe(is
->length
) == 0) { 
  94         /* Check for the case where we know we cannot find the value, 
  95          * but do know the insert position. */ 
  96         if (value 
> _intsetGet(is
,intrev32ifbe(is
->length
)-1)) { 
  97             if (pos
) *pos 
= intrev32ifbe(is
->length
); 
  99         } else if (value 
< _intsetGet(is
,0)) { 
 107         cur 
= _intsetGet(is
,mid
); 
 110         } else if (value 
< cur
) { 
 126 /* Upgrades the intset to a larger encoding and inserts the given integer. */ 
 127 static intset 
*intsetUpgradeAndAdd(intset 
*is
, int64_t value
) { 
 128     uint8_t curenc 
= intrev32ifbe(is
->encoding
); 
 129     uint8_t newenc 
= _intsetValueEncoding(value
); 
 130     int length 
= intrev32ifbe(is
->length
); 
 131     int prepend 
= value 
< 0 ? 1 : 0; 
 133     /* First set new encoding and resize */ 
 134     is
->encoding 
= intrev32ifbe(newenc
); 
 135     is 
= intsetResize(is
,intrev32ifbe(is
->length
)+1); 
 137     /* Upgrade back-to-front so we don't overwrite values. 
 138      * Note that the "prepend" variable is used to make sure we have an empty 
 139      * space at either the beginning or the end of the intset. */ 
 141         _intsetSet(is
,length
+prepend
,_intsetGetEncoded(is
,length
,curenc
)); 
 143     /* Set the value at the beginning or the end. */ 
 145         _intsetSet(is
,0,value
); 
 147         _intsetSet(is
,intrev32ifbe(is
->length
),value
); 
 148     is
->length 
= intrev32ifbe(intrev32ifbe(is
->length
)+1); 
 152 static void intsetMoveTail(intset 
*is
, uint32_t from
, uint32_t to
) { 
 154     uint32_t bytes 
= intrev32ifbe(is
->length
)-from
; 
 155     uint32_t encoding 
= intrev32ifbe(is
->encoding
); 
 157     if (encoding 
== INTSET_ENC_INT64
) { 
 158         src 
= (int64_t*)is
->contents
+from
; 
 159         dst 
= (int64_t*)is
->contents
+to
; 
 160         bytes 
*= sizeof(int64_t); 
 161     } else if (encoding 
== INTSET_ENC_INT32
) { 
 162         src 
= (int32_t*)is
->contents
+from
; 
 163         dst 
= (int32_t*)is
->contents
+to
; 
 164         bytes 
*= sizeof(int32_t); 
 166         src 
= (int16_t*)is
->contents
+from
; 
 167         dst 
= (int16_t*)is
->contents
+to
; 
 168         bytes 
*= sizeof(int16_t); 
 170     memmove(dst
,src
,bytes
); 
 173 /* Insert an integer in the intset */ 
 174 intset 
*intsetAdd(intset 
*is
, int64_t value
, uint8_t *success
) { 
 175     uint8_t valenc 
= _intsetValueEncoding(value
); 
 177     if (success
) *success 
= 1; 
 179     /* Upgrade encoding if necessary. If we need to upgrade, we know that 
 180      * this value should be either appended (if > 0) or prepended (if < 0), 
 181      * because it lies outside the range of existing values. */ 
 182     if (valenc 
> intrev32ifbe(is
->encoding
)) { 
 183         /* This always succeeds, so we don't need to curry *success. */ 
 184         return intsetUpgradeAndAdd(is
,value
); 
 186         /* Abort if the value is already present in the set. 
 187          * This call will populate "pos" with the right position to insert 
 188          * the value when it cannot be found. */ 
 189         if (intsetSearch(is
,value
,&pos
)) { 
 190             if (success
) *success 
= 0; 
 194         is 
= intsetResize(is
,intrev32ifbe(is
->length
)+1); 
 195         if (pos 
< intrev32ifbe(is
->length
)) intsetMoveTail(is
,pos
,pos
+1); 
 198     _intsetSet(is
,pos
,value
); 
 199     is
->length 
= intrev32ifbe(intrev32ifbe(is
->length
)+1); 
 203 /* Delete integer from intset */ 
 204 intset 
*intsetRemove(intset 
*is
, int64_t value
, int *success
) { 
 205     uint8_t valenc 
= _intsetValueEncoding(value
); 
 207     if (success
) *success 
= 0; 
 209     if (valenc 
<= intrev32ifbe(is
->encoding
) && intsetSearch(is
,value
,&pos
)) { 
 210         uint32_t len 
= intrev32ifbe(is
->length
); 
 212         /* We know we can delete */ 
 213         if (success
) *success 
= 1; 
 215         /* Overwrite value with tail and update length */ 
 216         if (pos 
< (len
-1)) intsetMoveTail(is
,pos
+1,pos
); 
 217         is 
= intsetResize(is
,len
-1); 
 218         is
->length 
= intrev32ifbe(len
-1); 
 223 /* Determine whether a value belongs to this set */ 
 224 uint8_t intsetFind(intset 
*is
, int64_t value
) { 
 225     uint8_t valenc 
= _intsetValueEncoding(value
); 
 226     return valenc 
<= intrev32ifbe(is
->encoding
) && intsetSearch(is
,value
,NULL
); 
 229 /* Return random member */ 
 230 int64_t intsetRandom(intset 
*is
) { 
 231     return _intsetGet(is
,rand()%intrev
32ifbe
(is
->length
)); 
 234 /* Sets the value to the value at the given position. When this position is 
 235  * out of range the function returns 0, when in range it returns 1. */ 
 236 uint8_t intsetGet(intset 
*is
, uint32_t pos
, int64_t *value
) { 
 237     if (pos 
< intrev32ifbe(is
->length
)) { 
 238         *value 
= _intsetGet(is
,pos
); 
 244 /* Return intset length */ 
 245 uint32_t intsetLen(intset 
*is
) { 
 246     return intrev32ifbe(is
->length
); 
 249 /* Return intset blob size in bytes. */ 
 250 size_t intsetBlobLen(intset 
*is
) { 
 251     return sizeof(intset
)+intrev32ifbe(is
->length
)*intrev32ifbe(is
->encoding
); 
 254 #ifdef INTSET_TEST_MAIN 
 255 #include <sys/time.h> 
 257 void intsetRepr(intset 
*is
) { 
 259     for (i 
= 0; i 
< intrev32ifbe(is
->length
); i
++) { 
 260         printf("%lld\n", (uint64_t)_intsetGet(is
,i
)); 
 265 void error(char *err
) { 
 274 long long usec(void) { 
 276     gettimeofday(&tv
,NULL
); 
 277     return (((long long)tv
.tv_sec
)*1000000)+tv
.tv_usec
; 
 280 #define assert(_e) ((_e)?(void)0:(_assert(#_e,__FILE__,__LINE__),exit(1))) 
 281 void _assert(char *estr
, char *file
, int line
) { 
 282     printf("\n\n=== ASSERTION FAILED ===\n"); 
 283     printf("==> %s:%d '%s' is not true\n",file
,line
,estr
); 
 286 intset 
*createSet(int bits
, int size
) { 
 287     uint64_t mask 
= (1<<bits
)-1; 
 289     intset 
*is 
= intsetNew(); 
 291     for (i 
= 0; i 
< size
; i
++) { 
 293             value 
= (rand()*rand()) & mask
; 
 295             value 
= rand() & mask
; 
 297         is 
= intsetAdd(is
,value
,NULL
); 
 302 void checkConsistency(intset 
*is
) { 
 305     for (i 
= 0; i 
< (intrev32ifbe(is
->length
)-1); i
++) { 
 306         uint32_t encoding 
= intrev32ifbe(is
->encoding
); 
 308         if (encoding 
== INTSET_ENC_INT16
) { 
 309             int16_t *i16 
= (int16_t*)is
->contents
; 
 310             assert(i16
[i
] < i16
[i
+1]); 
 311         } else if (encoding 
== INTSET_ENC_INT32
) { 
 312             int32_t *i32 
= (int32_t*)is
->contents
; 
 313             assert(i32
[i
] < i32
[i
+1]); 
 315             int64_t *i64 
= (int64_t*)is
->contents
; 
 316             assert(i64
[i
] < i64
[i
+1]); 
 321 int main(int argc
, char **argv
) { 
 327     printf("Value encodings: "); { 
 328         assert(_intsetValueEncoding(-32768) == INTSET_ENC_INT16
); 
 329         assert(_intsetValueEncoding(+32767) == INTSET_ENC_INT16
); 
 330         assert(_intsetValueEncoding(-32769) == INTSET_ENC_INT32
); 
 331         assert(_intsetValueEncoding(+32768) == INTSET_ENC_INT32
); 
 332         assert(_intsetValueEncoding(-2147483648) == INTSET_ENC_INT32
); 
 333         assert(_intsetValueEncoding(+2147483647) == INTSET_ENC_INT32
); 
 334         assert(_intsetValueEncoding(-2147483649) == INTSET_ENC_INT64
); 
 335         assert(_intsetValueEncoding(+2147483648) == INTSET_ENC_INT64
); 
 336         assert(_intsetValueEncoding(-9223372036854775808ull) == INTSET_ENC_INT64
); 
 337         assert(_intsetValueEncoding(+9223372036854775807ull) == INTSET_ENC_INT64
); 
 341     printf("Basic adding: "); { 
 343         is 
= intsetAdd(is
,5,&success
); assert(success
); 
 344         is 
= intsetAdd(is
,6,&success
); assert(success
); 
 345         is 
= intsetAdd(is
,4,&success
); assert(success
); 
 346         is 
= intsetAdd(is
,4,&success
); assert(!success
); 
 350     printf("Large number of random adds: "); { 
 353         for (i 
= 0; i 
< 1024; i
++) { 
 354             is 
= intsetAdd(is
,rand()%0x800
,&success
); 
 355             if (success
) inserts
++; 
 357         assert(intrev32ifbe(is
->length
) == inserts
); 
 358         checkConsistency(is
); 
 362     printf("Upgrade from int16 to int32: "); { 
 364         is 
= intsetAdd(is
,32,NULL
); 
 365         assert(intrev32ifbe(is
->encoding
) == INTSET_ENC_INT16
); 
 366         is 
= intsetAdd(is
,65535,NULL
); 
 367         assert(intrev32ifbe(is
->encoding
) == INTSET_ENC_INT32
); 
 368         assert(intsetFind(is
,32)); 
 369         assert(intsetFind(is
,65535)); 
 370         checkConsistency(is
); 
 373         is 
= intsetAdd(is
,32,NULL
); 
 374         assert(intrev32ifbe(is
->encoding
) == INTSET_ENC_INT16
); 
 375         is 
= intsetAdd(is
,-65535,NULL
); 
 376         assert(intrev32ifbe(is
->encoding
) == INTSET_ENC_INT32
); 
 377         assert(intsetFind(is
,32)); 
 378         assert(intsetFind(is
,-65535)); 
 379         checkConsistency(is
); 
 383     printf("Upgrade from int16 to int64: "); { 
 385         is 
= intsetAdd(is
,32,NULL
); 
 386         assert(intrev32ifbe(is
->encoding
) == INTSET_ENC_INT16
); 
 387         is 
= intsetAdd(is
,4294967295,NULL
); 
 388         assert(intrev32ifbe(is
->encoding
) == INTSET_ENC_INT64
); 
 389         assert(intsetFind(is
,32)); 
 390         assert(intsetFind(is
,4294967295)); 
 391         checkConsistency(is
); 
 394         is 
= intsetAdd(is
,32,NULL
); 
 395         assert(intrev32ifbe(is
->encoding
) == INTSET_ENC_INT16
); 
 396         is 
= intsetAdd(is
,-4294967295,NULL
); 
 397         assert(intrev32ifbe(is
->encoding
) == INTSET_ENC_INT64
); 
 398         assert(intsetFind(is
,32)); 
 399         assert(intsetFind(is
,-4294967295)); 
 400         checkConsistency(is
); 
 404     printf("Upgrade from int32 to int64: "); { 
 406         is 
= intsetAdd(is
,65535,NULL
); 
 407         assert(intrev32ifbe(is
->encoding
) == INTSET_ENC_INT32
); 
 408         is 
= intsetAdd(is
,4294967295,NULL
); 
 409         assert(intrev32ifbe(is
->encoding
) == INTSET_ENC_INT64
); 
 410         assert(intsetFind(is
,65535)); 
 411         assert(intsetFind(is
,4294967295)); 
 412         checkConsistency(is
); 
 415         is 
= intsetAdd(is
,65535,NULL
); 
 416         assert(intrev32ifbe(is
->encoding
) == INTSET_ENC_INT32
); 
 417         is 
= intsetAdd(is
,-4294967295,NULL
); 
 418         assert(intrev32ifbe(is
->encoding
) == INTSET_ENC_INT64
); 
 419         assert(intsetFind(is
,65535)); 
 420         assert(intsetFind(is
,-4294967295)); 
 421         checkConsistency(is
); 
 425     printf("Stress lookups: "); { 
 426         long num 
= 100000, size 
= 10000; 
 429         is 
= createSet(bits
,size
); 
 430         checkConsistency(is
); 
 433         for (i 
= 0; i 
< num
; i
++) intsetSearch(is
,rand() % ((1<<bits
)-1),NULL
); 
 434         printf("%ld lookups, %ld element set, %lldusec\n",num
,size
,usec()-start
); 
 437     printf("Stress add+delete: "); { 
 440         for (i 
= 0; i 
< 0xffff; i
++) { 
 442             is 
= intsetAdd(is
,v1
,NULL
); 
 443             assert(intsetFind(is
,v1
)); 
 446             is 
= intsetRemove(is
,v2
,NULL
); 
 447             assert(!intsetFind(is
,v2
)); 
 449         checkConsistency(is
);