UNPKG

ipfs-bitswap

Version:

JavaScript implementation of the Bitswap data exchange protocol used by IPFS

147 lines 4.46 kB
/** * SortedMap is a Map whose iterator order can be defined by the user */ export class SortedMap extends Map { _cmp; _keys; constructor(entries, cmp) { super(); this._cmp = cmp ?? this._defaultSort; this._keys = []; for (const [k, v] of entries ?? []) { this.set(k, v); } } /** * Call update to update the position of the key when it should change. * For example if the compare function sorts by the priority field, and the * priority changes, call update. * Call indexOf() to get the index _before_ the change happens. */ update(i) { if (i < 0 || i >= this._keys.length) { return; } const k = this._keys[i]; this._keys.splice(i, 1); const newIdx = this._find(k); this._keys.splice(newIdx, 0, k); } set(k, v) { // If the key is already in the map, remove it from the ordering and // re-insert it below if (this.has(k)) { const i = this.indexOf(k); this._keys.splice(i, 1); } // Update / insert the k/v into the map super.set(k, v); // Find the correct position of the newly inserted k/v in the order const i = this._find(k); this._keys.splice(i, 0, k); return this; } clear() { super.clear(); this._keys = []; } delete(k) { if (!this.has(k)) { return false; } const i = this.indexOf(k); this._keys.splice(i, 1); return super.delete(k); } indexOf(k) { if (!this.has(k)) { return -1; } const i = this._find(k); if (this._keys[i] === k) { return i; } // There may be more than one key with the same ordering // eg { k1: <priority 5>, k2: <priority 5> } // so scan outwards until the key matches for (let j = 1; j < this._keys.length; j++) { if (this._keys[i + j] === k) return i + j; if (this._keys[i - j] === k) return i - j; } return -1; // should never happen for existing key } _find(k) { let lower = 0; let upper = this._keys.length; while (lower < upper) { const pivot = (lower + upper) >>> 1; // lower + (upper - lower) / 2 const cmp = this._kCmp(this._keys[pivot], k); // console.log(` _find ${lower}:${upper}[${pivot}] ${cmp}`) if (cmp < 0) { // pivot < k lower = pivot + 1; } else if (cmp > 0) { // pivot > k upper = pivot; } else { // pivot == k return pivot; } } return lower; } *keys() { for (const k of this._keys) { yield k; } return undefined; } *values() { for (const k of this._keys) { // @ts-expect-error - return of `this.get(k)` is `Value|undefined` which is // incompatible with `Value`. Typechecker can't that this contains values // for all the `_keys`. ts(2322) yield this.get(k); } return undefined; } *entries() { for (const k of this._keys) { // @ts-expect-error - return of `this.get(k)` is `Value|undefined` which is // incompatible with `Value`. Typechecker can't that this contains values // for all the `_keys`. ts(2322) yield [k, this.get(k)]; } return undefined; } *[Symbol.iterator]() { yield* this.entries(); } // @ts-expect-error - Callback in Map forEach is (V, K, Map<K, V>) => void forEach(cb, thisArg = this) { if (cb == null) { return; } for (const k of this._keys) { const val = this.get(k); if (val == null) { throw new Error('Value cannot be undefined'); } cb.apply(thisArg, [[k, val]]); } } _defaultSort(a, b) { if (a[0] < b[0]) return -1; if (b[0] < a[0]) return 1; return 0; } _kCmp(a, b) { return this._cmp( // @ts-expect-error - get may return undefined [a, this.get(a)], [b, this.get(b)]); } } //# sourceMappingURL=sorted-map.js.map