forked from wasilibs/nottinygc
-
Notifications
You must be signed in to change notification settings - Fork 2
/
intmap.go
135 lines (119 loc) · 2.84 KB
/
intmap.go
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
// Copyright wasilibs authors
// SPDX-License-Identifier: MIT
package nottinygc
import "C"
import "unsafe"
// Aim for initial over on the order of a few kilobytes.
const (
initialBuckets = 512
numEmbedded = 8
)
type item struct {
key uintptr
val uintptr
}
type extraNode struct {
next *extraNode
item item
}
type bucket struct {
embedded [numEmbedded]item
extra *extraNode
count byte
}
// intMap is a map from int to int. As it is used to cache descriptors within
// allocation, it cannot itself allocate using the Go heap and uses malloc
// instead. It also takes advantage of knowing we never replace values, so it
// does not support replacement.
type intMap struct {
buckets []bucket
count int
}
func newIntMap() intMap {
return intMap{
buckets: newBuckets(initialBuckets),
count: 0,
}
}
func (m *intMap) put(key uintptr, val uintptr) {
if float64(m.count+1) > float64(len(m.buckets))*0.75 {
m.resize()
}
doPut(m.buckets, key, val)
m.count++
}
func doPut(buckets []bucket, key uintptr, val uintptr) {
pos := hash(key) % uintptr(len(buckets))
b := &buckets[pos]
if b.count < numEmbedded {
b.embedded[b.count] = item{key: key, val: val}
} else {
e := newExtraNode()
e.item = item{key: key, val: val}
e.next = b.extra
b.extra = e
}
b.count++
}
func (m *intMap) resize() {
newSz := len(m.buckets) * 2
newBkts := newBuckets(newSz)
for i := 0; i < len(m.buckets); i++ {
b := &m.buckets[i]
for j := 0; j < int(b.count); j++ {
if j < numEmbedded {
doPut(newBkts, b.embedded[j].key, b.embedded[j].val)
} else {
for n := b.extra; n != nil; {
doPut(newBkts, n.item.key, n.item.val)
next := n.next
cfree(unsafe.Pointer(n))
n = next
}
}
}
}
cfree(unsafe.Pointer(&m.buckets[0]))
m.buckets = newBkts
}
func (m *intMap) get(key uintptr) (uintptr, bool) {
pos := hash(key) % uintptr(len(m.buckets))
b := &m.buckets[pos]
for i := 0; i < int(b.count); i++ {
if i < numEmbedded {
if b.embedded[i].key == key {
return b.embedded[i].val, true
}
} else {
for n := b.extra; n != nil; n = n.next {
if n.item.key == key {
return n.item.val, true
}
}
break
}
}
return 0, false
}
func hash(key uintptr) uintptr {
// Use Java algorithm for cheap and easy handling of aligned values.
// There are better ones with more operations, we can try later if needed.
return key ^ (key >> 16)
}
func newBuckets(size int) []bucket {
sz := unsafe.Sizeof(bucket{}) * uintptr(size)
bucketsArr := cmalloc(sz)
for i := uintptr(0); i < sz; i++ {
*(*byte)(unsafe.Pointer(uintptr(bucketsArr) + i)) = 0
}
buckets := unsafe.Slice((*bucket)(bucketsArr), size)
return buckets
}
func newExtraNode() *extraNode {
sz := unsafe.Sizeof(extraNode{})
arr := cmalloc(sz)
for i := uintptr(0); i < sz; i++ {
*(*byte)(unsafe.Pointer(uintptr(arr) + i)) = 0
}
return (*extraNode)(arr)
}