@btc-vision/transaction
Version:
OPNet transaction library allows you to create and sign transactions for the OPNet network.
271 lines • 8.95 kB
JavaScript
export class CustomMap {
static INITIAL_CAPACITY = 16;
static LOAD_FACTOR = 0.75;
#keys;
#values;
deleted;
capacity;
constructor() {
this.capacity = CustomMap.INITIAL_CAPACITY;
this.#keys = new Array(this.capacity);
this.#values = new Array(this.capacity);
this.deleted = new Array(this.capacity).fill(false);
}
_size = 0;
get size() {
return this._size;
}
set(key, value) {
let exist = true;
const index = this.findInsertIndex(key);
if (this.#keys[index] === undefined || this.deleted[index]) {
this._size++;
exist = false;
}
this.#keys[index] = key;
this.#values[index] = value;
this.deleted[index] = false;
if (this._size > this.capacity * CustomMap.LOAD_FACTOR) {
this.resize();
}
return exist;
}
get(key) {
const index = this.findIndex(key);
return index === -1 ? undefined : this.#values[index];
}
has(key) {
return this.findIndex(key) !== -1;
}
indexOf(key) {
return this.findIndex(key);
}
delete(key) {
const index = this.findIndex(key);
if (index === -1) {
return false;
}
this.#keys[index] = undefined;
this.#values[index] = undefined;
this.deleted[index] = true;
this._size--;
return true;
}
clear() {
this.#keys = new Array(this.capacity);
this.#values = new Array(this.capacity);
this.deleted = new Array(this.capacity).fill(false);
this._size = 0;
}
[Symbol.dispose]() {
this.clear();
}
*entries() {
for (let i = 0; i < this.capacity; i++) {
if (this.#keys[i] !== undefined && !this.deleted[i]) {
yield [this.#keys[i], this.#values[i]];
}
}
}
*keys() {
for (let i = 0; i < this.capacity; i++) {
if (this.#keys[i] !== undefined && !this.deleted[i]) {
yield this.#keys[i];
}
}
}
*values() {
for (let i = 0; i < this.capacity; i++) {
if (this.#keys[i] !== undefined && !this.deleted[i]) {
yield this.#values[i];
}
}
}
*[Symbol.iterator]() {
yield* this.entries();
}
hashBigInt(key) {
// For small bigints that fit in 32 bits, use direct conversion
if (key >= -2147483648n && key <= 2147483647n) {
return Number(key) | 0;
}
// For larger bigints, use bit manipulation
// Mix high and low 32-bit parts
let hash = 2166136261; // FNV-1a initial value
// Process the bigint in 32-bit chunks
let n = key < 0n ? -key : key;
while (n > 0n) {
// Extract 32-bit chunk
const chunk = Number(n & 0xffffffffn);
hash ^= chunk;
hash = Math.imul(hash, 16777619);
n = n >> 32n;
}
// Mix in the sign
if (key < 0n) {
hash ^= 0x80000000;
hash = Math.imul(hash, 16777619);
}
return Math.abs(hash);
}
hash(key) {
let hash = 0;
switch (typeof key) {
case 'number':
// Handle NaN and infinity specially
if (key !== key)
return 0x7ff8000000000000; // NaN
if (!isFinite(key))
return key > 0 ? 0x7ff0000000000000 : 0xfff0000000000000;
// Use the number itself as hash
hash = key | 0; // Convert to 32-bit integer
break;
case 'string':
// FNV-1a hash for strings
hash = 2166136261;
for (let i = 0; i < key.length; i++) {
hash ^= key.charCodeAt(i);
hash = Math.imul(hash, 16777619);
}
break;
case 'boolean':
hash = key ? 1231 : 1237;
break;
case 'symbol': {
// Symbols need special handling - use description
const desc = key.description || '';
hash = this.hash(desc); // Recursive call with string
break;
}
case 'bigint':
// Convert bigint to string for hashing
hash = this.hashBigInt(key);
break;
case 'undefined':
hash = 0;
break;
case 'object':
if (key === null) {
hash = 0;
}
else if (key instanceof Date) {
hash = key.getTime() | 0;
}
else if (ArrayBuffer.isView(key) || key instanceof ArrayBuffer) {
// Handle Buffer, TypedArrays, ArrayBuffer
hash = this.hashBuffer(key);
}
else if (Array.isArray(key)) {
// Hash arrays by combining element hashes
hash = 1;
for (const item of key) {
hash = Math.imul(hash, 31) + this.hash(item);
}
}
else {
throw new Error('Raw object not supported.');
// For objects, we need reference equality
// So we'll use linear probing with === comparison
// Start with a random-ish position
//hash = 0x42424242;
}
break;
case 'function':
// Hash function by its string representation
hash = this.hash(key.toString());
break;
}
// Ensure positive index
return Math.abs(hash) % this.capacity;
}
hashBuffer(buffer) {
let bytes;
if (buffer instanceof ArrayBuffer) {
bytes = new Uint8Array(buffer);
}
else if (ArrayBuffer.isView(buffer)) {
bytes = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
}
else {
return 0;
}
// FNV-1a hash for bytes
let hash = 2166136261;
for (let i = 0; i < Math.min(bytes.length, 100); i++) {
// Cap at 100 bytes for performance
hash ^= bytes[i];
hash = Math.imul(hash, 16777619);
}
return hash;
}
equals(a, b) {
// Handle special cases
if (a === b)
return true;
// NaN === NaN should be true for map #keys
if (typeof a === 'number' && typeof b === 'number' && a !== a && b !== b) {
return true;
}
// For buffers, do deep comparison
if ((ArrayBuffer.isView(a) || a instanceof ArrayBuffer) &&
(ArrayBuffer.isView(b) || b instanceof ArrayBuffer)) {
return this.buffersEqual(a, b);
}
return false;
}
buffersEqual(a, b) {
const bytesA = this.getBytes(a);
const bytesB = this.getBytes(b);
if (bytesA.length !== bytesB.length)
return false;
for (let i = 0; i < bytesA.length; i++) {
if (bytesA[i] !== bytesB[i])
return false;
}
return true;
}
getBytes(buffer) {
if (buffer instanceof ArrayBuffer) {
return new Uint8Array(buffer);
}
else if (ArrayBuffer.isView(buffer)) {
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
}
return new Uint8Array(0);
}
findIndex(key) {
let index = this.hash(key);
while (this.#keys[index] !== undefined || this.deleted[index]) {
if (this.#keys[index] !== undefined && this.equals(this.#keys[index], key)) {
return index;
}
index = (index + 1) % this.capacity;
}
return -1;
}
findInsertIndex(key) {
let index = this.hash(key);
while (this.#keys[index] !== undefined && !this.deleted[index]) {
if (this.equals(this.#keys[index], key)) {
return index; // Key already exists
}
index = (index + 1) % this.capacity;
}
return index;
}
resize() {
const oldKeys = this.#keys;
const oldValues = this.#values;
this.capacity *= 2;
this.#keys = new Array(this.capacity);
this.#values = new Array(this.capacity);
this.deleted = new Array(this.capacity).fill(false);
this._size = 0;
for (let i = 0; i < oldKeys.length; i++) {
if (oldKeys[i] !== undefined && !this.deleted[i]) {
this.set(oldKeys[i], oldValues[i]);
}
}
}
}
//# sourceMappingURL=CustomMap.js.map