cmpstr
Version:
CmpStr is a lightweight, fast and well performing package for calculating string similarity
195 lines (193 loc) • 6.72 kB
JavaScript
// CmpStr v3.0.1 dev-052fa0c-250614 by Paul Köhler @komed3 / MIT License
/**
* Pool Utility
* src/utils/Pool.ts
*
* @see https://en.wikipedia.org/wiki/Circular_buffer
*
* The Pool class provides a simple and efficient buffer pool for dynamic programming
* algorithms that require temporary arrays (such as Levenshtein, LCS, etc.).
* By reusing pre-allocated typed arrays, it reduces memory allocations and garbage
* collection overhead, especially for repeated or batch computations.
*
* It supports different types of buffers (Uint16Array, number[], Set, Map) and allows
* for acquiring buffers of specific sizes while managing a maximum pool size.
*
* @module Utils/Pool
* @author Paul Köhler (komed3)
* @license MIT
*/
/**
* RingPool is a circular buffer implementation that manages a pool of buffers.
*
* It allows for efficient acquisition and release of buffers, ensuring that
* buffers are reused without unnecessary allocations.
*
* @template T - The type of buffers managed by the pool
*/
class RingPool {
maxSize;
// The buffers in the pool
buffers = [];
// The current pointer for acquiring buffers
pointer = 0;
/**
* Creates a new RingPool with a specified maximum size.
*
* @param {number} maxSize - The maximum number of buffers that can be stored in the pool
*/
constructor(maxSize) {
this.maxSize = maxSize;
}
/**
* Acquires a buffer of at least the specified minimum size from the pool.
*
* @param {number} minSize - The minimum size of the buffer to acquire
* @param {boolean} allowOversize - Whether to allow buffers larger than minSize
* @return {PoolBuffer<T>|null} - The acquired buffer or null if no suitable buffer is found
*/
acquire(minSize, allowOversize) {
const len = this.buffers.length;
// Iterate through the buffers in the pool
for (let i = 0; i < len; i++) {
const idx = (this.pointer + i) % len;
const item = this.buffers[idx];
// Check if the item size is greater than or equal to the minimum size
if (item.size >= minSize) {
// Set the pointer to the next position
this.pointer = (idx + 1) % len;
// If the item size is equal to minSize or oversize is allowed, return the item
return allowOversize || item.size === minSize ? item : null;
}
}
// If no suitable buffer is found, return null
return null;
}
/**
* Releases a buffer back to the pool.
* If the pool is full, it replaces the oldest buffer with the new one.
*
* @param {PoolBuffer<T>} item - The buffer to release back to the pool
*/
release(item) {
if (this.buffers.length < this.maxSize) {
// If the pool is not full, simply add the item
this.buffers.push(item);
} else {
// If the pool is full, replace the oldest buffer
this.buffers[this.pointer] = item;
this.pointer = (this.pointer + 1) % this.maxSize;
}
}
/**
* Clears the pool, removing all buffers.
* This resets the pointer and empties the buffer list.
*/
clear() {
this.buffers = [];
this.pointer = 0;
}
}
/**
* The Pool class provides a buffer pool for dynamic programming algorithms.
*
* It allows for efficient reuse of buffers (Uint16Array, number[], Set, Map)
* to reduce memory allocations and garbage collection overhead.
*/
class Pool {
// Pool Types
static CONFIG = {
uint16: {
type: 'uint16',
maxSize: 32,
maxItemSize: 2048,
allowOversize: true
},
'number[]': {
type: 'number[]',
maxSize: 16,
maxItemSize: 1024,
allowOversize: false
},
set: { type: 'set', maxSize: 8, maxItemSize: 0, allowOversize: false },
map: { type: 'map', maxSize: 8, maxItemSize: 0, allowOversize: false }
};
// Pool Rings for each type
static POOLS = {
uint16: new RingPool(32),
'number[]': new RingPool(16),
set: new RingPool(8),
map: new RingPool(8)
};
/**
* Allocates a new buffer of the specified type and size.
*
* @param {PoolType} type - The type of buffer to allocate
* @param {number} size - The size of the buffer to allocate
* @return {any} - The newly allocated buffer
*/
static allocate(type, size) {
switch (type) {
case 'uint16':
return new Uint16Array(size);
case 'number[]':
return new Array(size).fill(0);
case 'set':
return new Set();
case 'map':
return new Map();
}
}
/**
* Acquires a buffer of the specified type and size from the pool.
* If no suitable buffer is available, it allocates a new one.
*
* @param {PoolType} type - The type of buffer to acquire (e.g., 'uint16', 'number[]', 'set', 'map')
* @param {number} size - The size of the buffer to acquire
* @return {T} - The acquired buffer of the specified type
*/
static acquire(type, size) {
// Get the configuration for the specified type
const CONFIG = this.CONFIG[type];
// If the requested size exceeds the maximum item size, allocate a new buffer
if (size > CONFIG.maxItemSize) return this.allocate(type, size);
// Try to acquire a buffer from the pool ring
// If a suitable buffer is found, return it (subarray for uint16)
const item = this.POOLS[type].acquire(size, CONFIG.allowOversize);
if (item) {
// If the type is 'uint16', return a subarray of the buffer
return type === 'uint16' ? item.buffer.subarray(0, size) : item.buffer;
}
// If no suitable buffer is found, allocate a new one
return this.allocate(type, size);
}
/**
* Acquires multiple buffers of the specified type and sizes from the pool.
*
* @param {PoolType} type - The type of buffers to acquire
* @param {number[]} sizes - An array of sizes for each buffer to acquire
* @return {T[]} - An array of acquired buffers of the specified type
*/
static acquireMany(type, sizes) {
return sizes.map((size) => this.acquire(type, size));
}
/**
* Releases a buffer back to the pool.
* If the size of the buffer is larger than the maximum item size, it will not be released.
*
* @param {PoolType} type - The type of buffer to release
* @param {T} buffer - The buffer to release
* @param {number} size - The size of the buffer
*/
static release(type, buffer, size) {
// Get the configuration for the specified type
const CONFIG = this.CONFIG[type];
// If the size of the buffer is less than or equal to the maximum item size, release it
if (size <= CONFIG.maxItemSize) {
// Release the buffer back to the pool ring
this.POOLS[type].release({ buffer, size });
}
}
}
export { Pool };
//# sourceMappingURL=Pool.js.map