@btc-vision/btc-runtime
Version:
Bitcoin L1 Smart Contract Runtime for OP_NET. Build decentralized applications on Bitcoin using AssemblyScript and WebAssembly. Fully audited.
153 lines (128 loc) • 4.08 kB
text/typescript
import { Revert } from '../types/Revert';
export interface IMap<K, V> {
readonly size: i32;
has(key: K): bool;
set(key: K, value: V): this;
get(key: K): V;
delete(key: K): bool;
clear(): void;
keys(): K[];
values(): V[];
toString(): string;
}
export class Map<K, V> implements IMap<K, V> {
protected _keys: K[] = [];
protected _values: V[] = [];
// OPTIMIZATION: Cache the last found index.
// This makes has() -> get() sequences O(1) for the second call.
protected _lastIndex: i32 = -1;
public get size(): i32 {
return this._keys.length;
}
public keys(): K[] {
return this._keys;
}
public values(): V[] {
return this._values;
}
public set(key: K, value: V): this {
// Fast Cache Check
if (this._lastIndex != -1) {
if (unchecked(this._keys[this._lastIndex]) == key) {
unchecked((this._values[this._lastIndex] = value));
return this;
}
}
// Full Scan
const index = this.indexOf(key);
if (index == -1) {
this._keys.push(key);
this._values.push(value);
// Update cache to new item
this._lastIndex = this._keys.length - 1;
} else {
unchecked((this._values[index] = value));
// Update cache to found item (indexOf updates it too, but explicit here for safety)
this._lastIndex = index;
}
return this;
}
/**
* Optimized Linear Scan.
* Iterates backwards (LIFO assumption: recent items are hotter).
*/
public indexOf(key: K): i32 {
const len = this._keys.length;
// Optimization: Check cache first
if (this._lastIndex != -1 && this._lastIndex < len) {
if (unchecked(this._keys[this._lastIndex]) == key) {
return this._lastIndex;
}
}
// Reverse loop is often slightly faster for finding recent items
for (let i = len - 1; i >= 0; i--) {
if (unchecked(this._keys[i] == key)) {
this._lastIndex = i; // Update cache
return i;
}
}
return -1;
}
public get(key: K): V {
const index = this.indexOf(key);
if (index == -1) {
throw new Revert('Key not found in map (Map)');
}
return unchecked(this._values[index]);
}
public has(key: K): bool {
return this.indexOf(key) != -1;
}
/**
* Optimized Delete (Swap and Pop).
* O(1) complexity instead of O(N) splice.
*/
public delete(key: K): bool {
const index = this.indexOf(key);
if (index == -1) {
return false;
}
const lastIndex = this._keys.length - 1;
// If the element to delete is not the last one, swap it with the last one
if (index != lastIndex) {
unchecked((this._keys[index] = this._keys[lastIndex]));
unchecked((this._values[index] = this._values[lastIndex]));
// Fix cache if we moved the cached item
if (this._lastIndex == lastIndex) {
this._lastIndex = index;
} else if (this._lastIndex == index) {
this._lastIndex = -1;
}
} else {
// We are deleting the tail
if (this._lastIndex == lastIndex) this._lastIndex = -1;
}
this._keys.pop();
this._values.pop();
return true;
}
public clear(): void {
this._keys = [];
this._values = [];
this._lastIndex = -1;
}
public toString(): string {
// Warning: String concatenation in loops is O(N^2). Use sparingly.
let str = '';
const len = this._keys.length;
for (let i = 0; i < len; i++) {
str += `[${unchecked(this._keys[i])}] => [${unchecked(this._values[i])}]`;
}
return str;
}
}