Skip to content

Commit

Permalink
perf: createFastIterator.ts
Browse files Browse the repository at this point in the history
  • Loading branch information
tsctx committed Feb 12, 2024
1 parent 4a5108e commit 3eee31d
Showing 1 changed file with 123 additions and 0 deletions.
123 changes: 123 additions & 0 deletions createIterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,126 @@ export function createIterator(
return iterator;
};
}

export function createFastIterator<F extends (target: any) => any[]>(
name: string,
internalIteratorSymbol: symbol,
keyIndex: string | number = 0,
valueIndex: string | number = 1,
) {
type T = Parameters<F>[0];
type K = ReturnType<F>[number][0];
type V = ReturnType<F>[number][1];
class FastIterableIterator {
#target: any;
#kind: "key" | "value" | "key+value";
#index;

// https://webidl.spec.whatwg.org/#dfn-default-iterator-object
constructor(target: T, kind: "key" | "value" | "key+value") {
this.#target = target;
this.#kind = kind;
this.#index = 0;
}

next() {
// 1. Let interface be the interface for which the iterator prototype object exists.
// 2. Let thisValue be the this value.
// 3. Let object be ? ToObject(thisValue).
// 4. If object is a platform object, then perform a security
// check, passing:
// 5. If object is not a default iterator object for interface,
// then throw a TypeError.
// Object.getPrototypeOf(this) !== FastIterator.prototype
if (typeof this !== "object" || this === null || !(#target in this)) {
throw new TypeError(
`'next' called on an object that does not implement interface ${name} Iterator.`,
);
}

// 6. Let index be object’s index.
// 7. Let kind be object’s kind.
// 8. Let values be object’s target's value pairs to iterate over.
const index = this.#index;
const values = this.#target[internalIteratorSymbol];

// 9. Let len be the length of values.
const len = values.length;

// 10. If index is greater than or equal to len, then return
// CreateIterResultObject(undefined, true).
if (index >= len) {
return {
value: undefined,
done: true,
};
}

// 11. Let pair be the entry in values at index index.
const { [keyIndex!]: key, [valueIndex!]: value } = values[index];

// 12. Set object’s index to index + 1.
this.#index = index + 1;

// 13. Return the iterator result for pair and kind.

// https://webidl.spec.whatwg.org/#iterator-result

// 1. Let result be a value determined by the value of kind:
let result;
if (this.#kind === "key") {
// 1. Let idlKey be pair’s key.
// 2. Let key be the result of converting idlKey to an
// ECMAScript value.
// 3. result is key.
result = key;
} else if (this.#kind === "value") {
// 1. Let idlValue be pair’s value.
// 2. Let value be the result of converting idlValue to
// an ECMAScript value.
// 3. result is value.
result = value;
} else {
// 1. Let idlKey be pair’s key.
// 2. Let idlValue be pair’s value.
// 3. Let key be the result of converting idlKey to an
// ECMAScript value.
// 4. Let value be the result of converting idlValue to
// an ECMAScript value.
// 5. Let array be ! ArrayCreate(2).
// 6. Call ! CreateDataProperty(array, "0", key).
// 7. Call ! CreateDataProperty(array, "1", value).
// 8. result is array.
result = [key, value];
}
// 2. Return CreateIterResultObject(result, false).
return {
value: result,
done: false,
};
}
}
// https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
//@ts-ignore
delete FastIterableIterator.prototype.constructor;

Object.setPrototypeOf(
FastIterableIterator.prototype,
Object.getPrototypeOf(Array.prototype[Symbol.iterator]()),
);

Object.defineProperties(FastIterableIterator.prototype, {
[Symbol.toStringTag]: {
writable: false,
enumerable: false,
configurable: true,
value: `${name} Iterator`,
},
next: { writable: true, enumerable: true, configurable: true },
});

return <Kind extends "key" | "value" | "key+value">(target: T, kind: Kind) =>
new FastIterableIterator(target, kind) as unknown as IterableIterator<
Kind extends "key" ? K : Kind extends "value" ? V : [K, V]
>;
}

0 comments on commit 3eee31d

Please sign in to comment.