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
|
func hashGrow(t *maptype, h *hmap) {
bigger := uint8(1)
if !overLoadFactor(int64(h.count), h.B) {
//不是因为超过装载因子而扩容,则bigger置0
bigger = 0
//设置同大小扩容标志位
h.flags |= sameSizeGrow
}
//当前桶数组地址复制到老桶数组地址
oldbuckets := h.buckets
//申请新地址空间
newbuckets, nextOverflow := makeBucketArray(t, h.B+bigger)
//当前flags清空两个标志位
flags := h.flags &^ (iterator | oldIterator)
//如果map有迭代器在读
if h.flags&iterator != 0 {
//flags的老迭代器标志位置1
flags |= oldIterator
}
//设置map的新属性值
h.B += bigger
h.flags = flags
h.oldbuckets = oldbuckets
h.buckets = newbuckets
h.nevacuate = 0
h.noverflow = 0
if h.extra != nil && h.extra.overflow[0] != nil {
if h.extra.overflow[1] != nil {
throw("overflow is not nil")
}
h.extra.overflow[1] = h.extra.overflow[0]
h.extra.overflow[0] = nil
}
if nextOverflow != nil {
if h.extra == nil {
h.extra = new(mapextra)
}
h.extra.nextOverflow = nextOverflow
}
}
func (h *hmap) growing() bool {
return h.oldbuckets != nil
}
func growWork(t *maptype, h *hmap, bucket uintptr) {
//搬迁bucket桶
//和老桶掩码运算,确保搬迁老的桶下标
evacuate(t, h, bucket&h.oldbucketmask())
//没有搬迁完,则在搬一个,这次搬最小未搬迁的桶
if h.growing() {
evacuate(t, h, h.nevacuate)
}
}
func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
//获取老桶数组中oldbucket的桶地址
b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize))) //增长的大小,例如原先map的B=4,扩容后B=5则newbit=2^4
//用于计算新搬迁桶地址以及相关标志位
newbit := h.noldbuckets()
alg := t.key.alg
//判断是否搬迁了
if !evacuated(b) {
var (
x, y *bmap
xi, yi int
xk, yk unsafe.Pointer
xv, yv unsafe.Pointer
)
//x指向新桶数组中的老位置
x = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize)))
xi = 0
xk = add(unsafe.Pointer(x), dataOffset)
xv = add(xk, bucketCnt*uintptr(t.keysize))
if !h.sameSizeGrow() {
//真正扩容增长,y指向新桶数组的新位置
//和老位置的区别就是差newbit的个数
//按照取桶地址的方法,新B比老B多1位,也就是新的地址下标比老的下标多看hash值左边一位
y = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize)))
yi = 0
yk = add(unsafe.Pointer(y), dataOffset)
yv = add(yk, bucketCnt*uintptr(t.keysize))
}
//开始遍历b,进行搬迁
for ; b != nil; b = b.overflow(t) {
k := add(unsafe.Pointer(b), dataOffset)
v := add(k, bucketCnt*uintptr(t.keysize))
for i := 0; i < bucketCnt; i, k, v = i+1, add(k, uintptr(t.keysize)), add(v, uintptr(t.valuesize)) {
top := b.tophash[i]
if top == empty {
//标定搬迁空状态
b.tophash[i] = evacuatedEmpty
continue
}
//理论上不会出现小于minTopHash的值
if top < minTopHash {
throw("bad map state")
}
k2 := k
if t.indirectkey {
k2 = *((*unsafe.Pointer)(k2))
}
useX := true
if !h.sameSizeGrow() {
//计算hash值
hash := alg.hash(k2, uintptr(h.hash0))
//有迭代器访问的时候,这个和迭代器的遍历规则有关联,所以这里会判断
if h.flags&iterator != 0 {
//自己和自己对比,不一致,说明key是浮点NANs
if !t.reflexivekey && !alg.equal(k2, k2) {
//看旧桶的top值最后一位
if top&1 != 0 {
//hash值在新B位置0,即将这个key放到新桶数组中的老位置
hash |= newbit
} else {
//放到新位置
hash &^= newbit
}
//记录新的top值
top = uint8(hash >> (sys.PtrSize*8 - 8))
//top修正
if top < minTopHash {
top += minTopHash
}
}
}
//判断是是否是0即是否在新桶数组的老位置
useX = hash&newbit == 0
}
if useX {
//老桶中的key的tophash标记位迁移到x,即新桶数组的老位置
b.tophash[i] = evacuatedX
//xi等于8了,说明这个桶装满了,需要溢出桶
if xi == bucketCnt {
newx := h.newoverflow(t, x)
x = newx
xi = 0
xk = add(unsafe.Pointer(x), dataOffset)
xv = add(xk, bucketCnt*uintptr(t.keysize))
}
//设定新的tophash
x.tophash[xi] = top
if t.indirectkey {
*(*unsafe.Pointer)(xk) = k2
} else {
typedmemmove(t.key, xk, k)
}
if t.indirectvalue {
*(*unsafe.Pointer)(xv) = *(*unsafe.Pointer)(v)
} else {
typedmemmove(t.elem, xv, v)
}
//下一个
xi++
xk = add(xk, uintptr(t.keysize))
xv = add(xv, uintptr(t.valuesize))
} else {
//同上,只是把key放在新桶数组中的新位置
b.tophash[i] = evacuatedY
if yi == bucketCnt {
newy := h.newoverflow(t, y)
y = newy
yi = 0
yk = add(unsafe.Pointer(y), dataOffset)
yv = add(yk, bucketCnt*uintptr(t.keysize))
}
y.tophash[yi] = top
if t.indirectkey {
*(*unsafe.Pointer)(yk) = k2
} else {
typedmemmove(t.key, yk, k)
}
if t.indirectvalue {
*(*unsafe.Pointer)(yv) = *(*unsafe.Pointer)(v)
} else {
typedmemmove(t.elem, yv, v)
}
yi++
yk = add(yk, uintptr(t.keysize))
yv = add(yv, uintptr(t.valuesize))
}
}
}
//判断是否老桶数组还有迭代器在访问
if h.flags&oldIterator == 0 {
//没有,则处理一下,帮助gc
b = (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)))
if t.bucket.kind&kindNoPointers == 0 {
memclrHasPointers(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
} else {
memclrNoHeapPointers(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
}
}
}
//刚好,上面处理的桶是最小未处理桶
if oldbucket == h.nevacuate {
//最小未处理桶往后走一位
h.nevacuate = oldbucket + 1
//尝试往后判断1024次,看看当前的最小未处理桶是否已经处理了,以及是否有连续的已经处理的桶
//有的话,就更新这个最小未处理桶下标,让这个数值精确
stop := h.nevacuate + 1024
if stop > newbit {
stop = newbit
}
for h.nevacuate != stop && bucketEvacuated(t, h, h.nevacuate) {
h.nevacuate++
}
//已经处理完了
if h.nevacuate == newbit {
//oldbuckets指向空
h.oldbuckets = nil
//指向空,让gc处理
if h.extra != nil {
h.extra.overflow[1] = nil
}
//标志位清空
h.flags &^= sameSizeGrow
}
}
}
|