@nxtedition/slice 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +45 -0
- package/lib/index.js +462 -0
- package/lib/index.test.d.ts +1 -0
- package/package.json +24 -0
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import util from 'node:util';
|
|
2
|
+
export declare class Slice {
|
|
3
|
+
buffer: Buffer;
|
|
4
|
+
byteOffset: number;
|
|
5
|
+
byteLength: number;
|
|
6
|
+
maxByteLength: number;
|
|
7
|
+
static EMPTY_BUF: Buffer;
|
|
8
|
+
constructor(buffer?: Buffer<ArrayBufferLike>, byteOffset?: number, byteLength?: number, maxByteLength?: number);
|
|
9
|
+
reset(): void;
|
|
10
|
+
get length(): number;
|
|
11
|
+
copy(target: Uint8Array | Slice, targetStart?: number, sourceStart?: number, sourceEnd?: number): number;
|
|
12
|
+
compare(target: Uint8Array | Slice, targetStart?: number, targetEnd?: number, sourceStart?: number, sourceEnd?: number): -1 | 0 | 1;
|
|
13
|
+
write(string: string, offset?: number, length?: number, encoding?: BufferEncoding): number;
|
|
14
|
+
set(source: Buffer | Slice | null | undefined, offset?: number): void;
|
|
15
|
+
at(index: number): number;
|
|
16
|
+
test(expr: {
|
|
17
|
+
test: (buffer: Buffer, byteOffset: number, byteLength: number) => boolean;
|
|
18
|
+
}): boolean;
|
|
19
|
+
toString(encoding?: BufferEncoding, start?: number, end?: number): string;
|
|
20
|
+
toBuffer(start?: number, end?: number): Buffer;
|
|
21
|
+
[Symbol.toStringTag](): string;
|
|
22
|
+
[util.inspect.custom](depth: number, options: util.InspectOptionsStylized, inspect: typeof util.inspect): string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A buddy allocator using XOR bitmap for efficient coalescing.
|
|
26
|
+
* Each bit tracks the XOR of a buddy pair's allocation state.
|
|
27
|
+
* When a bit is 0, both buddies have the same state (both free or both allocated).
|
|
28
|
+
* When a bit is 1, the buddies have different states.
|
|
29
|
+
*/
|
|
30
|
+
export declare class BuddyAllocator {
|
|
31
|
+
#private;
|
|
32
|
+
constructor(poolTotal?: number);
|
|
33
|
+
realloc(slice: Slice, byteLength: number): Slice;
|
|
34
|
+
get stats(): {
|
|
35
|
+
size: number;
|
|
36
|
+
padding: number;
|
|
37
|
+
ratio: number;
|
|
38
|
+
poolTotal: number;
|
|
39
|
+
poolUsed: number;
|
|
40
|
+
poolSize: number;
|
|
41
|
+
poolCount: number;
|
|
42
|
+
poolList: number[];
|
|
43
|
+
};
|
|
44
|
+
isFromPool(slice: Slice | null | undefined): boolean;
|
|
45
|
+
}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import util from 'node:util'
|
|
2
|
+
|
|
3
|
+
const EMPTY_BUF = Buffer.alloc(0)
|
|
4
|
+
|
|
5
|
+
export class Slice {
|
|
6
|
+
buffer = EMPTY_BUF
|
|
7
|
+
byteOffset = 0
|
|
8
|
+
byteLength = 0
|
|
9
|
+
maxByteLength = 0
|
|
10
|
+
|
|
11
|
+
static EMPTY_BUF = EMPTY_BUF
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
buffer = Slice.EMPTY_BUF,
|
|
15
|
+
byteOffset = 0,
|
|
16
|
+
byteLength = buffer.byteLength,
|
|
17
|
+
maxByteLength = byteLength,
|
|
18
|
+
) {
|
|
19
|
+
if (!(buffer instanceof Buffer)) {
|
|
20
|
+
throw new TypeError('buffer must be a Buffer')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (byteOffset < 0 || !Number.isInteger(byteOffset)) {
|
|
24
|
+
throw new RangeError(`Invalid byteOffset: ${byteOffset}`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (byteLength < 0 || !Number.isInteger(byteLength)) {
|
|
28
|
+
throw new RangeError(`Invalid byteLength: ${byteLength}`)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (
|
|
32
|
+
maxByteLength < byteLength ||
|
|
33
|
+
maxByteLength > buffer.byteLength ||
|
|
34
|
+
!Number.isInteger(maxByteLength)
|
|
35
|
+
) {
|
|
36
|
+
throw new RangeError(`Invalid maxByteLength: ${maxByteLength}`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.buffer = buffer
|
|
40
|
+
this.byteOffset = byteOffset
|
|
41
|
+
this.byteLength = byteLength
|
|
42
|
+
this.maxByteLength = maxByteLength
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
reset() {
|
|
46
|
+
this.buffer = Slice.EMPTY_BUF
|
|
47
|
+
this.byteOffset = 0
|
|
48
|
+
this.byteLength = 0
|
|
49
|
+
this.maxByteLength = 0
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get length() {
|
|
53
|
+
return this.byteLength
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
copy(
|
|
57
|
+
target ,
|
|
58
|
+
targetStart ,
|
|
59
|
+
sourceStart ,
|
|
60
|
+
sourceEnd ,
|
|
61
|
+
) {
|
|
62
|
+
if (target instanceof Slice) {
|
|
63
|
+
if (targetStart === undefined) {
|
|
64
|
+
targetStart = target.byteOffset
|
|
65
|
+
} else {
|
|
66
|
+
targetStart += target.byteOffset
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
target = target.buffer
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (sourceStart === undefined) {
|
|
73
|
+
sourceStart = this.byteOffset
|
|
74
|
+
} else {
|
|
75
|
+
sourceStart += this.byteOffset
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (sourceEnd === undefined) {
|
|
79
|
+
sourceEnd = this.byteLength + this.byteOffset
|
|
80
|
+
} else {
|
|
81
|
+
sourceEnd += this.byteOffset
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (this.buffer === target && sourceStart === targetStart) {
|
|
85
|
+
return sourceEnd - sourceStart
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return this.buffer.copy(target, targetStart, sourceStart, sourceEnd)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
compare(
|
|
92
|
+
target ,
|
|
93
|
+
targetStart ,
|
|
94
|
+
targetEnd ,
|
|
95
|
+
sourceStart ,
|
|
96
|
+
sourceEnd ,
|
|
97
|
+
) {
|
|
98
|
+
if (target instanceof Slice) {
|
|
99
|
+
if (targetStart === undefined) {
|
|
100
|
+
targetStart = target.byteOffset
|
|
101
|
+
} else {
|
|
102
|
+
targetStart += target.byteOffset
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (targetEnd === undefined) {
|
|
106
|
+
targetEnd = target.byteLength + target.byteOffset
|
|
107
|
+
} else {
|
|
108
|
+
targetEnd += target.byteOffset
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
target = target.buffer
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (sourceStart === undefined) {
|
|
115
|
+
sourceStart = this.byteOffset
|
|
116
|
+
} else {
|
|
117
|
+
sourceStart += this.byteOffset
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (sourceEnd === undefined) {
|
|
121
|
+
sourceEnd = this.byteLength + this.byteOffset
|
|
122
|
+
} else {
|
|
123
|
+
sourceEnd += this.byteOffset
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return this.buffer.compare(target, targetStart, targetEnd, sourceStart, sourceEnd)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
write(string , offset , length , encoding ) {
|
|
130
|
+
if (offset === undefined) {
|
|
131
|
+
offset = this.byteOffset
|
|
132
|
+
} else {
|
|
133
|
+
offset += this.byteOffset
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (length === undefined) {
|
|
137
|
+
length = this.byteLength + this.byteOffset - offset
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return this.buffer.write(string, offset, length, encoding)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
set(source , offset ) {
|
|
144
|
+
if (source == null) {
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (offset === undefined) {
|
|
149
|
+
offset = this.byteOffset
|
|
150
|
+
} else {
|
|
151
|
+
offset += this.byteOffset
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
source.copy(this.buffer, offset)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
at(index ) {
|
|
158
|
+
return index >= 0
|
|
159
|
+
? this.buffer[this.byteOffset + index]
|
|
160
|
+
: this.buffer[this.byteOffset + this.byteLength + index]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
test(expr
|
|
164
|
+
|
|
165
|
+
) {
|
|
166
|
+
return expr.test(this.buffer, this.byteOffset, this.byteLength)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
toString(encoding , start , end ) {
|
|
170
|
+
if (start === undefined) {
|
|
171
|
+
start = this.byteOffset
|
|
172
|
+
} else {
|
|
173
|
+
start += this.byteOffset
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (end === undefined) {
|
|
177
|
+
end = this.byteLength + this.byteOffset
|
|
178
|
+
} else {
|
|
179
|
+
end += this.byteOffset
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return this.buffer.toString(encoding, start, end)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
toBuffer(start , end ) {
|
|
186
|
+
if (start === undefined) {
|
|
187
|
+
start = this.byteOffset
|
|
188
|
+
} else {
|
|
189
|
+
start += this.byteOffset
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (end === undefined) {
|
|
193
|
+
end = this.byteLength + this.byteOffset
|
|
194
|
+
} else {
|
|
195
|
+
end += this.byteOffset
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return start === 0 && end === this.buffer.byteLength
|
|
199
|
+
? this.buffer
|
|
200
|
+
: this.buffer.subarray(start, end)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
[Symbol.toStringTag]() {
|
|
204
|
+
return this.toString()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
[util.inspect.custom](
|
|
208
|
+
depth ,
|
|
209
|
+
options ,
|
|
210
|
+
inspect ,
|
|
211
|
+
) {
|
|
212
|
+
const bytes = []
|
|
213
|
+
for (let i = 0; i < this.byteLength; i++) {
|
|
214
|
+
bytes.push(this.buffer[this.byteOffset + i].toString(16).padStart(2, '0'))
|
|
215
|
+
}
|
|
216
|
+
return `Slice: "${this.toString()}" <${bytes.join(' ')}>`
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* A buddy allocator using XOR bitmap for efficient coalescing.
|
|
222
|
+
* Each bit tracks the XOR of a buddy pair's allocation state.
|
|
223
|
+
* When a bit is 0, both buddies have the same state (both free or both allocated).
|
|
224
|
+
* When a bit is 1, the buddies have different states.
|
|
225
|
+
*/
|
|
226
|
+
export class BuddyAllocator {
|
|
227
|
+
#size = 0
|
|
228
|
+
#padding = 0
|
|
229
|
+
#poolCount = 0
|
|
230
|
+
|
|
231
|
+
#poolBuffer
|
|
232
|
+
#poolTotal
|
|
233
|
+
#minOrder
|
|
234
|
+
#maxOrder
|
|
235
|
+
|
|
236
|
+
// Free lists indexed by order - simple arrays with push/pop
|
|
237
|
+
#freeLists = []
|
|
238
|
+
|
|
239
|
+
// XOR bitmap for coalescing - single contiguous buffer for cache locality
|
|
240
|
+
// Each order has a view into this buffer
|
|
241
|
+
// Bit = 0: both buddies same state, Bit = 1: different states
|
|
242
|
+
#bitmaps = []
|
|
243
|
+
#bitmapBuffer = null
|
|
244
|
+
|
|
245
|
+
constructor(poolTotal = 128 * 1024 * 1024) {
|
|
246
|
+
// Handle invalid pool sizes gracefully - fall back to no pool
|
|
247
|
+
if (!Number.isFinite(poolTotal) || poolTotal <= 0) {
|
|
248
|
+
this.#poolBuffer = Buffer.allocUnsafe(0)
|
|
249
|
+
this.#poolTotal = 0
|
|
250
|
+
this.#minOrder = 3
|
|
251
|
+
this.#maxOrder = 0
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Round up to nearest power of 2
|
|
256
|
+
this.#maxOrder = Math.ceil(Math.log2(poolTotal))
|
|
257
|
+
this.#poolTotal = 1 << this.#maxOrder
|
|
258
|
+
|
|
259
|
+
// Minimum block size is 8 bytes (order 3)
|
|
260
|
+
this.#minOrder = 3
|
|
261
|
+
|
|
262
|
+
this.#poolBuffer = Buffer.allocUnsafe(this.#poolTotal)
|
|
263
|
+
|
|
264
|
+
// Calculate total bitmap size and offsets for each order
|
|
265
|
+
// Total pairs = 2^(maxOrder-minOrder) - 1, total words ≈ that / 32
|
|
266
|
+
let totalWords = 0
|
|
267
|
+
const offsets = []
|
|
268
|
+
for (let order = 0; order <= this.#maxOrder; order++) {
|
|
269
|
+
offsets.push(totalWords)
|
|
270
|
+
const numPairs = 1 << Math.max(0, this.#maxOrder - order - 1)
|
|
271
|
+
totalWords += Math.ceil(numPairs / 32)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Allocate single contiguous buffer for all bitmaps
|
|
275
|
+
this.#bitmapBuffer = new ArrayBuffer(totalWords * 4)
|
|
276
|
+
|
|
277
|
+
// Create views into the buffer for each order
|
|
278
|
+
for (let order = 0; order <= this.#maxOrder; order++) {
|
|
279
|
+
this.#freeLists.push([])
|
|
280
|
+
|
|
281
|
+
const numPairs = 1 << Math.max(0, this.#maxOrder - order - 1)
|
|
282
|
+
const numWords = Math.ceil(numPairs / 32)
|
|
283
|
+
this.#bitmaps.push(new Uint32Array(this.#bitmapBuffer, offsets[order] * 4, numWords))
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Initially, the entire buffer is one free block at max order
|
|
287
|
+
this.#freeLists[this.#maxOrder].push(0)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
realloc(slice , byteLength ) {
|
|
291
|
+
if (!Number.isInteger(byteLength) || byteLength < 0) {
|
|
292
|
+
throw new TypeError(`Invalid byteLength: ${byteLength}`)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (slice == null) {
|
|
296
|
+
slice = new Slice()
|
|
297
|
+
} else if (!(slice instanceof Slice)) {
|
|
298
|
+
throw new TypeError('slice must be a Slice instance')
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (slice.byteLength === byteLength) {
|
|
302
|
+
return slice
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const dstOrder = byteLength === 0 ? 0 : this.#getOrder(byteLength)
|
|
306
|
+
|
|
307
|
+
if (this.isFromPool(slice)) {
|
|
308
|
+
// Derive order from maxByteLength
|
|
309
|
+
const srcOrder = 31 - Math.clz32(slice.maxByteLength)
|
|
310
|
+
|
|
311
|
+
this.#size -= slice.byteLength
|
|
312
|
+
this.#padding -= slice.maxByteLength - slice.byteLength
|
|
313
|
+
|
|
314
|
+
if (srcOrder === dstOrder) {
|
|
315
|
+
slice.byteLength = byteLength
|
|
316
|
+
this.#size += slice.byteLength
|
|
317
|
+
this.#padding += slice.maxByteLength - slice.byteLength
|
|
318
|
+
return slice
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Free the old block
|
|
322
|
+
this.#freeBlock(slice.byteOffset, srcOrder)
|
|
323
|
+
this.#poolCount--
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
slice.reset()
|
|
327
|
+
|
|
328
|
+
if (byteLength === 0) {
|
|
329
|
+
return slice
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const offset = this.#allocBlock(dstOrder)
|
|
333
|
+
if (offset == null) {
|
|
334
|
+
// Fall back to regular allocation
|
|
335
|
+
slice.buffer = Buffer.allocUnsafeSlow(byteLength)
|
|
336
|
+
slice.byteOffset = 0
|
|
337
|
+
slice.byteLength = byteLength
|
|
338
|
+
slice.maxByteLength = byteLength
|
|
339
|
+
} else {
|
|
340
|
+
slice.buffer = this.#poolBuffer
|
|
341
|
+
slice.byteOffset = offset
|
|
342
|
+
slice.byteLength = byteLength
|
|
343
|
+
slice.maxByteLength = 1 << dstOrder
|
|
344
|
+
|
|
345
|
+
this.#poolCount++
|
|
346
|
+
this.#size += byteLength
|
|
347
|
+
this.#padding += slice.maxByteLength - slice.byteLength
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return slice
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
get stats() {
|
|
354
|
+
// Calculate free space from all free lists
|
|
355
|
+
let freeSpace = 0
|
|
356
|
+
for (let order = this.#minOrder; order <= this.#maxOrder; order++) {
|
|
357
|
+
freeSpace += this.#freeLists[order].length << order
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const poolUsed = this.#poolTotal - freeSpace
|
|
361
|
+
|
|
362
|
+
return {
|
|
363
|
+
size: this.#size,
|
|
364
|
+
padding: this.#padding,
|
|
365
|
+
ratio: this.#size > 0 ? (this.#size + this.#padding) / this.#size : 1,
|
|
366
|
+
poolTotal: this.#poolTotal,
|
|
367
|
+
poolUsed,
|
|
368
|
+
poolSize: poolUsed,
|
|
369
|
+
poolCount: this.#poolCount,
|
|
370
|
+
poolList: this.#freeLists.map((list) => list.length),
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
isFromPool(slice ) {
|
|
375
|
+
return slice != null && slice.buffer === this.#poolBuffer
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
#toggleBit(offset , order ) {
|
|
379
|
+
if (order >= this.#maxOrder) {
|
|
380
|
+
return false
|
|
381
|
+
}
|
|
382
|
+
const pairIndex = offset >>> (order + 1)
|
|
383
|
+
const wordIndex = pairIndex >>> 5
|
|
384
|
+
const bitMask = 1 << (pairIndex & 31)
|
|
385
|
+
const bitmap = this.#bitmaps[order]
|
|
386
|
+
bitmap[wordIndex] ^= bitMask
|
|
387
|
+
return (bitmap[wordIndex] & bitMask) !== 0
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
#allocBlock(order ) {
|
|
391
|
+
// Try to allocate from pool
|
|
392
|
+
if (order > this.#maxOrder || this.#poolTotal === 0) {
|
|
393
|
+
return null
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Find the smallest order with a free block
|
|
397
|
+
let currentOrder = order
|
|
398
|
+
while (currentOrder <= this.#maxOrder && this.#freeLists[currentOrder].length === 0) {
|
|
399
|
+
currentOrder++
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (currentOrder > this.#maxOrder) {
|
|
403
|
+
return null
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Pop the free block
|
|
407
|
+
const offset = this.#freeLists[currentOrder].pop()
|
|
408
|
+
|
|
409
|
+
// Found block at exact order - just toggle and return
|
|
410
|
+
if (currentOrder === order) {
|
|
411
|
+
this.#toggleBit(offset, order)
|
|
412
|
+
return offset
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Split down to the requested order
|
|
416
|
+
while (currentOrder > order) {
|
|
417
|
+
currentOrder--
|
|
418
|
+
// Add buddy to free list
|
|
419
|
+
const buddyOffset = offset ^ (1 << currentOrder)
|
|
420
|
+
this.#freeLists[currentOrder].push(buddyOffset)
|
|
421
|
+
// Toggle bit - one buddy free, one allocated/kept
|
|
422
|
+
this.#toggleBit(offset, currentOrder)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return offset
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
#freeBlock(offset , order ) {
|
|
429
|
+
while (order < this.#maxOrder) {
|
|
430
|
+
// Toggle bit and check if we can coalesce
|
|
431
|
+
const isDifferent = this.#toggleBit(offset, order)
|
|
432
|
+
|
|
433
|
+
if (isDifferent) {
|
|
434
|
+
// Bit is now 1: buddy is allocated, can't coalesce
|
|
435
|
+
this.#freeLists[order].push(offset)
|
|
436
|
+
return
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Bit is now 0: both buddies are free, coalesce
|
|
440
|
+
// Remove buddy from free list
|
|
441
|
+
const buddyOffset = offset ^ (1 << order)
|
|
442
|
+
const idx = this.#freeLists[order].indexOf(buddyOffset)
|
|
443
|
+
if (idx !== -1) {
|
|
444
|
+
// Swap with last and pop for O(1) removal
|
|
445
|
+
const last = this.#freeLists[order].length - 1
|
|
446
|
+
this.#freeLists[order][idx] = this.#freeLists[order][last]
|
|
447
|
+
this.#freeLists[order].pop()
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Move up to parent level
|
|
451
|
+
offset = offset & ~(1 << order)
|
|
452
|
+
order++
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Reached max order
|
|
456
|
+
this.#freeLists[order].push(offset)
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
#getOrder(byteLength ) {
|
|
460
|
+
return byteLength <= 1 << this.#minOrder ? this.#minOrder : 32 - Math.clz32(byteLength - 1)
|
|
461
|
+
}
|
|
462
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nxtedition/slice",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib"
|
|
9
|
+
],
|
|
10
|
+
"license": "UNLICENSED",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "rimraf lib && tsc && amaroc ./src/index.ts && mv src/index.js lib/",
|
|
13
|
+
"prepublishOnly": "yarn build",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
15
|
+
"test": "node --test",
|
|
16
|
+
"test:ci": "node --test"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"amaroc": "^1.0.1",
|
|
20
|
+
"rimraf": "^6.1.2",
|
|
21
|
+
"typescript": "^5.9.3"
|
|
22
|
+
},
|
|
23
|
+
"gitHead": "2517ca631e010f17f7ba8be4e1b854ad18cbd746"
|
|
24
|
+
}
|