1
16
17 package allocator
18
19 import (
20 "errors"
21 "fmt"
22 "math/big"
23 "math/rand"
24 "sync"
25 "time"
26 )
27
28
29
30
31
32
33
34
35
36
37 type AllocationBitmap struct {
38
39 strategy bitAllocator
40
41 max int
42
43 rangeSpec string
44
45
46 lock sync.Mutex
47
48 count int
49
50 allocated *big.Int
51 }
52
53
54 var _ Interface = &AllocationBitmap{}
55 var _ Snapshottable = &AllocationBitmap{}
56
57
58 type bitAllocator interface {
59 AllocateBit(allocated *big.Int, max, count int) (int, bool)
60 }
61
62
63 func NewAllocationMap(max int, rangeSpec string) *AllocationBitmap {
64 return NewAllocationMapWithOffset(max, rangeSpec, 0)
65 }
66
67
68
69
70
71
72 func NewAllocationMapWithOffset(max int, rangeSpec string, offset int) *AllocationBitmap {
73 a := AllocationBitmap{
74 strategy: randomScanStrategyWithOffset{
75 rand: rand.New(rand.NewSource(time.Now().UnixNano())),
76 offset: offset,
77 },
78 allocated: big.NewInt(0),
79 count: 0,
80 max: max,
81 rangeSpec: rangeSpec,
82 }
83
84 return &a
85 }
86
87
88
89 func (r *AllocationBitmap) Allocate(offset int) (bool, error) {
90 r.lock.Lock()
91 defer r.lock.Unlock()
92
93
94 if offset < 0 || offset >= r.max {
95 return false, fmt.Errorf("offset %d out of range [0,%d]", offset, r.max)
96 }
97 if r.allocated.Bit(offset) == 1 {
98 return false, nil
99 }
100 r.allocated = r.allocated.SetBit(r.allocated, offset, 1)
101 r.count++
102 return true, nil
103 }
104
105
106
107 func (r *AllocationBitmap) AllocateNext() (int, bool, error) {
108 r.lock.Lock()
109 defer r.lock.Unlock()
110
111 next, ok := r.strategy.AllocateBit(r.allocated, r.max, r.count)
112 if !ok {
113 return 0, false, nil
114 }
115 r.count++
116 r.allocated = r.allocated.SetBit(r.allocated, next, 1)
117 return next, true, nil
118 }
119
120
121
122
123 func (r *AllocationBitmap) Release(offset int) error {
124 r.lock.Lock()
125 defer r.lock.Unlock()
126
127 if r.allocated.Bit(offset) == 0 {
128 return nil
129 }
130
131 r.allocated = r.allocated.SetBit(r.allocated, offset, 0)
132 r.count--
133 return nil
134 }
135
136 const (
137
138 notZero = uint64(^big.Word(0))
139 wordPower = (notZero>>8)&1 + (notZero>>16)&1 + (notZero>>32)&1
140 wordSize = 1 << wordPower
141 )
142
143
144
145 func (r *AllocationBitmap) ForEach(fn func(int)) {
146 r.lock.Lock()
147 defer r.lock.Unlock()
148
149 words := r.allocated.Bits()
150 for wordIdx, word := range words {
151 bit := 0
152 for word > 0 {
153 if (word & 1) != 0 {
154 fn((wordIdx * wordSize * 8) + bit)
155 word = word &^ 1
156 }
157 bit++
158 word = word >> 1
159 }
160 }
161 }
162
163
164
165 func (r *AllocationBitmap) Has(offset int) bool {
166 r.lock.Lock()
167 defer r.lock.Unlock()
168
169 return r.allocated.Bit(offset) == 1
170 }
171
172
173 func (r *AllocationBitmap) Free() int {
174 r.lock.Lock()
175 defer r.lock.Unlock()
176 return r.max - r.count
177 }
178
179
180 func (r *AllocationBitmap) Snapshot() (string, []byte) {
181 r.lock.Lock()
182 defer r.lock.Unlock()
183
184 return r.rangeSpec, r.allocated.Bytes()
185 }
186
187
188 func (r *AllocationBitmap) Restore(rangeSpec string, data []byte) error {
189 r.lock.Lock()
190 defer r.lock.Unlock()
191
192 if r.rangeSpec != rangeSpec {
193 return errors.New("the provided range does not match the current range")
194 }
195
196 r.allocated = big.NewInt(0).SetBytes(data)
197 r.count = countBits(r.allocated)
198
199 return nil
200 }
201
202
203 func (r *AllocationBitmap) Destroy() {
204 }
205
206
207
208
209 type randomScanStrategy struct {
210 rand *rand.Rand
211 }
212
213 func (rss randomScanStrategy) AllocateBit(allocated *big.Int, max, count int) (int, bool) {
214 if count >= max {
215 return 0, false
216 }
217 offset := rss.rand.Intn(max)
218 for i := 0; i < max; i++ {
219 at := (offset + i) % max
220 if allocated.Bit(at) == 0 {
221 return at, true
222 }
223 }
224 return 0, false
225 }
226
227 var _ bitAllocator = randomScanStrategy{}
228
229
230
231
232
233 type randomScanStrategyWithOffset struct {
234 rand *rand.Rand
235 offset int
236 }
237
238 func (rss randomScanStrategyWithOffset) AllocateBit(allocated *big.Int, max, count int) (int, bool) {
239 if count >= max {
240 return 0, false
241 }
242
243 subrangeMax := max - rss.offset
244
245 start := rss.rand.Intn(subrangeMax)
246 for i := 0; i < subrangeMax; i++ {
247 at := rss.offset + ((start + i) % subrangeMax)
248 if allocated.Bit(at) == 0 {
249 return at, true
250 }
251 }
252
253 start = rss.rand.Intn(rss.offset)
254
255 for i := 0; i < rss.offset; i++ {
256 at := (start + i) % rss.offset
257 if allocated.Bit(at) == 0 {
258 return at, true
259 }
260 }
261 return 0, false
262 }
263
264 var _ bitAllocator = randomScanStrategyWithOffset{}
265
View as plain text