目录
  1. 1. redis源码阅读-intset(整数集合)
    1. 1.1. intset
    2. 1.2. intset.h
    3. 1.3. intset.c
    4. 1.4. 文献整理
redis源码阅读-intset(整数集合)

redis源码阅读-intset(整数集合)

intset

intset是redis实现的整数集合,是redis集合键底层实现之一。主要用在数据量比较小,并且是整数的地方。
intset能保存int16_t,int32_t,int64_t类型的整数,并且可以保存其中不会出现重复数据,而且其数据的大小是从小到大有序排列(这个有序排列我还没想明白是怎么实现的)。
intset的结构如下:

1
2
3
4
5
typedef struct intset {
uint32_t encoding; // intset集合真正的类型,有uint16_t,uint32_t,uint64_t三种类型。
uint32_t length; // 长度
int8_t contents[]; // 内容数组,存储整数集合,其声明类型没有意义
} intset;

其中的encoding是用来判断intset是什么类型的,而contents是用来存储intset集合元素的,默认声明是int8_t,但是没有意义。
intset有个特性就是升级,当加入intset的值的编码比当前Intset的编码大时,intset会自动升级为加入的值一样的编码。例如:当前intset是int8_t类型,有4个元素,总共占8*3=24位的内存空间,现在加入一个int32_t类型的65535,intset这个集合升级为占32*4=128位的内存空间,而升级后不能降级

intset.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/*
* Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot 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 list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list 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 __INTSET_H
#define __INTSET_H
#include <stdint.h>
// 定义整数集合结构
typedef struct intset {
uint32_t encoding; // intset集合真正的类型,有uint16_t,uint32_t,uint64_t三种类型。
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, int *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(const intset *is);
// 获取整数集合占用内存空间的长度
size_t intsetBlobLen(intset *is);

// 用于测试
#ifdef REDIS_TEST
int intsetTest(int argc, char *argv[]);
#endif

#endif // __INTSET_H

intset.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
/*
* Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot 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 list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list 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.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "intset.h"
#include "zmalloc.h"
#include "endianconv.h"

/* Note that these encodings are ordered, so:
* INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */

/*
* 大小端字节序:https://www.cnblogs.com/gremount/p/8830707.html
* 在计算机中,一般是从低位字节开始读取,而人总是习惯从高位字节开始读取
* 为了适应计算机需求,需要将字节转换为小端字节序,也就是把字节的低位转到高位
*/

// 各种整数类型占位大小
#define INTSET_ENC_INT16 (sizeof(int16_t))
#define INTSET_ENC_INT32 (sizeof(int32_t))
#define INTSET_ENC_INT64 (sizeof(int64_t))

/* Return the required encoding for the provided value. */
// 返回提供的整数类型的占位大小,虽然输入的是int64_t类型,但是如果输入的值范围在更小的类型中,那就返回适合的类型占位大小
static uint8_t _intsetValueEncoding(int64_t v) {
if (v < INT32_MIN || v > INT32_MAX) // 如果输入的数值小于整型所占最小范围或者大于整型所占最大范围,那确定它是64位整型
re1turn INTSET_ENC_INT64;
else if (v < INT16_MIN || v > INT16_MAX)
return INTSET_ENC_INT32;
else
return INTSET_ENC_INT16;
}

/* Return the value at pos, given an encoding. */
/* 从is中获取pos位置的整数,它是通过enc编码来获取地址的
* 因为在intset集合中,contents一般声明为int8_t类型,取地址一般是8位8位的取。但是intset集合的类型是encoding来决定的
* 如果intset是int16_t类型,却8位8位的取地址,取出的内容都是不对的。我们需要将contents的类型转换为enc指定的类型。
* 取地址的时候通过enc指定的位数来取,这样就能取得正确的地址,获取正确地址中的正确内容
* intset *is : 整数集合
* int pos : 整数集合中一个整数所在位置
* uint8_t enc : 类型值 uint8_t类型值是1 , uint16_t类型值是2 , uint32_t类型值是4 , uint64_t类型值是8
* 一般enc都是is->encoding
*/
static int64_t _intsetGetEncoded(intset *is, int pos, uint8_t enc) {
int64_t v64;
int32_t v32;
int16_t v16;

// 如果enc=8 , 表示is是一个uint64_t类型的整数集合
if (enc == INTSET_ENC_INT64) {
// 不管怎样,先将is的contents类型转换为int64_t,将int64_t类型区域复制给v64
memcpy(&v64,((int64_t*)is->contents)+pos,sizeof(v64));
memrev64ifbe(&v64); // 将v64地址转换为小端字节序,方便计算机读取
return v64; // 然后返回v64的内容
} else if (enc == INTSET_ENC_INT32) {
memcpy(&v32,((int32_t*)is->contents)+pos,sizeof(v32));
memrev32ifbe(&v32);
return v32;
} else {
memcpy(&v16,((int16_t*)is->contents)+pos,sizeof(v16));
memrev16ifbe(&v16);
return v16;
}
}

/* Return the value at pos, using the configured encoding. */
// 获取pos位置的整型变量,整型集合的类型直接从encoding中获取
static int64_t _intsetGet(intset *is, int pos) {
return _intsetGetEncoded(is,pos,intrev32ifbe(is->encoding));
}

/* Set the value at pos, using the configured encoding. */
// 在pos位置设置一个整数值
static void _intsetSet(intset *is, int pos, int64_t value) {
uint32_t encoding = intrev32ifbe(is->encoding); //先获取is是哪种类型的整数集合

if (encoding == INTSET_ENC_INT64) { // 如果is是uint64_t类型的整数集合,先将is中的contents强制转换为int64_t类型,将value放置在64位的区域内
((int64_t*)is->contents)[pos] = value;
memrev64ifbe(((int64_t*)is->contents)+pos); // 将地址转换为小端地址序存储
} else if (encoding == INTSET_ENC_INT32) {
((int32_t*)is->contents)[pos] = value;
memrev32ifbe(((int32_t*)is->contents)+pos);
} else {
((int16_t*)is->contents)[pos] = value;
memrev16ifbe(((int16_t*)is->contents)+pos);
}
}

/* Create an empty intset. */
// 创建一个空的整数集合
intset *intsetNew(void) {
intset *is = zmalloc(sizeof(intset));
is->encoding = intrev32ifbe(INTSET_ENC_INT16);
is->length = 0;
return is;
}

/* Resize the intset */
// 改变整数集合大小
static intset *intsetResize(intset *is, uint32_t len) {
uint32_t size = len*intrev32ifbe(is->encoding);
is = zrealloc(is,sizeof(intset)+size);
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. */
/* 搜索pos位置的value,如果value被找到,则返回1,并且pos位置在集合中
* 如果value没有被找到,则返回0,并且pos位置不在集合中,就为pos置0或者最大值
*/
static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {
// 使用二分查找法
int min = 0, max = intrev32ifbe(is->length)-1, mid = -1; // min:最小值,max:最大值,mid:初始中间值
int64_t cur = -1; // 当前游标

/* The value can never be found when the set is empty */
if (intrev32ifbe(is->length) == 0) { // 当集合长度为0,表示为空
if (pos) *pos = 0; // 如果输入的pos地址不畏NULL,就给它置0
return 0;
} else {
/* Check for the case where we know we cannot find the value,
* but do know the insert position. */
// 在intset集合中,最大值会被放在最后,最小值会被放在最前
// 因为,数据编码最大的会放在最后面,而最大值的数据编码是最大的
if (value > _intsetGet(is,max)) {
if (pos) *pos = intrev32ifbe(is->length);
return 0;
} else if (value < _intsetGet(is,0)) {
if (pos) *pos = 0;
return 0;
}
}

// 当索引最大值大于索引最小值
while(max >= min) {
mid = ((unsigned int)min + (unsigned int)max) >> 1; // (相当于min+max)/2
cur = _intsetGet(is,mid); // 获取中间值
// 判断value值在哪边,如果在中间就表示已经被找到,索引值就不要改变,直接执行下一步
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;
}
}

/* Upgrades the intset to a larger encoding and inserts the given integer. */
// 新增value,并根据新增value的编码来升级intset的编码
static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {
uint8_t curenc = intrev32ifbe(is->encoding); // 将encoding转换为32为的小端字节序,并返回其内容
uint8_t newenc = _intsetValueEncoding(value); // 获取value值的编码类型
int length = intrev32ifbe(is->length); // 获取is的长度
int prepend = value < 0 ? 1 : 0;

/* First set new encoding and resize */
is->encoding = intrev32ifbe(newenc); // is小端字节序存储新编码
is = intsetResize(is,intrev32ifbe(is->length)+1); // 扩容

/* Upgrade back-to-front so we don't overwrite values.
* Note that the "prepend" variable is used to make sure we have an empty
* space at either the beginning or the end of the intset. */
/* 如果prepend=1,表示需要在最前面增加值,就将intset集合中的元素一个一个的向后摞一位,使得索引为0时是空的
* 如果prepend=0,表示需要在最后面增加值,就将intset集合重新增加一遍,不改变索引
*/
while(length--)
_intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));

/* Set the value at the beginning or the end. */
// 如果输入的整数是小于0的,就将其安放在最开始
if (prepend)
_intsetSet(is,0,value);
else // 否则就放在结束
_intsetSet(is,intrev32ifbe(is->length),value);
is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
return is;
}

// 向后移动元素,主要是用来为新元素预留空间
static void intsetMoveTail(intset *is, uint32_t from, uint32_t to) {
void *src, *dst;
uint32_t bytes = intrev32ifbe(is->length)-from; // 获取索引from后的元素数
uint32_t encoding = intrev32ifbe(is->encoding);

if (encoding == INTSET_ENC_INT64) {
src = (int64_t*)is->contents+from;
dst = (int64_t*)is->contents+to;
bytes *= sizeof(int64_t);
} else if (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,添加成功,success=1,否则success=0
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
uint8_t valenc = _intsetValueEncoding(value); // 为value值设置一个合适的编码
uint32_t pos;
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 > intrev32ifbe(is->encoding)) { // 如果需要增加值的编码比is的编码大,就需要升级is为value一样的编码,并将value增加到最后
/* This always succeeds, so we don't need to curry *success. */
return intsetUpgradeAndAdd(is,value);
} else { // 如果编码一样或者更小,就找到value的插入点
/* 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)) { // 表示查找到intset有相同的值
if (success) *success = 0;
return is;
}

is = intsetResize(is,intrev32ifbe(is->length)+1);
// 如果pos=0,就直接将整体向后移一位,空出第一位
if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);
}

_intsetSet(is,pos,value);
is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
return is;
}

/* Delete integer from intset */
// 从intset中删除
intset *intsetRemove(intset *is, int64_t value, int *success) {
uint8_t valenc = _intsetValueEncoding(value);
uint32_t pos;
if (success) *success = 0;

if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,&pos)) {
uint32_t len = intrev32ifbe(is->length);

/* We know we can delete */
if (success) *success = 1;

/* Overwrite value with tail and update length */
if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);
is = intsetResize(is,len-1);
is->length = intrev32ifbe(len-1);
}
return is;
}

/* Determine whether a value belongs to this set */
// 查询value,调用intsetSearch函数,查询成功返回1,失败返回0
uint8_t intsetFind(intset *is, int64_t value) {
uint8_t valenc = _intsetValueEncoding(value);
return valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,NULL);
}

/* Return random member */
// 返回一个随机索引中的值
int64_t intsetRandom(intset *is) {
return _intsetGet(is,rand()%intrev32ifbe(is->length));
}

/* Get the value at the given position. When this position is
* out of range the function returns 0, when in range it returns 1. */
// 获取pos位置的整型变量,如果成功获取,返回1,并且value存储获取到的值;否则返回0,value=NULL
uint8_t intsetGet(intset *is, uint32_t pos, int64_t *value) {
if (pos < intrev32ifbe(is->length)) {
*value = _intsetGet(is,pos);
return 1;
}
return 0;
}

/* Return intset length */
// 获取is长度
uint32_t intsetLen(const intset *is) {
return intrev32ifbe(is->length);
}

/* Return intset blob size in bytes. */
// len*encoding+sizeof(intset);返回的是内存空间的长度
size_t intsetBlobLen(intset *is) {
return sizeof(intset)+intrev32ifbe(is->length)*intrev32ifbe(is->encoding);
}

#ifdef REDIS_TEST
#include <sys/time.h>
#include <time.h>

#if 0
static void intsetRepr(intset *is) {
for (uint32_t i = 0; i < intrev32ifbe(is->length); i++) {
printf("%lld\n", (uint64_t)_intsetGet(is,i));
}
printf("\n");
}

static void error(char *err) {
printf("%s\n", err);
exit(1);
}
#endif

static void ok(void) {
printf("OK\n");
}

static 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)))
static 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);
}

static intset *createSet(int bits, int size) {
uint64_t mask = (1<<bits)-1;
uint64_t value;
intset *is = intsetNew();

for (int i = 0; i < size; i++) {
if (bits > 32) {
value = (rand()*rand()) & mask;
} else {
value = rand() & mask;
}
is = intsetAdd(is,value,NULL);
}
return is;
}

static void checkConsistency(intset *is) {
for (uint32_t i = 0; i < (intrev32ifbe(is->length)-1); i++) {
uint32_t encoding = intrev32ifbe(is->encoding);

if (encoding == INTSET_ENC_INT16) {
int16_t *i16 = (int16_t*)is->contents;
assert(i16[i] < i16[i+1]);
} else if (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]);
}
}
}

#define UNUSED(x) (void)(x)
int intsetTest(int argc, char **argv) {
uint8_t success;
int i;
intset *is;
srand(time(NULL));

UNUSED(argc);
UNUSED(argv);

printf("Value encodings: "); {
assert(_intsetValueEncoding(-32768) == INTSET_ENC_INT16);
assert(_intsetValueEncoding(+32767) == INTSET_ENC_INT16);
assert(_intsetValueEncoding(-32769) == INTSET_ENC_INT32);
assert(_intsetValueEncoding(+32768) == INTSET_ENC_INT32);
assert(_intsetValueEncoding(-2147483648) == INTSET_ENC_INT32);
assert(_intsetValueEncoding(+2147483647) == INTSET_ENC_INT32);
assert(_intsetValueEncoding(-2147483649) == INTSET_ENC_INT64);
assert(_intsetValueEncoding(+2147483648) == INTSET_ENC_INT64);
assert(_intsetValueEncoding(-9223372036854775808ull) ==
INTSET_ENC_INT64);
assert(_intsetValueEncoding(+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: "); {
uint32_t inserts = 0;
is = intsetNew();
for (i = 0; i < 1024; i++) {
is = intsetAdd(is,rand()%0x800,&success);
if (success) inserts++;
}
assert(intrev32ifbe(is->length) == inserts);
checkConsistency(is);
ok();
}

printf("Upgrade from int16 to int32: "); {
is = intsetNew();
is = intsetAdd(is,32,NULL);
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
is = intsetAdd(is,65535,NULL);
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
assert(intsetFind(is,32));
assert(intsetFind(is,65535));
checkConsistency(is);

is = intsetNew();
is = intsetAdd(is,32,NULL);
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
is = intsetAdd(is,-65535,NULL);
assert(intrev32ifbe(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(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
is = intsetAdd(is,4294967295,NULL);
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);
assert(intsetFind(is,32));
assert(intsetFind(is,4294967295));
checkConsistency(is);

is = intsetNew();
is = intsetAdd(is,32,NULL);
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
is = intsetAdd(is,-4294967295,NULL);
assert(intrev32ifbe(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(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
is = intsetAdd(is,4294967295,NULL);
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);
assert(intsetFind(is,65535));
assert(intsetFind(is,4294967295));
checkConsistency(is);

is = intsetNew();
is = intsetAdd(is,65535,NULL);
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
is = intsetAdd(is,-4294967295,NULL);
assert(intrev32ifbe(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();
}

return 0;
}
#endif

文献整理

  1. Redis内部数据结构详解(7)——intset
  2. Redis之整数集合intset
  3. redis中整数聚合的add操作
文章作者: rack-leen
文章链接: http://yoursite.com/2019/12/22/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/C/redis/redis%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB-intset-%E6%95%B4%E6%95%B0%E9%9B%86%E5%90%88/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 rack-leen's blog
打赏
  • 微信
  • 支付宝

评论