roaring-wasm-papandreou
Version:
WebAssembly port of Roaring Bitmaps for NodeJS
326 lines (305 loc) • 8.54 kB
JavaScript
'use strict'
const roaringWasm = require('./lib/roaring-wasm')
/**
* Array of bytes allocted directly in roaring library WASM memory.
* Note: Memory is not garbage collected, you are responsible to free the allocated memory calling "dispose" method.
*
* @class RoaringUint8Array
*/
class RoaringUint8Array {
/**
* Allocates an array in the roaring WASM heap.
*
* Note: Memory is not garbage collected, you are responsible to free the allocated memory calling "dispose" method.
*
* If the parameter is a number, it creates a new uninitialized array of the given length.
* If the parameter is an Iterable, it creates a copy of the given iterable.
*
* @constructor
* @param {(number | Iterable<number>)} lengthOrArray Length of the array to allocate or the array to copy
* @memberof RoaringUint8Array
*/
constructor(lengthOrArray, _pointer) {
this.byteOffset = 0
this.length = 0
let length
if (typeof lengthOrArray === 'number') {
length = lengthOrArray
} else if (lengthOrArray !== null && typeof lengthOrArray === 'object') {
length = lengthOrArray.length
if (typeof length !== 'number') {
const copy = new Uint8Array(lengthOrArray)
lengthOrArray = copy
length = copy.length
}
} else {
throw new TypeError('Invalid argument')
}
if (length > 0) {
if (_pointer === undefined) {
_pointer = roaringWasm._malloc(length)
}
if (!_pointer) {
throw new Error(`RoaringUint8Array failed to allocate ${length} bytes`)
}
this.byteOffset = _pointer
this.length = length
if (typeof lengthOrArray !== 'number') {
try {
this.set(lengthOrArray)
} catch (error) {
this.dispose()
throw error
}
}
}
}
/**
* The type of typed array used by this class.
* For RoaringUint8Array is Uint8Array.
*
* @readonly
* @property
* @type {typeof Uint8Array}
* @memberof RoaringUint8Array
*/
get TypedArray() {
return Uint8Array
}
/**
* The size in bytes of each element in the array.
* For RoaringUint8Array is always 1
*
* @readonly
* @property
* @type {number}
* @memberof RoaringUint8Array
*/
get BYTES_PER_ELEMENT() {
return 1
}
/**
* The ArrayBuffer instance referenced by the array.
* Note that the buffer may become invalid if the WASM allocated memory grows.
* When the WASM grows the preallocated memory this property will return the new allocated buffer.
* Use the returned buffer for short periods of time.
*
* @readonly
* @property
* @type {ArrayBuffer}
* @memberof RoaringUint8Array
*/
get buffer() {
return roaringWasm.wasmMemory.buffer
}
/**
* Returns true if this object was deallocated.
*
* @readonly
* @property
* @type {boolean}
* @memberof RoaringUint8Array
*/
get isDisposed() {
return !this.byteOffset
}
/**
* The length in bytes of the array.
* For RoaringUint8Array it is equal to this.length
*
* @readonly
* @property
* @type {number}
* @memberof RoaringUint8Array
*/
get byteLength() {
return this.length
}
/**
* The full WASM heap in hich this array is allocated.
* Note that the buffer may become invalid if the WASM allocated memory grows.
* When the WASM grows the preallocated memory this property will return the new allocated buffer.
* Use the returned array for short periods of time.
*
* @readonly
* @property
* @type {TypedArray}
* @memberof RoaringUint8Array
*/
get heap() {
return roaringWasm.HEAPU8
}
/**
* Frees the allocated memory.
* Is safe to call this method more than once.
*
* @returns {boolean} True if memory gets freed during this call, false if not.
* @memberof RoaringUint8Array
*/
dispose() {
const ptr = this.byteOffset
if (ptr) {
this.byteOffset = 0
this.length = 0
roaringWasm._free(ptr)
return true
}
return false
}
/**
* Throws an error if the memory was freed.
*
* @readonly
* @property
* @returns {(void | never)}
* @memberof RoaringUint8Array
*/
throwIfDisposed() {
if (this.isDisposed) {
throw new TypeError('RoaringUint8Array is disposed')
}
}
/**
* Writes the given array at the specified position
* @param array A typed or untyped array of values to set.
* @param offset The index in the current array at which the values are to be written.
* @memberof RoaringUint8Array
*/
set(array, offset = 0) {
if (!Number.isInteger(offset) || offset < 0) {
throw new TypeError(`Invalid offset ${offset}`)
}
if (array instanceof RoaringUint8Array) {
array = array.asTypedArray()
}
const length = array.length
if (typeof length !== 'number') {
return this.set(new Uint8Array(array))
}
if (offset + length > this.length) {
throw new TypeError(`Invalid offset ${offset}`)
}
this.heap.set(array, this.byteOffset + offset)
return this
}
/**
* Gets a new JS typed array instance that shares the memory used by this buffer.
* Note that the buffer may point to an outdated WASM memory if the WASM allocated memory grows while using the returned buffer.
* Use the returned array for short periods of time.
*
* @returns {Uint8Array} A new typed array that shares the memory with this array.
* @memberof RoaringUint8Array
*/
asTypedArray() {
return new Uint8Array(roaringWasm.wasmMemory.buffer, this.byteOffset, this.length)
}
/**
* Gets a new NodeJS Buffer instance that shares the memory used by this buffer.
* Note that the buffer may point to an outdated WASM memory if the WASM allocated memory grows while using the returned buffer.
* Use the returned array for short periods of time.
*
* @returns {Buffer} A new instance of NodeJS Buffer
* @memberof RoaringUint8Array
*/
asNodeBuffer() {
return Buffer.from(roaringWasm.wasmMemory.buffer, this.byteOffset, this.length)
}
/**
* Copies the content of this buffer to a typed array.
* The returned array is garbage collected and don't need to be disposed manually.
*
* @returns {TypedArray} A new typed array that contains a copy of this buffer
* @memberof RoaringUint8Array
*/
toTypedArray() {
const array = new Uint8Array(this.length)
array.set(this.asTypedArray())
return array
}
/**
* Copies the content of this buffer to a NodeJS Buffer.
* The returned buffer is garbage collected and don't need to be disposed manually.
*
* @returns {Buffer} A new instance of NodeJS Buffer that contains a copy of this buffer
* @memberof RoaringUint8Array
*/
toNodeBuffer() {
return Buffer.from(this.asNodeBuffer())
}
/**
* Copies the content of this typed array into a standard JS array of numbers and returns it.
*
* @returns {number[]} A new array.
* @memberof RoaringUint8Array
*/
toArray() {
return Array.from(this.asTypedArray())
}
/**
* Returns a string representation of an array.
* @memberof RoaringUint8Array
*/
toString() {
return this.asTypedArray().toString()
}
/**
* Iterator that iterates through all values in the array.
*
* @returns {IterableIterator<number>}
* @memberof RoaringUint8Array
*/
[Symbol.iterator]() {
return this.asTypedArray()[Symbol.iterator]()
}
}
/**
* The type of typed array used by this class.
* For RoaringUint8Array is Uint8Array.
*
* @static
* @readonly
* @property
* @type {typeof Uint8Array}
* @memberof RoaringUint8Array
*/
RoaringUint8Array.TypedArray = Uint8Array
/**
* The size in bytes of each element in the array.
* For RoaringUint8Array is always 1
*
* @static
* @readonly
* @property
* @type {number}
* @memberof RoaringUint8Array
*/
RoaringUint8Array.BYTES_PER_ELEMENT = 1
Object.defineProperties(RoaringUint8Array.prototype, {
TypedArray: {
value: Uint8Array,
writable: false,
configurable: false,
enumerable: false
},
BYTES_PER_ELEMENT: {
value: 1,
writable: false,
configurable: false,
enumerable: false
},
size: {
get: function getSize() {
return this.length
},
configurable: false,
enumerable: false
},
toJSON: {
value: function arrayToJSON() {
return this.asTypedArray()
},
configurable: true,
enumerable: false
}
})
module.exports = RoaringUint8Array