-
Notifications
You must be signed in to change notification settings - Fork 238
/
Copy patht_hash.c
1472 lines (1229 loc) · 54.5 KB
/
t_hash.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
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* 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 "server.h"
#include <math.h>
/*-----------------------------------------------------------------------------
* Hash type API
*----------------------------------------------------------------------------*/
/* Check the length of a number of objects to see if we need to convert a
* listpack to a real hash. Note that we only check string encoded objects
* as their string length can be queried in constant time. */
/* 检查对象的长度,看我们是否需要将一个 listpack(紧凑列表) 转换为真正的 hashtable(哈希表)
* 注意,我们只检查字符串编码的对象,因为它的字符串长度可以在 O(1) 时间内被查询到 */
void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
int i;
size_t sum = 0;
/* 如果内部编码类型不是 listpack,就不用转换了 */
if (o->encoding != OBJ_ENCODING_LISTPACK) return;
/* 使用给定的 start 到 end 范围读取 argv 参数 */
for (i = start; i <= end; i++) {
/* 检查对象类型是否为字符串编码,如果不是就不用继续检查长度 */
if (!sdsEncodedObject(argv[i]))
continue;
/* 获取字符串长度 */
size_t len = sdslen(argv[i]->ptr);
/* 检查长度是否超过限制值 hash_max_listpack_value ,
* 若超过限制则调用 hashTypeConvert 函数转换编码类型为 hashtable */
if (len > server.hash_max_listpack_value) {
hashTypeConvert(o, OBJ_ENCODING_HT);
return;
}
/* 计算字符串编码对象总和 */
sum += len;
}
/* 对字符串编码对象长度总和与目前 listpack 的大小进行加法运算,
* 检查结果是否超出 listpack 最大安全容量限制 (默认1GB)
* 若超出限制返回0,并转换编码为 hashtable */
if (!lpSafeToAdd(o->ptr, sum))
hashTypeConvert(o, OBJ_ENCODING_HT);
}
/* Get the value from a listpack encoded hash, identified by field.
* Returns -1 when the field cannot be found. */
/* 从 listpack 编码的哈希类型对象获取 value ,
* 如果查找不到返回 -1 */
int hashTypeGetFromListpack(robj *o, sds field,
unsigned char **vstr,
unsigned int *vlen,
long long *vll)
{
unsigned char *zl, *fptr = NULL, *vptr = NULL;
/* 断言:o 内部编码类型一定是 OBJ_ENCODING_LISTPACK ,否则中止 */
serverAssert(o->encoding == OBJ_ENCODING_LISTPACK);
zl = o->ptr;
fptr = lpFirst(zl);
/* 从头开始遍历整个 listpack 来查找 field 所在的 entry */
if (fptr != NULL) {
fptr = lpFind(zl, fptr, (unsigned char*)field, sdslen(field), 1);
if (fptr != NULL) {
/* Grab pointer to the value (fptr points to the field) */
/* fptr 为指向 field 的指针, vptr 为指向 value 的指针 ,
* 对于哈希类型,value 被保存在对应的 field 的下一个 entry */
vptr = lpNext(zl, fptr);
serverAssert(vptr != NULL);
}
}
/* 找到了 value , 若 value 为字符串编码则令 vstr 指向该 value,
* 若 value 为 整数编码则 vll 会指向 value */
if (vptr != NULL) {
*vstr = lpGetValue(vptr, vlen, vll);
return 0;
}
return -1;
}
/* Get the value from a hash table encoded hash, identified by field.
* Returns NULL when the field cannot be found, otherwise the SDS value
* is returned. */
/* 从 hashtable 编码的哈希类型对象获取 value ,
* 查找不到返回 NULL ,否则返回一个 sds 类型变量( sds 是 redis 中的字符串,底层为 char* ) */
sds hashTypeGetFromHashTable(robj *o, sds field) {
dictEntry *de;
serverAssert(o->encoding == OBJ_ENCODING_HT);
/* 在字典/哈希表 dict 中查找 field 对应的 dictEntry */
de = dictFind(o->ptr, field);
if (de == NULL) return NULL;
return dictGetVal(de);
}
/* Higher level function of hashTypeGet*() that returns the hash value
* associated with the specified field. If the field is found C_OK
* is returned, otherwise C_ERR. The returned object is returned by
* reference in either *vstr and *vlen if it's returned in string form,
* or stored in *vll if it's returned as a number.
*
* If *vll is populated *vstr is set to NULL, so the caller
* can always check the function return by checking the return value
* for C_OK and checking if vll (or vstr) is NULL. */
/* hashTypeGet*() 的高级函数(我认为叫通用函数更容易理解),返回 field 对应的 value
* 如果查找成功,返回 C_OK ,否则为 C_ERR
* 如果是以字符串形式返回,则字符串值和长度的引用存储在 *vstr 和 *vlen 中
* 如果以数字形式返回,则存储在 *vll 中 (只有 listpack 会有整数编码, hashtable 依然以字符串编码保存整数)
*
* 如果 *vll 被赋值,*vstr 被设置为NULL ,反之亦然
* 所以调用者可以通过检查返回值是否返回 C_OK ,返回C_OK后检查 vll(或 vstr)是否为 NULL
*/
int hashTypeGetValue(robj *o, sds field, unsigned char **vstr, unsigned int *vlen, long long *vll) {
/* 判断内部编码类型并调用相应的函数获取 value */
if (o->encoding == OBJ_ENCODING_LISTPACK) {
*vstr = NULL;
if (hashTypeGetFromListpack(o, field, vstr, vlen, vll) == 0)
return C_OK;
} else if (o->encoding == OBJ_ENCODING_HT) {
sds value;
if ((value = hashTypeGetFromHashTable(o, field)) != NULL) {
*vstr = (unsigned char*) value;
*vlen = sdslen(value);
return C_OK;
}
} else {
serverPanic("Unknown hash encoding");
}
return C_ERR;
}
/* Like hashTypeGetValue() but returns a Redis object, which is useful for
* interaction with the hash type outside t_hash.c.
* The function returns NULL if the field is not found in the hash. Otherwise
* a newly allocated string object with the value is returned. */
/* 与 hashTypeGetValue() 类似,但返回一个 redisObject(Redis对象),
* 这对与 t_hash.c 以外的哈希类型的交互很有用
* 如果在哈希对象中没有找到 field ,该函数返回 NULL。否则,将返回一个新分配的带有该值的字符串对象 */
robj *hashTypeGetValueObject(robj *o, sds field) {
unsigned char *vstr;
unsigned int vlen;
long long vll;
if (hashTypeGetValue(o,field,&vstr,&vlen,&vll) == C_ERR) return NULL;
if (vstr) return createStringObject((char*)vstr,vlen);
else return createStringObjectFromLongLong(vll);
}
/* Higher level function using hashTypeGet*() to return the length of the
* object associated with the requested field, or 0 if the field does not
* exist. */
/* hashTypeGet*() 的高级函数,返回与 field 相关的对象的长度。
* 对象的长度,如果 field 不存在,则返回0。 */
size_t hashTypeGetValueLength(robj *o, sds field) {
size_t len = 0;
unsigned char *vstr = NULL;
unsigned int vlen = UINT_MAX;
long long vll = LLONG_MAX;
if (hashTypeGetValue(o, field, &vstr, &vlen, &vll) == C_OK)
len = vstr ? vlen : sdigits10(vll);
return len;
}
/* Test if the specified field exists in the given hash. Returns 1 if the field
* exists, and 0 when it doesn't. */
/* 测试指定的 field 是否存在于给定的哈希类型对象中,如果存在则返回1,不存在则返回0 */
int hashTypeExists(robj *o, sds field) {
unsigned char *vstr = NULL;
unsigned int vlen = UINT_MAX;
long long vll = LLONG_MAX;
return hashTypeGetValue(o, field, &vstr, &vlen, &vll) == C_OK;
}
/* Add a new field, overwrite the old with the new value if it already exists.
* Return 0 on insert and 1 on update.
*
* By default, the key and value SDS strings are copied if needed, so the
* caller retains ownership of the strings passed. However this behavior
* can be effected by passing appropriate flags (possibly bitwise OR-ed):
*
* HASH_SET_TAKE_FIELD -- The SDS field ownership passes to the function.
* HASH_SET_TAKE_VALUE -- The SDS value ownership passes to the function.
*
* When the flags are used the caller does not need to release the passed
* SDS string(s). It's up to the function to use the string to create a new
* entry or to free the SDS string before returning to the caller.
*
* HASH_SET_COPY corresponds to no flags passed, and means the default
* semantics of copying the values if needed.
*
*/
/* 添加一个新 field-value (在 set 中 field 和 value 是成对的,field-value 表示一个 field value 对)
* 如果 field 已经存在,则用新值覆盖。
* 插入新的 field-value 时返回0,更新时返回1。
*
* 默认情况下,如果需要 field 和 value 是返回它们的字符串副本,因此调用者保留了对所传递字符串的所有权。
* 可以通过传递的标志 (flags) 来决定函数的执行方法。
*
* HASH_SET_TAKE_FIELD -- SDS field 的所有权传递给函数。
* HASH_SET_TAKE_VALUE -- SDS value 的所有权传给了函数。
*
* 当这些标志被使用时,调用者不需要自己释放所传递的 SDS 字符串,
* 函数会根据标志决定是否使用该字符串创建一个新的 entry,或在返回给调用者之前释放 SDS 字符串。
*
* HASH_SET_COPY 对应于没有设置任何标志,意味着默认的语义是在需要时复制 value。
*/
#define HASH_SET_TAKE_FIELD (1<<0)
#define HASH_SET_TAKE_VALUE (1<<1)
#define HASH_SET_COPY 0
int hashTypeSet(robj *o, sds field, sds value, int flags) {
int update = 0;
/* Check if the field is too long for listpack, and convert before adding the item.
* This is needed for HINCRBY* case since in other commands this is handled early by
* hashTypeTryConversion, so this check will be a NOP. */
/* 检查 field 和 value 是否对 listpack 来说太长,并在添加前进行编码类型转换。
* 这是在使用 HINCRBY 和 HINCRBYFLOAT 命令的情况下需要的,而在其他命令中,
* 这个编码转换问题会被 hashTypeTryConversion 提前处理,所以这个检查将是一个空操作 */
if (o->encoding == OBJ_ENCODING_LISTPACK) {
if (sdslen(field) > server.hash_max_listpack_value || sdslen(value) > server.hash_max_listpack_value)
hashTypeConvert(o, OBJ_ENCODING_HT);
}
/* 哈希对象编码类型为listpack */
if (o->encoding == OBJ_ENCODING_LISTPACK) {
unsigned char *zl, *fptr, *vptr;
zl = o->ptr;
fptr = lpFirst(zl);
/* 从头开始遍历 listpack 找到对应的 field-value */
if (fptr != NULL) {
fptr = lpFind(zl, fptr, (unsigned char*)field, sdslen(field), 1);
if (fptr != NULL) {
/* Grab pointer to the value (fptr points to the field) */
/* 获取指针所指向的 value ( fptr 指向的区域)*/
vptr = lpNext(zl, fptr);
serverAssert(vptr != NULL);
/* update 设为1表示查找成功 */
update = 1;
/* Replace value */
/* 替换 value 为所要设置的值 */
zl = lpReplace(zl, &vptr, (unsigned char*)value, sdslen(value));
}
}
/* 查找失败 */
if (!update) {
/* Push new field/value pair onto the tail of the listpack */
/* 在 listpack 尾部追加 field-value */
zl = lpAppend(zl, (unsigned char*)field, sdslen(field));
zl = lpAppend(zl, (unsigned char*)value, sdslen(value));
}
o->ptr = zl;
/* Check if the listpack needs to be converted to a hash table */
/* entry 数量超过限制的最大值,哈希编码类型需要转换为 hashtable */
if (hashTypeLength(o) > server.hash_max_listpack_entries)
hashTypeConvert(o, OBJ_ENCODING_HT);
/* 哈希编码类型为 hashtable */
} else if (o->encoding == OBJ_ENCODING_HT) {
dictEntry *de = dictFind(o->ptr,field);
/* 查找成功 */
if (de) {
/* 先将 dictEntry 中原来的 value 释放 */
sdsfree(dictGetVal(de));
/* 设置了 HASH_SET_TAKE_VALUE 标志的情况下,直接让 dictEntry 的 val 引用这个 value,
* 并让 value = NULL ,即不再引用任何地方 */
if (flags & HASH_SET_TAKE_VALUE) {
dictGetVal(de) = value;
value = NULL;
} else {
/* 没有设置 HASH_SET_TAKE_VALUE 标志,我们复制一个 value 并让 dicrEntry 引用这个副本 */
dictGetVal(de) = sdsdup(value);
}
update = 1;
/* 查找失败,设置 field, value 具体过程与上方查找成功类似 */
} else {
sds f,v;
if (flags & HASH_SET_TAKE_FIELD) {
f = field;
field = NULL;
} else {
f = sdsdup(field);
}
if (flags & HASH_SET_TAKE_VALUE) {
v = value;
value = NULL;
} else {
v = sdsdup(value);
}
dictAdd(o->ptr,f,v);
}
} else {
serverPanic("Unknown hash encoding");
}
/* Free SDS strings we did not referenced elsewhere if the flags
* want this function to be responsible. */
/* 如果有把所有权交给函数的标志,释放我们没有在引用其他地方的 SDS 字符串 */
if (flags & HASH_SET_TAKE_FIELD && field) sdsfree(field);
if (flags & HASH_SET_TAKE_VALUE && value) sdsfree(value);
return update;
}
/* Delete an element from a hash.
* Return 1 on deleted and 0 on not found. */
/* 从哈希类型对象中删除一个元素,返回1表示被删除,返回0表示未找到 */
int hashTypeDelete(robj *o, sds field) {
int deleted = 0;
if (o->encoding == OBJ_ENCODING_LISTPACK) {
unsigned char *zl, *fptr;
zl = o->ptr;
fptr = lpFirst(zl);
if (fptr != NULL) {
fptr = lpFind(zl, fptr, (unsigned char*)field, sdslen(field), 1);
if (fptr != NULL) {
/* Delete both of the key and the value. */
/* 将 field 和 value 一并删除 */
zl = lpDeleteRangeWithEntry(zl,&fptr,2);
o->ptr = zl;
deleted = 1;
}
}
} else if (o->encoding == OBJ_ENCODING_HT) {
if (dictDelete((dict*)o->ptr, field) == C_OK) {
deleted = 1;
/* Always check if the dictionary needs a resize after a delete. */
/* 检查删除元素后是否需要重新分配哈希表的内存空间 */
if (htNeedsResize(o->ptr)) dictResize(o->ptr);
}
} else {
serverPanic("Unknown hash encoding");
}
return deleted;
}
/* Return the number of elements in a hash. */
/* 返回哈希类型对象中的元素数量 */
unsigned long hashTypeLength(const robj *o) {
unsigned long length = ULONG_MAX;
if (o->encoding == OBJ_ENCODING_LISTPACK) {
/* field-value 对为一个元素,所以元素数量是 listpack 的 entry 数量除以2 */
length = lpLength(o->ptr) / 2;
} else if (o->encoding == OBJ_ENCODING_HT) {
/* redis 的哈希表是渐进式 rehash 的,所以 dict 中有一新一旧的两个哈希表,
* 所以 dictSize 计算元素数量方法是将两个哈希表的元素数量相加 */
length = dictSize((const dict*)o->ptr);
} else {
serverPanic("Unknown hash encoding");
}
return length;
}
/* 哈希类型对象迭代器的初始化函数 */
hashTypeIterator *hashTypeInitIterator(robj *subject) {
hashTypeIterator *hi = zmalloc(sizeof(hashTypeIterator));
hi->subject = subject;
hi->encoding = subject->encoding;
if (hi->encoding == OBJ_ENCODING_LISTPACK) {
hi->fptr = NULL;
hi->vptr = NULL;
} else if (hi->encoding == OBJ_ENCODING_HT) {
hi->di = dictGetIterator(subject->ptr);
} else {
serverPanic("Unknown hash encoding");
}
return hi;
}
/* 释放哈希类型对象迭代器 */
void hashTypeReleaseIterator(hashTypeIterator *hi) {
if (hi->encoding == OBJ_ENCODING_HT)
dictReleaseIterator(hi->di);
zfree(hi);
}
/* Move to the next entry in the hash. Return C_OK when the next entry
* could be found and C_ERR when the iterator reaches the end. */
/* 移动到哈希表的下一个 entry。当找到下一个 entry 时返回 C_OK,当迭代器到达终点时返回 C_ERR */
int hashTypeNext(hashTypeIterator *hi) {
/* 编码类型为 listpack */
if (hi->encoding == OBJ_ENCODING_LISTPACK) {
unsigned char *zl;
unsigned char *fptr, *vptr;
zl = hi->subject->ptr;
fptr = hi->fptr;
vptr = hi->vptr;
if (fptr == NULL) {
/* Initialize cursor */
/* 初始化游标,即令 fptr 等于 listpack 的第一个 entry */
serverAssert(vptr == NULL);
fptr = lpFirst(zl);
} else {
/* Advance cursor */
/* 游标前进,即令 fptr 等于 vptr 的下一个 entry */
serverAssert(vptr != NULL);
fptr = lpNext(zl, vptr);
}
/* 如果到这 fptr 仍为空,则 fptr 指向了终点,返回 C_ERR */
if (fptr == NULL) return C_ERR;
/* Grab pointer to the value (fptr points to the field) */
/* vptr 指向 fptr 下一个节点,即指向了 field 对应的 value */
vptr = lpNext(zl, fptr);
serverAssert(vptr != NULL);
/* fptr, vptr now point to the first or next pair */
/* 改变迭代器中的 fptr 和 vptr */
hi->fptr = fptr;
hi->vptr = vptr;
/* 编码类型为 hashtable */
} else if (hi->encoding == OBJ_ENCODING_HT) {
if ((hi->de = dictNext(hi->di)) == NULL) return C_ERR;
} else {
serverPanic("Unknown hash encoding");
}
return C_OK;
}
/* Get the field or value at iterator cursor, for an iterator on a hash value
* encoded as a listpack. Prototype is similar to `hashTypeGetFromListpack`. */
/* 在迭代器游标处获取 field 或 value ,哈希类型为 listpack 时使用*/
void hashTypeCurrentFromListpack(hashTypeIterator *hi, int what,
unsigned char **vstr,
unsigned int *vlen,
long long *vll)
{
serverAssert(hi->encoding == OBJ_ENCODING_LISTPACK);
/* what & OBJ_HASH_KEY == true ,则取出 field ,否则取出 value */
if (what & OBJ_HASH_KEY) {
*vstr = lpGetValue(hi->fptr, vlen, vll);
} else {
*vstr = lpGetValue(hi->vptr, vlen, vll);
}
}
/* Get the field or value at iterator cursor, for an iterator on a hash value
* encoded as a hash table. Prototype is similar to
* `hashTypeGetFromHashTable`. */
/* 在迭代器游标处获取 field 或 value ,哈希类型为 hashtable 时使用*/
sds hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what) {
serverAssert(hi->encoding == OBJ_ENCODING_HT);
if (what & OBJ_HASH_KEY) {
return dictGetKey(hi->de);
} else {
return dictGetVal(hi->de);
}
}
/* Higher level function of hashTypeCurrent*() that returns the hash value
* at current iterator position.
*
* The returned element is returned by reference in either *vstr and *vlen if
* it's returned in string form, or stored in *vll if it's returned as
* a number.
*
* If *vll is populated *vstr is set to NULL, so the caller
* can always check the function return by checking the return value
* type checking if vstr == NULL. */
/* hashTypeCurrent*()的高级函数,是返回当前哈希迭代器位置的元素
* 如果它返回的元素是字符串类型的,则既填充 *vstr 又填充 *vlen ,如果它返回的是整数类型的,则只填充 *vil
* 那么调用方始终可以通过检查 vstr 是否为空的方法,来得知元素被保存在了哪个变量中 */
void hashTypeCurrentObject(hashTypeIterator *hi, int what, unsigned char **vstr, unsigned int *vlen, long long *vll) {
/* 根据编码类型,调用不同的函数获取值 */
if (hi->encoding == OBJ_ENCODING_LISTPACK) {
*vstr = NULL;
hashTypeCurrentFromListpack(hi, what, vstr, vlen, vll);
} else if (hi->encoding == OBJ_ENCODING_HT) {
sds ele = hashTypeCurrentFromHashTable(hi, what);
*vstr = (unsigned char*) ele;
*vlen = sdslen(ele);
} else {
serverPanic("Unknown hash encoding");
}
}
/* Return the key or value at the current iterator position as a new
* SDS string. */
/* 返回当前迭代器位置的 key 或 value ,返回的是一个新创建的 SDS 字符串(或者说副本) */
sds hashTypeCurrentObjectNewSds(hashTypeIterator *hi, int what) {
unsigned char *vstr;
unsigned int vlen;
long long vll;
hashTypeCurrentObject(hi,what,&vstr,&vlen,&vll);
if (vstr) return sdsnewlen(vstr,vlen);
return sdsfromlonglong(vll);
}
/* 在写模式下查找key,如果查找不到则创建该key */
robj *hashTypeLookupWriteOrCreate(client *c, robj *key) {
robj *o = lookupKeyWrite(c->db,key);
if (checkType(c,o,OBJ_HASH)) return NULL;
if (o == NULL) {
o = createHashObject();
dbAdd(c->db,key,o);
}
return o;
}
/* 将哈希编码类型从 listpack 转化为 hashtable */
void hashTypeConvertListpack(robj *o, int enc) {
serverAssert(o->encoding == OBJ_ENCODING_LISTPACK);
/* enc 指定了转化类型,如果转化类型为 listpack 则什么也不做 */
if (enc == OBJ_ENCODING_LISTPACK) {
/* Nothing to do... */
} else if (enc == OBJ_ENCODING_HT) {
hashTypeIterator *hi;
dict *dict;
int ret;
/* 初始化迭代器 hi 和字典(哈希表) dict */
hi = hashTypeInitIterator(o);
dict = dictCreate(&hashDictType);
/* Presize the dict to avoid rehashing */
/* 预分配内存空间,避免rehashing重新分配空间导致性能下降 */
dictExpand(dict,hashTypeLength(o));
/* 利用迭代器遍历 listpack */
while (hashTypeNext(hi) != C_ERR) {
sds key, value;
/* 取出迭代器当前游标下的 key 和 value */
key = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_KEY);
value = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_VALUE);
/* 将 key 和 value 加入 dict */
ret = dictAdd(dict, key, value);
/* key 和 value 添加到 dict 失败,释放 key, value, hi ,并向服务器报错 */
if (ret != DICT_OK) {
sdsfree(key); sdsfree(value); /* Needed for gcc ASAN */
hashTypeReleaseIterator(hi); /* Needed for gcc ASAN */
serverLogHexDump(LL_WARNING,"listpack with dup elements dump",
o->ptr,lpBytes(o->ptr));
serverPanic("Listpack corruption detected");
}
}
/* 释放迭代器内存空间 */
hashTypeReleaseIterator(hi);
/* 释放原来的 listpack */
zfree(o->ptr);
/* 修改 o 的编码类型标识为 hashtable */
o->encoding = OBJ_ENCODING_HT;
/* 修改 o 的 ptr 指针指向 dict */
o->ptr = dict;
} else {
serverPanic("Unknown hash encoding");
}
}
/* 哈希编码类型转换 */
void hashTypeConvert(robj *o, int enc) {
if (o->encoding == OBJ_ENCODING_LISTPACK) {
hashTypeConvertListpack(o, enc);
} else if (o->encoding == OBJ_ENCODING_HT) {
serverPanic("Not implemented");
} else {
serverPanic("Unknown hash encoding");
}
}
/* This is a helper function for the COPY command.
* Duplicate a hash object, with the guarantee that the returned object
* has the same encoding as the original one.
*
* The resulting object always has refcount set to 1 */
/* 这是一个用于 COPY 命令的辅助函数。
* 复制一个哈希对象,并保证返回的对象具有与原始对象相同的编码。
* 返回的对象总是将 refcount(被引用次数) 设置为1 */
robj *hashTypeDup(robj *o) {
robj *hobj;
hashTypeIterator *hi;
serverAssert(o->type == OBJ_HASH);
if(o->encoding == OBJ_ENCODING_LISTPACK) {
unsigned char *zl = o->ptr;
size_t sz = lpBytes(zl);
/* 为副本开辟内存空间 */
unsigned char *new_zl = zmalloc(sz);
/* 复制原来的 listpack */
memcpy(new_zl, zl, sz);
/* 创建新的哈希类型对象,并设置编码类型标识为 listpack */
hobj = createObject(OBJ_HASH, new_zl);
hobj->encoding = OBJ_ENCODING_LISTPACK;
} else if(o->encoding == OBJ_ENCODING_HT){
/* 创建副本的字典(哈希表) dict 并预分配空间 */
dict *d = dictCreate(&hashDictType);
dictExpand(d, dictSize((const dict*)o->ptr));
hi = hashTypeInitIterator(o);
/* 利用迭代器遍历源 hashtable */
while (hashTypeNext(hi) != C_ERR) {
sds field, value;
sds newfield, newvalue;
/* Extract a field-value pair from an original hash object.*/
/* 从原哈希对象中提取一对 field-value */
field = hashTypeCurrentFromHashTable(hi, OBJ_HASH_KEY);
value = hashTypeCurrentFromHashTable(hi, OBJ_HASH_VALUE);
/* 对 field, value 进行复制 */
newfield = sdsdup(field);
newvalue = sdsdup(value);
/* Add a field-value pair to a new hash object. */
/* 将 field-value 加入到新哈希对象中 */
dictAdd(d,newfield,newvalue);
}
hashTypeReleaseIterator(hi);
/* 创建新的哈希类型对象,并设置编码类型标识为 hashtable */
hobj = createObject(OBJ_HASH, d);
hobj->encoding = OBJ_ENCODING_HT;
} else {
serverPanic("Unknown hash encoding");
}
return hobj;
}
/* Create a new sds string from the listpack entry. */
/* 获取参数给定的 listpack 的 entry 中的值 ,并返回一个以该值创建的 sds 字符串 */
sds hashSdsFromListpackEntry(listpackEntry *e) {
return e->sval ? sdsnewlen(e->sval, e->slen) : sdsfromlonglong(e->lval);
}
/* Reply with bulk string from the listpack entry. */
/* 将参数给定的 listpack 的 entry 中的值回复给客户端 */
void hashReplyFromListpackEntry(client *c, listpackEntry *e) {
if (e->sval)
addReplyBulkCBuffer(c, e->sval, e->slen);
else
addReplyBulkLongLong(c, e->lval);
}
/* Return random element from a non empty hash.
* 'key' and 'val' will be set to hold the element.
* The memory in them is not to be freed or modified by the caller.
* 'val' can be NULL in which case it's not extracted. */
/* 从一个非空哈希类型对象中返回随机元素
* 'key' 和 'val' 将持有该元素
* 它们的内存不能被释放或被调用者修改。
* 'val' 可以是 NULL,在这种情况下它不会被进行赋值 */
void hashTypeRandomElement(robj *hashobj, unsigned long hashsize, listpackEntry *key, listpackEntry *val) {
if (hashobj->encoding == OBJ_ENCODING_HT) {
/* 调用 dictGetFairRandomKey 随机获取一个 dictEntry */
dictEntry *de = dictGetFairRandomKey(hashobj->ptr);
sds s = dictGetKey(de);
key->sval = (unsigned char*)s;
key->slen = sdslen(s);
/* val 不为 NULL, 要求获取 value */
if (val) {
sds s = dictGetVal(de);
val->sval = (unsigned char*)s;
val->slen = sdslen(s);
}
} else if (hashobj->encoding == OBJ_ENCODING_LISTPACK) {
/* 调用 lpRandomPair 随机获取一个 field 或 field-value (若 val 不为 NULL) */
lpRandomPair(hashobj->ptr, hashsize, key, val);
} else {
serverPanic("Unknown hash encoding");
}
}
/*-----------------------------------------------------------------------------
* Hash type commands
*----------------------------------------------------------------------------*/
/* 处理hsetnx命令的函数 */
void hsetnxCommand(client *c) {
robj *o;
/* 查找 key */
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
/* key 已存在,向客户端回复0 */
if (hashTypeExists(o, c->argv[2]->ptr)) {
addReply(c, shared.czero);
} else {
/* 尝试哈希编码类型转换(该函数仅判断输入 field 和 value 长度是否大于限制) */
hashTypeTryConversion(o,c->argv,2,3);
/* 设置 field-value */
hashTypeSet(o,c->argv[2]->ptr,c->argv[3]->ptr,HASH_SET_COPY);
/* 向客户端回复1 */
addReply(c, shared.cone);
/* 发送数据库被修改的通知 */
signalModifiedKey(c,c->db,c->argv[1]);
/* 发送事件通知 */
notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
/* 距离上次成功保存的数据变动次数 + 1 */
server.dirty++;
}
}
/* 处理hset命令的函数 */
void hsetCommand(client *c) {
int i, created = 0;
robj *o;
/* 该命令输入参数个数必为偶数,若为奇数则输入有误,将导致报错并返回 */
if ((c->argc % 2) == 1) {
addReplyErrorArity(c);
return;
}
/* 查找key */
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
hashTypeTryConversion(o,c->argv,2,c->argc-1);
/* 解析输入的 field value 参数 */
for (i = 2; i < c->argc; i += 2)
/* 设置 field-value ,同时给记录新创建的 field-value 数量的变量 created 计数
* 若 field-value 是新建的 hashTypeSet 返回0,所以需要前面加上 '!' 逻辑非符号转化为1 */
created += !hashTypeSet(o,c->argv[i]->ptr,c->argv[i+1]->ptr,HASH_SET_COPY);
/* HMSET (deprecated) and HSET return value is different. */
/* HMSET 命令 (不推荐使用,已过时,可被HSET替代) 和 HSET 命令的返回值不相同 */
char *cmdname = c->argv[0]->ptr;
if (cmdname[1] == 's' || cmdname[1] == 'S') {
/* HSET */
/* HSET 返回值为新的 field-value 个数 */
addReplyLongLong(c, created);
} else {
/* HMSET */
/* HMSET 返回值为 'OK' */
addReply(c, shared.ok);
}
signalModifiedKey(c,c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
server.dirty += (c->argc - 2)/2;
}
/* 处理hincrby命令的函数 */
void hincrbyCommand(client *c) {
long long value, incr, oldvalue;
robj *o;
sds new;
unsigned char *vstr;
unsigned int vlen;
/* 取出输入参数 increment 并赋值给 incr */
if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
/* 取出哈希对象的 value */
if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&value) == C_OK) {
if (vstr) {
/* 将哈希对象的 value 转换为 long long 并赋值给 value */
if (string2ll((char*)vstr,vlen,&value) == 0) {
/* value 无法转换,报错并返回 */
addReplyError(c,"hash value is not an integer");
return;
}
} /* Else hashTypeGetValue() already stored it into &value */
} else {
/* 哈希对象不存在,value 设置为默认值0 */
value = 0;
}
oldvalue = value;
/* 检查原 value 加上 incr 是否会溢出 */
if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
(incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
addReplyError(c,"increment or decrement would overflow");
return;
}
/* value 加上 incr */
value += incr;
/* new 是以 value 的值创建而来的字符串 */
new = sdsfromlonglong(value);
hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
addReplyLongLong(c,value);
signalModifiedKey(c,c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_HASH,"hincrby",c->argv[1],c->db->id);
server.dirty++;
}
/* 处理 hincrbyfloat 命令的函数,具体实现与 hincrby 类似 */
void hincrbyfloatCommand(client *c) {
long double value, incr;
long long ll;
robj *o;
sds new;
unsigned char *vstr;
unsigned int vlen;
if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&ll) == C_OK) {
if (vstr) {
if (string2ld((char*)vstr,vlen,&value) == 0) {
addReplyError(c,"hash value is not a float");
return;
}
} else {
value = (long double)ll;
}
} else {
value = 0;
}
value += incr;
if (isnan(value) || isinf(value)) {
addReplyError(c,"increment would produce NaN or Infinity");
return;
}
char buf[MAX_LONG_DOUBLE_CHARS];
int len = ld2string(buf,sizeof(buf),value,LD_STR_HUMAN);
new = sdsnewlen(buf,len);
hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
addReplyBulkCBuffer(c,buf,len);
signalModifiedKey(c,c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_HASH,"hincrbyfloat",c->argv[1],c->db->id);
server.dirty++;
/* Always replicate HINCRBYFLOAT as an HSET command with the final value
* in order to make sure that differences in float precision or formatting
* will not create differences in replicas or after an AOF restart. */
/* 始终将 HINCRBYFLOAT 作为 HSET 命令进行复制,
* 以确保浮点数精度或格式化的差异不会在复制中或AOF重启后产生差异 */
robj *newobj;
newobj = createRawStringObject(buf,len);
rewriteClientCommandArgument(c,0,shared.hset);
rewriteClientCommandArgument(c,3,newobj);
decrRefCount(newobj);
}
/* 查找指定的 field-value 并将 value 回复给客户端 */
static void addHashFieldToReply(client *c, robj *o, sds field) {
if (o == NULL) {
addReplyNull(c);
return;
}
unsigned char *vstr = NULL;
unsigned int vlen = UINT_MAX;
long long vll = LLONG_MAX;
if (hashTypeGetValue(o, field, &vstr, &vlen, &vll) == C_OK) {
if (vstr) {
addReplyBulkCBuffer(c, vstr, vlen);
} else {
addReplyBulkLongLong(c, vll);
}
} else {
addReplyNull(c);
}
}
/* 处理 hget 命令的函数 */
void hgetCommand(client *c) {
robj *o;
/* 查找 key 对应的 redis 对象 ,找到后检查对象类型是否为 hashtable */
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL ||
checkType(c,o,OBJ_HASH)) return;
/* 调用 addHashFieldToReply 进行查找与回复 value 给客户端 */
addHashFieldToReply(c, o, c->argv[2]->ptr);
}
/* 处理 hmget 命令的函数 */
void hmgetCommand(client *c) {
robj *o;
int i;
/* Don't abort when the key cannot be found. Non-existing keys are empty
* hashes, where HMGET should respond with a series of null bulks. */
/* 当找不到 key 的时候不要中止,不存在的 key 即空哈希对象 ,HMGET 应该用空值来回应 */
o = lookupKeyRead(c->db, c->argv[1]);
if (checkType(c,o,OBJ_HASH)) return;
/* 将以列表的形式回复客户端,这里设置列表的长度 */
addReplyArrayLen(c, c->argc-2);
/* 解析输入的 field 和 value 参数,并调用 addHashFieldToReply 进行查找与回复 value 给客户端 */
for (i = 2; i < c->argc; i++) {
addHashFieldToReply(c, o, c->argv[i]->ptr);
}
}
/* 处理 hdel 命令的函数 */
void hdelCommand(client *c) {
robj *o;
int j, deleted = 0, keyremoved = 0;