Skip to content

Commit 1444f05

Browse files
committed
Refactor typescript table iterators
1 parent d26f3a1 commit 1444f05

File tree

3 files changed

+62
-52
lines changed

3 files changed

+62
-52
lines changed

crates/bindings-typescript/src/lib/indexes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export interface ReadonlyRangedIndex<
108108
> {
109109
filter(
110110
range: IndexScanRangeBounds<TableDef, I>
111-
): IterableIterator<Prettify<RowType<TableDef>>>;
111+
): IteratorObject<Prettify<RowType<TableDef>>>;
112112
}
113113

114114
/**

crates/bindings-typescript/src/lib/table.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,8 @@ export interface ReadonlyTableMethods<TableDef extends UntypedTableDef> {
165165
count(): bigint;
166166

167167
/** Iterate over all rows in the TX state. Rust Iterator<Item=Row> → TS IterableIterator<Row>. */
168-
iter(): IterableIterator<Prettify<RowType<TableDef>>>;
169-
[Symbol.iterator](): IterableIterator<Prettify<RowType<TableDef>>>;
168+
iter(): IteratorObject<Prettify<RowType<TableDef>>>;
169+
[Symbol.iterator](): IteratorObject<Prettify<RowType<TableDef>>>;
170170
}
171171

172172
/**

crates/bindings-typescript/src/server/runtime.ts

Lines changed: 59 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ function makeTableView(
321321
const hasAutoIncrement = sequences.length > 0;
322322

323323
const iter = () =>
324-
new TableIterator(sys.datastore_table_scan_bsatn(table_id), rowType);
324+
tableIterator(sys.datastore_table_scan_bsatn(table_id), rowType);
325325

326326
const integrate_generated_columns = hasAutoIncrement
327327
? (row: RowType<any>, ret_buf: Uint8Array) => {
@@ -436,7 +436,7 @@ function makeTableView(
436436
find: (colVal: IndexVal<any, any>): RowType<any> | null => {
437437
if (numColumns === 1) colVal = [colVal];
438438
const args = serializeBound(colVal);
439-
const iter = new TableIterator(
439+
const iter = tableIterator(
440440
sys.datastore_index_scan_range_bsatn(index_id, ...args),
441441
rowType
442442
);
@@ -508,10 +508,10 @@ function makeTableView(
508508
return [prefix, prefix_elems, rstart, rend];
509509
};
510510
index = {
511-
filter: (range: any): IterableIterator<RowType<any>> => {
511+
filter: (range: any): IteratorObject<RowType<any>> => {
512512
if (numColumns === 1) range = [range];
513513
const args = serializeRange(range);
514-
return new TableIterator(
514+
return tableIterator(
515515
sys.datastore_index_scan_range_bsatn(index_id, ...args),
516516
rowType
517517
);
@@ -544,60 +544,70 @@ function hasOwn<K extends PropertyKey>(
544544
return Object.hasOwn(o, k);
545545
}
546546

547-
class TableIterator implements IterableIterator<any, undefined> {
548-
#id: u32 | -1;
549-
#reader: BinaryReader;
550-
#ty: AlgebraicType;
551-
constructor(id: u32, ty: AlgebraicType) {
552-
this.#id = id;
553-
this.#reader = new BinaryReader(new Uint8Array());
554-
this.#ty = ty;
555-
}
556-
[Symbol.iterator](): typeof this {
557-
return this;
558-
}
559-
next(): IteratorResult<any, undefined> {
560-
while (true) {
561-
if (this.#reader.remaining > 0) {
562-
const value = AlgebraicType.deserializeValue(
563-
this.#reader,
564-
this.#ty,
565-
MODULE_DEF.typespace
566-
);
567-
return { value };
568-
}
569-
if (this.#id === -1) {
570-
return { value: undefined, done: true };
571-
}
572-
this.#advance_iter();
547+
function* tableIterator(id: u32, ty: AlgebraicType): Generator<any, void> {
548+
using iter = new IteratorHandle(id);
549+
const { typespace } = MODULE_DEF;
550+
551+
let buf;
552+
while ((buf = advanceIter(iter)) != null) {
553+
const reader = new BinaryReader(buf);
554+
while (reader.remaining > 0) {
555+
yield AlgebraicType.deserializeValue(reader, ty, typespace);
573556
}
574557
}
558+
}
575559

576-
#advance_iter() {
577-
let buf_max_len = 0x10000;
578-
while (true) {
579-
try {
580-
const { 0: done, 1: buf } = sys.row_iter_bsatn_advance(
581-
this.#id,
582-
buf_max_len
583-
);
584-
if (done) this.#id = -1;
585-
this.#reader = new BinaryReader(buf);
586-
return;
587-
} catch (e) {
588-
if (e && typeof e === 'object' && hasOwn(e, '__buffer_too_small__')) {
589-
buf_max_len = e.__buffer_too_small__ as number;
590-
continue;
591-
}
592-
throw e;
560+
function advanceIter(iter: IteratorHandle): Uint8Array | null {
561+
let buf_max_len = 0x10000;
562+
while (true) {
563+
try {
564+
return iter.advance(buf_max_len);
565+
} catch (e) {
566+
if (e && typeof e === 'object' && hasOwn(e, '__buffer_too_small__')) {
567+
buf_max_len = e.__buffer_too_small__ as number;
568+
continue;
593569
}
570+
throw e;
594571
}
595572
}
573+
}
574+
575+
/** A class to manage the lifecycle of an iterator handle. */
576+
class IteratorHandle implements Disposable {
577+
#id: u32 | -1;
578+
579+
static #finalizationRegistry = new FinalizationRegistry<u32>(
580+
sys.row_iter_bsatn_close
581+
);
582+
583+
constructor(id: u32) {
584+
this.#id = id;
585+
IteratorHandle.#finalizationRegistry.register(this, id, this);
586+
}
587+
588+
/** Unregister this object with the finalization registry and return the id */
589+
#detach() {
590+
const id = this.#id;
591+
this.#id = -1;
592+
IteratorHandle.#finalizationRegistry.unregister(this);
593+
return id;
594+
}
595+
596+
/** Call `row_iter_bsatn_advance`, returning null if this iterator was already exhausted. */
597+
advance(buf_max_len: u32): Uint8Array | null {
598+
if (this.#id === -1) return null;
599+
const { 0: done, 1: buf } = sys.row_iter_bsatn_advance(
600+
this.#id,
601+
buf_max_len
602+
);
603+
if (done) this.#detach();
604+
return buf;
605+
}
596606

597607
[Symbol.dispose]() {
598608
if (this.#id >= 0) {
599-
this.#id = -1;
600-
sys.row_iter_bsatn_close(this.#id);
609+
const id = this.#detach();
610+
sys.row_iter_bsatn_close(id);
601611
}
602612
}
603613
}

0 commit comments

Comments
 (0)