UNPKG

roaring-wasm-papandreou

Version:

WebAssembly port of Roaring Bitmaps for NodeJS

771 lines (720 loc) 23.5 kB
'use strict' const roaringWasm = require('./lib/roaring-wasm') const RoaringUint32Array = require('./RoaringUint32Array') const RoaringUint8Array = require('./RoaringUint8Array') const { _roaring_bitmap_create_js, _roaring_bitmap_free, _roaring_bitmap_get_cardinality, _roaring_bitmap_is_empty, _roaring_bitmap_add, _roaring_bitmap_add_many, _roaring_bitmap_remove, _roaring_bitmap_maximum, _roaring_bitmap_minimum, _roaring_bitmap_contains, _roaring_bitmap_is_subset, _roaring_bitmap_is_strict_subset, _roaring_bitmap_to_uint32_array, _roaring_bitmap_equals, _roaring_bitmap_flip_inplace, _roaring_bitmap_optimize_js, _roaring_bitmap_select_js, _roaring_bitmap_and_cardinality, _roaring_bitmap_or_cardinality, _roaring_bitmap_andnot_cardinality, _roaring_bitmap_xor_cardinality, _roaring_bitmap_rank, _roaring_bitmap_and_inplace, _roaring_bitmap_or_inplace, _roaring_bitmap_xor_inplace, _roaring_bitmap_andnot_inplace, _roaring_bitmap_intersect, _roaring_bitmap_jaccard_index, _roaring_bitmap_add_checked_js, _roaring_bitmap_remove_checked_js, _roaring_bitmap_portable_size_in_bytes, _roaring_bitmap_portable_serialize, _roaring_bitmap_portable_deserialize, _roaring_bitmap_portable_deserialize_frozen, _roaring_bitmap_size_in_bytes, _roaring_bitmap_deserialize, _roaring_bitmap_serialize, _roaring_bitmap_deserialize_frozen_js } = roaringWasm /** * A Roaring Bitmap that supports 32 bit unsigned integers. * * The roaring bitmap allocates in WASM memory, remember to dispose * the RoaringBitmap32 when not needed anymore to release WASM memory. * * * @class RoaringBitmap32 */ class RoaringBitmap32 { /** * Creates a new roaring bitmap adding the specified values. * * The roaring bitmap allocates in WASM memory, remember to dispose * the RoaringBitmap32 when not needed anymore to release WASM memory. * @constructor * @param {(RoaringUint32Array | Iterable<number>)} values The values to add * @memberof RoaringBitmap32 */ constructor(values) { this._ptr = 0 if (values) { try { this.addMany(values) } catch (error) { this.dispose() throw error } } } /** * Creates a new roaring bitmap deserializing it from a buffer * * The roaring bitmap allocates in WASM memory, remember to dispose * the RoaringBitmap32 when not needed anymore to release WASM memory. * * @static * @param {(RoaringUint8Array | Uint8Array | Iterable<number>)} buffer The buffer to deserialize * @param {boolean} [portable=false] If true, deserialization is compatible with the Java and Go versions of the library. * If false, deserialization is compatible with the C version of the library. Default is false. * @returns {RoaringBitmap32} The reulting bitmap. Remember to dispose the instance when finished using it. * @memberof RoaringBitmap32 */ static deserialize(buffer, portable = false, frozen = false) { const bitmap = new RoaringBitmap32() try { bitmap.deserialize(buffer, portable, frozen) } catch (error) { bitmap.dispose() throw error } return bitmap } /** * Utility function that serializes an array of uint32 to a new NodeJS buffer. * The returned buffer is automatically garbage collected. * * @static * @param {(RoaringUint32Array | Iterable<number>)} values * @param {boolean} [portable=false] If true, serialization is compatible with the Java and Go versions of the library. * If false, serialization is compatible with the C version of the library. Default is false. * @returns {Buffer} The NodeJS buffer containing the serialized data. * @memberof RoaringBitmap32 */ static serializeArrayToNewBuffer(values, portable = false) { const bitmap = new RoaringBitmap32(values) try { bitmap.optimize() return bitmap.serializeToNodeBuffer(portable) } finally { bitmap.dispose() } } /** * Utility function that deserializes a RoaringBitmap32 serialized in a buffer to an Array<number> of values. * The array can be very big, be careful when you use this function. * * @static * @param {(RoaringUint8Array | Uint8Array | Iterable<number>)} buffer The buffer to deserialize. * @param {boolean} [portable=false] If true, deserialization is compatible with the Java and Go versions of the library. * If false, deserialization is compatible with the C version of the library. Default is false. * @returns {number[]} All the values in the bitmap. * @memberof RoaringBitmap32 */ static deserializeToArray(buffer, portable = false) { const bitmap = new RoaringBitmap32() try { bitmap.deserialize(buffer, portable) return bitmap.toArray() } finally { bitmap.dispose() } } /** * Utility function that deserializes a RoaringBitmap32 serialized in a buffer to a Set<number> of values. * The array can be very big, be careful when you use this function. * * @static * @param {(RoaringUint8Array | Uint8Array | Iterable<number>)} buffer The buffer to deserialize. * @param {boolean} [portable=false] If true, deserialization is compatible with the Java and Go versions of the library. * If false, deserialization is compatible with the C version of the library. Default is false. * @returns {number[]} All the values in the bitmap. * @memberof RoaringBitmap32 */ static deserializeToSet(buffer, portable = false) { const bitmap = new RoaringBitmap32() try { bitmap.deserialize(buffer, portable) return bitmap.toSet() } finally { bitmap.dispose() } } /** * Returns true if this instance was disposed. * * @readonly * @property * @type {boolean} * @memberof RoaringBitmap32 */ get isDisposed() { return !this._ptr } /** * Disposes this object freeing all WASM memory associated to it. * Is safe to call this method more than once. * * @returns {boolean} True if disposed during this call, false if not. * @memberof RoaringBitmap32 */ dispose() { const ptr = this._ptr if (ptr) { _roaring_bitmap_free(ptr) this._ptr = undefined if (this._frozenBuf) { roaringWasm._free(this._frozenBuf) this._frozenBuf = undefined } return true } return false } /** * Throws an exception if this object was disposed before. * * @returns {(void | never)} * @memberof RoaringBitmap32 */ throwIfDisposed() { if (typeof this._ptr !== 'number') { _throwDisposed() } } /** * Get the cardinality of the bitmap (number of elements). * * @returns {number} Number of elements in this bitmap. * @memberof RoaringBitmap32 */ cardinality() { const ptr = this._ptr return ptr ? _roaring_bitmap_get_cardinality(ptr) >>> 0 : 0 } /** * Returns true if the bitmap has no elements. * * @returns {boolean} True if the bitmap is empty. * @memberof RoaringBitmap32 */ isEmpty() { const ptr = this._ptr return !ptr || !!_roaring_bitmap_is_empty(ptr) } /** * Adds a 32 bit unsigned integer value. * Values are unique, this function does nothing if the value already exists. * * @param {number} value 32 bit unsigned integer to add in the set. * @memberof RoaringBitmap32 */ add(value) { _roaring_bitmap_add(_getPtr(this), value) } /** * Adds a 32 bit unsigned integer value checking if the bitmap changes. * Use add() if you don't need to know if something changed. * Values are unique, this function does nothing and returns false if the value already exists. * * @param {number} value 32 bit unsigned integer to add in the set. * @returns {boolean} True if the bitmap changed, false if not. * @memberof RoaringBitmap32 */ addChecked(value) { return !!_roaring_bitmap_add_checked_js(_getPtr(this), value) } /** * Adds multiple values. * Using this is faster than calling add() multiple times. * Inserting ordered or partially ordered arrays is faster. * * @param {(RoaringUint32Array | Iterable<number>)} values The values to add. * @memberof RoaringBitmap32 */ addMany(values) { if (values instanceof RoaringUint32Array) { if (values.length > 0) { _roaring_bitmap_add_many(_getPtr(this), values.length, values.byteOffset) } } else { const roaringArray = new RoaringUint32Array(values) try { if (roaringArray.length > 0) { _roaring_bitmap_add_many(_getPtr(this), roaringArray.length, roaringArray.byteOffset) } } finally { roaringArray.dispose() } } } /** * Removes a value from the set. * If the value does not exists, this function does nothing. * * @param {number} value The value to remove. * @memberof RoaringBitmap32 */ remove(value) { _roaring_bitmap_remove(_getPtr(this), value) } /** * Removes a value from the set checking if the bitmap changes. * Use remove() if you don't need to know if something changed. * If the value does not exists, this function does nothing and returns false. * * @param {number} value 32 bit unsigned integer to remove from the set. * @returns {boolean} True if the bitmap changed, false if not. * @memberof RoaringBitmap32 */ removeChecked(value) { return !!_roaring_bitmap_remove_checked_js(_getPtr(this), value) } /** * Gets the maximum value stored in the bitmap. * If the bitmap is empty, returns 0. * * @returns {number} The maximum 32 bit unsigned integer or 0 if empty. * @memberof RoaringBitmap32 */ maximum() { return _roaring_bitmap_maximum(_getPtr(this)) >>> 0 } /** * Gets the minimum value stored in the bitmap. * If the bitmap is empty, returns 0xFFFFFFFF * * @returns {number} The minimum 32 bit unsigned integer or 0xFFFFFFFF if empty. * @memberof RoaringBitmap32 */ minimum() { return _roaring_bitmap_minimum(_getPtr(this)) >>> 0 } /** * Checks whether the given value is contained in the set. * * @param {number} value The value to look for. * @returns {boolean} True if value exists in the set, false if not. * @memberof RoaringBitmap32 */ contains(value) { return !!_roaring_bitmap_contains(_getPtr(this), value) } /** * Returns true if the bitmap is subset of the other. * * @param {RoaringBitmap32} other the other bitmap * @returns {boolean} * @memberof RoaringBitmap32 */ isSubset(other) { return !!_roaring_bitmap_is_subset(_getPtr(this), _getPtr(other)) } /** * Returns true if this bitmap is strict subset of the other. * * @param {RoaringBitmap32} other The other bitmap * @returns {boolean} True if this bitmap is a strict subset of other * @memberof RoaringBitmap32 */ isStrictSubset(other) { return !!_roaring_bitmap_is_strict_subset(_getPtr(this), _getPtr(other)) } /** * Converts the bitmap to an array. * The array may be very big, use this function with caution. * The returned RoaringUint32Array is allocated in WASM memory and not garbage collected, * it need to be freed manually calling dispose(). * * @returns {RoaringUint32Array} The RoaringUint32Array. Remember to manually dispose to free the memory. * @memberof RoaringBitmap32 */ toRoaringUint32Array() { const ptr = _getPtr(this) const cardinality = _roaring_bitmap_get_cardinality(ptr) >>> 0 const result = new RoaringUint32Array(cardinality) if (cardinality > 0) { _roaring_bitmap_to_uint32_array(ptr, result.byteOffset) } return result } /** * Converts the bitmap to a JS array. * The resulting array may be very big, use this function with caution. * * @returns {number[]} The array containing all values in the bitmap. * @memberof RoaringBitmap32 */ toArray() { const roaringArray = this.toRoaringUint32Array() try { return roaringArray.toArray() } finally { roaringArray.dispose() } } /** * Converts the bitmap to a JS Set<number>. * The resulting set may be very big, use this function with caution. * * @returns {Set<number>} The set containing all values in the bitmap. * @memberof RoaringBitmap32 */ toSet() { const roaringArray = this.toRoaringUint32Array() try { return new Set(roaringArray.asTypedArray()) } finally { roaringArray.dispose() } } /** * Converts the bitmap to a JS Uint32Array. * The resulting array may be very big, use this function with caution. * * @returns {Uint32Array} The array containing all values in the bitmap. * @memberof RoaringBitmap32 */ toUint32Array() { const roaringArray = this.toRoaringUint32Array() try { return roaringArray.toTypedArray() } finally { roaringArray.dispose() } } /** * Checks wether two roaring bitmap contains the same data. * * @param {RoaringBitmap32} other * @returns {boolean} True if the bitmaps contains the same data, false if not. * @memberof RoaringBitmap32 */ equals(other) { if (this === other) { return true } if (!(other instanceof RoaringBitmap32)) { return false } const a = _getPtr(this) const b = _getPtr(other) if (a === b) { return true } return !!_roaring_bitmap_equals(a, b) } /** * Negates in place the bitmap within a specified interval. * Areas outside the range are passed through unchanged. * * @param {number} start Range start. * @param {number} end Range end. * @memberof RoaringBitmap32 */ flipRange(start, end) { start >>>= 0 end >>>= 0 if (end > start) { return } _roaring_bitmap_flip_inplace(_getPtr(this), start, end) } /** * Optimizes the bitmap releasing unused memory and compressing containers. * Returns true if something changed. * * @returns {boolean} True if something changed. * @memberof RoaringBitmap32 */ optimize() { return !!_roaring_bitmap_optimize_js(_getPtr(this)) } /** * If the size of the roaring bitmap is strictly greater than rank, then * this function returns the element of given rank. * Otherwise, it returns NaN. * * @param {number} rank Element rank * @returns {number} element or NaN * @memberof RoaringBitmap32 */ select(rank) { return _roaring_bitmap_select_js(_getPtr(this), rank) } /** * Computes the size of the intersection between two bitmaps. * Both bitmaps are unchanged. * * @param {RoaringBitmap32} other * @returns {number} Cardinality of the intersection between two bitmaps. * @memberof RoaringBitmap32 */ andCardinality(other) { return _roaring_bitmap_and_cardinality(_getPtr(this), _getPtr(other)) >>> 0 } /** * Computes the size of the union of two bitmaps. * Both bitmaps are unchanged. * * @param {RoaringBitmap32} other * @returns {number} Cardinality of the union of two bitmaps. * @memberof RoaringBitmap32 */ orCardinality(other) { return _roaring_bitmap_or_cardinality(_getPtr(this), _getPtr(other)) >>> 0 } /** * Computes the size of the difference (andnot) of two bitmaps. * Both bitmaps are unchanged. * * @param {RoaringBitmap32} other * @returns {number} Cardinality of the difference (andnot) of two bitmaps. * @memberof RoaringBitmap32 */ andNotCardinality(other) { return _roaring_bitmap_andnot_cardinality(_getPtr(this), _getPtr(other)) >>> 0 } /** * Computes the size of the symmetric difference (xor) between two bitmaps. * Both bitmaps are unchanged. * * @param {RoaringBitmap32} other * @returns {number} Cardinality of the symmetric difference (xor) of two bitmaps. */ xorCardinality(other) { return _roaring_bitmap_xor_cardinality(_getPtr(this), _getPtr(other)) >>> 0 } /** * Intersects this bitmap with another. * Removes the elements from this bitmap that don't exists in the other. * Stores the result in this bitmap. * The provided bitmap is not modified. * * @param {RoaringBitmap32} other * @memberof RoaringBitmap32 */ andInPlace(other) { _roaring_bitmap_and_inplace(_getPtr(this), _getPtr(other)) } /** * Adds the element of the other bitmap into this bitmap. * Stores the result in this bitmap. * The provided bitmap is not modified. * * @param {RoaringBitmap32} other * @memberof RoaringBitmap32 */ orInPlace(other) { _roaring_bitmap_or_inplace(_getPtr(this), _getPtr(other)) } /** * Computes the difference between two bitmaps. * Stores the result in this bitmap. * The provided bitmap is not modified. * * @param {RoaringBitmap32} other * @memberof RoaringBitmap32 */ xorInPlace(other) { _roaring_bitmap_xor_inplace(_getPtr(this), _getPtr(other)) } /** * Compute the difference between this and the provided bitmap, * writing the result in the current bitmap. * The provided bitmap is not modified. * * @param {RoaringBitmap32} other * @memberof RoaringBitmap32 */ andNotInPlace(other) { _roaring_bitmap_andnot_inplace(_getPtr(this), _getPtr(other)) } /** * Returns the number of integers that are smaller or equal to the given value. * * @param {number} value The value to rank * @returns {number} The number of values smaller than the given value * @memberof RoaringBitmap32 */ rank(value) { return _roaring_bitmap_rank(_getPtr(this), value) >>> 0 } /** * Check whether the two bitmaps intersect (have at least one element in common). * * @param {RoaringBitmap32} other The other bitmap. * @returns {boolean} True if the two bitmaps intersects, false if not. * @memberof RoaringBitmap32 */ intersects(other) { return !!_roaring_bitmap_intersect(_getPtr(this), _getPtr(other)) } /** * Computes the Jaccard index between two bitmaps. * (Also known as the Tanimoto distance, or the Jaccard similarity coefficient) * See https://en.wikipedia.org/wiki/Jaccard_index * * The Jaccard index is undefined if both bitmaps are empty. * * @returns {number} The Jaccard index * @memberof RoaringBitmap32 */ jaccardIndex(other) { return _roaring_bitmap_jaccard_index(_getPtr(this), _getPtr(other)) } /** * How many bytes are required to serialize this bitmap. * * @param {boolean} [portable=false] If true, deserialization is compatible with the Java and Go versions of the library. * If false, deserialization is compatible with the C version of the library. Default is false. * @memberof RoaringBitmap32 */ getSerializationSizeInBytes(portable = false) { const ptr = _getPtr(this) return portable ? _roaring_bitmap_portable_size_in_bytes(ptr) : _roaring_bitmap_size_in_bytes(ptr) >>> 0 } /** * Serializes a bitmap to a byte buffer allocated in WASM memory. * * The returned RoaringUint8Array is allocated in WASM memory and not garbage collected, * it need to be freed manually calling dispose(). * * @param {boolean} [portable=false] If true, serialization is compatible with the Java and Go versions of the library. * If false, serialization is compatible with the C version of the library. Default is false. * @returns {RoaringUint8Array} The RoaringUint8Array. Remember to manually dispose to free the memory. * @memberof RoaringBitmap32 */ serializeToRoaringUint8Array(portable = false) { const ptr = _getPtr(this) const size = this.getSerializationSizeInBytes(portable) const buf = roaringWasm._malloc(size) if (portable) { _roaring_bitmap_portable_serialize(ptr, buf) } else { _roaring_bitmap_serialize(ptr, buf) } return new RoaringUint8Array(size, buf) } /** * Serializes a bitmap to a typed Uint8Array. * The returned array is automatically garbage collected and there is no need to be disposed manually. * * @param {boolean} [portable=false] If true, serialization is compatible with the Java and Go versions of the library. * If false, serialization is compatible with the C version of the library. Default is false. * @returns {Uint8Array} The Uint8Array that contains the serialized bitmap * @memberof RoaringBitmap32 */ serializeToUint8Array(portable = false) { const roaringArray = this.serializeToRoaringUint8Array(portable) try { return roaringArray.toTypedArray() } finally { roaringArray.dispose() } } /** * Serializes a bitmap to a NodeJS buffer. * The returned buffer is automatically garbage collected and there is no need to be disposed manually. * * @param {boolean} [portable=false] If true, serialization is compatible with the Java and Go versions of the library. * If false, serialization is compatible with the C version of the library. Default is false. * @returns {Buffer} The NodeJS Buffer that contains the serialized bitmap * @memberof RoaringBitmap32 */ serializeToNodeBuffer(portable = false) { const roaringArray = this.serializeToRoaringUint8Array(portable) try { return roaringArray.toNodeBuffer() } finally { roaringArray.dispose() } } /** * Reads a bitmap from a serialized version. * Throws an error if deserialization failed. * * @param {(RoaringUint8Array | Uint8Array | Iterable<number>)} buffer * @param {boolean} [portable=false] If true, deserialization is compatible with the Java and Go versions of the library. * If false, deserialization is compatible with the C version of the library. Default is false. * @returns {void} * @memberof RoaringBitmap32 */ deserialize(buffer, portable = false, frozen = false) { if (!(buffer instanceof RoaringUint8Array)) { if (typeof buffer === 'number') { throw new TypeError('deserialize expects an array of bytes') } const roaringArray = new RoaringUint8Array(buffer) try { this.deserialize(roaringArray, portable, frozen) } finally { if (!frozen) { roaringArray.dispose() } } return } let ptr if (frozen) { if (portable) { ptr = _roaring_bitmap_portable_deserialize_frozen(buffer.byteOffset) } else { ptr = _roaring_bitmap_deserialize_frozen_js(buffer.byteOffset) } this._frozenBuf = buffer.byteOffset } else { if (portable) { ptr = _roaring_bitmap_portable_deserialize(buffer.byteOffset) } else { ptr = _roaring_bitmap_deserialize(buffer.byteOffset) } } if (ptr === null) { throw new Error(`RoaringBitmap32 deserialization failed`) } this._ptr = ptr } } function _throwGetPtrTypeError() { throw new TypeError('RoaringBitmap32 expected') } function _throwAllocationError() { throw new Error('Failed to allocate RoaringBitmap32') } function _throwDisposed() { throw new TypeError('RoaringBitmap32 was disposed') } function _getPtr(bitmap) { if (!(bitmap instanceof RoaringBitmap32)) { _throwGetPtrTypeError() } let ptr = bitmap._ptr if (ptr === null || ptr === undefined) { _throwDisposed() } if (ptr === 0) { ptr = _roaring_bitmap_create_js(0) >>> 0 if (!ptr) { _throwAllocationError() } bitmap._ptr = ptr } return ptr } module.exports = RoaringBitmap32