3 var <user, initTemp, temp, perm, mask, permFreed;
6 *new { arg user=0, initTemp = 1000;
7 if (user > 31) { "NodeIDAllocator user id > 31".error; ^nil };
8 ^super.newCopyArgs(user, initTemp).reset
14 permFreed = IdentitySet.new;
19 temp = (x + 1).wrap(initTemp, 0x03FFFFFF);
24 if(permFreed.size > 0) {
25 x = permFreed.minItem;
29 perm = (x + 1).min(initTemp - 1);
34 // should not add a temp node id to the freed-permanent collection
35 id = id bitAnd: 0x03FFFFFF;
36 if(id < initTemp) { permFreed.add(id) }
43 var <address, <size, <>next;
44 *new { arg address, size;
45 ^super.newCopyArgs(address, size)
51 // THIS IS THE RECOMMENDED ALLOCATOR FOR BUSES AND BUFFERS
52 var size, array, freeLists, pos=0;
54 *new { arg size, pos=0;
55 ^super.newCopyArgs(size, Array.newClear(size), Array.newClear(32), pos)
58 var sizeClass, node, address;
60 sizeClass = n.log2Ceil;
62 node = freeLists.at(sizeClass);
64 freeLists.put(sizeClass, node.next);
67 if (pos + n <= size, {
68 array.put(pos, PowerOfTwoBlock(pos, n));
76 var node, sizeClass,next;
78 if((node = array.at(address)).notNil,{
80 sizeClass = node.size.log2Ceil;
81 node.next = freeLists.at(sizeClass);
82 freeLists.put(sizeClass, node);
83 array.put(address, nil);
88 ^array.select({ arg b; b.notNil })
94 // implements a least recently used ID allocator.
97 var array, size, head=0, tail=0;
100 ^super.newCopyArgs(lo, hi).init
104 array = Array.newClear(size);
105 for(lo, hi-1, { arg i, j; array.put(j, i) });
111 nextIndex = (head+1) % size;
112 if ( nextIndex == tail, { ^nil }); // full
118 if (head == tail, { ^nil }); // empty
120 tail = (tail+1) % size;
127 var lo, hi, freeList, next;
130 ^super.newCopyArgs(lo, hi).init
136 if (freeList.size > 0, { ^freeList.pop });
137 if (next < hi, { ^next = next + 1; });
141 freeList = freeList.add(inIndex);
150 ^super.newCopyArgs(lo, hi).init
156 ^next = (next + 1).wrap(lo,hi)
161 // by hjh: for better handling of dynamic allocation
165 var <start, <size, <>used = false; // assume free; owner must say otherwise
167 *new { |start, size| ^super.newCopyArgs(start, size) }
172 ^(start < block.start and: { start + size >= block.start })
173 or: { start > block.start and: { block.start + block.size >= start } }
178 this.adjoins(block).if({
179 ^this.class.new(newstart = min(start, block.start),
180 max(start + size, block.start + block.size) - newstart)
188 ^[this.class.new(start, span),
189 this.class.new(start + span, size - span)]
197 storeArgs { ^[start, size, used] }
198 printOn { |stream| this.storeOn(stream) }
202 ContiguousBlockAllocator {
203 var size, array, freed, pos, top;
205 *new { |size, pos = 0|
206 ^super.newCopyArgs(size, Array.newClear(size).put(pos, ContiguousBlock(pos, size-pos)),
207 IdentityDictionary.new, pos, pos);
212 (block = this.findAvailable(n)).notNil.if({
213 ^this.prReserve(block.start, n, block).start
217 reserve { |address, size = 1, warn = true|
219 ((block = array[address] ?? { this.findNext(address) }).notNil and:
221 { address + size > block.start } }).if({
222 warn.if({ "The block at (%, %) is already in use and cannot be reserved."
223 .format(address, size).warn; });
225 (block.start == address).if({
226 new = this.prReserve(address, size, block);
229 ((block = this.findPrevious(address)).notNil and:
231 { block.start + block.size > address } }).if({
232 warn.if({ "The block at (%, %) is already in use and cannot be reserved."
233 .format(address, size).warn; });
235 new = this.prReserve(address, size, nil, block);
246 ((block = array[address]).notNil and: { block.used }).if({
248 this.addToFreed(block);
249 ((prev = this.findPrevious(address)).notNil and: { prev.used.not }).if({
250 (temp = prev.join(block)).notNil.if({
251 // if block is the last one, reduce the top
252 (block.start == top).if({ top = temp.start });
253 array[temp.start] = temp;
254 array[block.start] = nil;
255 this.removeFromFreed(prev).removeFromFreed(block);
256 (top > temp.start).if({ this.addToFreed(temp); });
260 ((next = this.findNext(block.start)).notNil and: { next.used.not }).if({
261 (temp = next.join(block)).notNil.if({
262 // if next is the last one, reduce the top
263 (next.start == top).if({ top = temp.start });
264 array[temp.start] = temp;
265 array[next.start] = nil;
266 this.removeFromFreed(next).removeFromFreed(block);
267 (top > temp.start).if({ this.addToFreed(temp); });
274 ^array.select({ arg b; b.notNil and: { b.used } })
278 (freed[n].size > 0).if({ ^freed[n].choose });
280 freed.keysValuesDo({ |size, set|
281 (size >= n and: { set.size > 0 }).if({
286 (top + n > size or: { array[top].used }).if({ ^nil });
291 freed[block.size].isNil.if({ freed[block.size] = IdentitySet.new });
292 freed[block.size].add(block);
295 removeFromFreed { |block|
296 freed[block.size].tryPerform(\remove, block);
297 // I tested without gc; performance is about half as efficient without it
298 (freed[block.size].size == 0).if({ freed.removeAt(block.size) });
301 findPrevious { |address|
302 forBy(address-1, pos, -1, { |i|
303 array[i].notNil.if({ ^array[i] });
310 (temp = array[address]).notNil.if({
311 ^array[temp.start + temp.size]
313 for(address+1, top, { |i|
314 array[i].notNil.if({ ^array[i] });
320 prReserve { |address, size, availBlock, prevBlock|
322 (availBlock.isNil and: { prevBlock.isNil }).if({
323 prevBlock = this.findPrevious(address);
325 availBlock = availBlock ? prevBlock;
326 (availBlock.start < address).if({
327 #leftover, availBlock = this.prSplit(availBlock,
328 address - availBlock.start, false);
330 ^this.prSplit(availBlock, size, true)[0];
333 prSplit { |availBlock, n, used = true|
335 #new, leftover = availBlock.split(n);
337 this.removeFromFreed(availBlock);
338 used.not.if({ this.addToFreed(new) });
339 array[new.start] = new;
341 array[leftover.start] = leftover;
342 top = max(top, leftover.start);
343 (top > leftover.start).if({ this.addToFreed(leftover); });
349 Post << text << ":\n\nArray:\n";
351 item.notNil.if({ Post << i << ": " << item << "\n"; });
353 Post << "\nFree sets:\n";
354 freed.keysValuesDo({ |size, set|
355 Post << size << ": " <<< set << "\n";