@tai-kun/surrealdb
Version:
The SurrealDB SDK for JavaScript
169 lines (139 loc) • 4.49 kB
text/typescript
import type { Uint8ArrayLike } from "@tai-kun/surrealdb/types";
import type { AllocatedMemoryBlock } from "./_memory";
export interface WriterOptions {
readonly maxDepth?: number | undefined;
}
export class Writer {
readonly maxDepth: number;
constructor(
memory: AllocatedMemoryBlock,
options: WriterOptions | undefined = {},
) {
const {
maxDepth = 64,
} = options;
this.maxDepth = maxDepth;
this.data = memory.data;
this.view = memory.view;
this.depth = 0;
this.total = 0;
this.offset = 0;
this.chunks = [memory.data];
this.memory = memory;
}
data: Uint8Array;
view: DataView;
depth: number;
total: number;
offset: number;
protected chunks: [Uint8Array, ...Uint8Array[]];
protected readonly memory: AllocatedMemoryBlock;
protected datacopy(): Uint8Array {
return this.offset >= this.data.length
? this.data.slice()
: this.data.slice(0, this.offset);
}
protected dataref(): Uint8Array {
if (this.offset <= this.data.length) {
return this.data.subarray(0, this.offset); // trim
}
return this.data;
}
claim(size: number): void {
if (size > this.memory.size) {
// 事前に用意されたメモリーサイズを超える要求があれば:
// (1) 現チャンクに内容があれば必要なだけ複製を記録する。
// (2) 要求サイズちょうどのデータを作成する。
// (3) 総容量を更新する。
// (4) オフセットを 0 に戻す。
let last = this.chunks.length - 1;
// (1)
if (this.offset > 0) {
this.chunks[last++] = this.datacopy();
}
this.data = this.chunks[last] = new Uint8Array(size); // (2)
this.view = new DataView(this.data.buffer, 0, size); // (2)
this.total += this.offset; // (3)
this.offset = 0; // (4)
} else if (size > (this.data.length - this.offset)) {
// 要求サイズがチャンクに収まらない場合:
// (1) 現チャンクが事前に用意されたメモリーなら必要なだけ複製を記録する。
// (2) 現チャンクがカスタムサイズのメモリーなら必要なだけ参照を記録する。
// (3) 現チャンクを事前に用意されたメモリーにする。
// (4) 総容量を更新する。
// (5) オフセットを 0 に戻す。
const last = this.chunks.length - 1;
// (1)
if (this.data === this.memory.data) {
this.chunks[last] = this.datacopy();
} //
// (2)
else if (this.data.length < this.offset) {
this.chunks[last] = this.dataref();
}
this.data = this.chunks[last + 1] = this.memory.data; // (3)
this.view = this.memory.view; // (3)
this.total += this.offset; // (4)
this.offset = 0; // (5)
}
}
output(): Uint8Array {
if (this.chunks.length < 2) {
if (this.data === this.memory.data) {
return this.datacopy();
}
return this.data;
}
const last = this.chunks.length - 1;
this.chunks[last] = this.dataref(); // コピーされるので参照で構わない。
const acc = new Uint8Array(this.total + this.offset);
for (
let offset = 0, chunk: Uint8Array | undefined;
(chunk = this.chunks.shift());
) {
acc.set(chunk, offset);
offset += chunk.length;
}
return acc;
}
writeBytes(value: Uint8ArrayLike): void {
this.claim(value.length);
this.data.set(value, this.offset);
this.offset += value.length;
}
writeUint8(value: number): void {
this.claim(1);
this.view.setUint8(this.offset, value);
this.offset += 1;
}
writeUint16(value: number): void {
this.claim(2);
this.view.setUint16(this.offset, value);
this.offset += 2;
}
writeUint32(value: number): void {
this.claim(4);
this.view.setUint32(this.offset, value);
this.offset += 4;
}
writeBigUint64(value: bigint): void {
this.claim(8);
this.view.setBigUint64(this.offset, value);
this.offset += 8;
}
// writeFloat16(value: number): void {
// this.claim(2);
// setFloat16(this.view, this.offset, value);
// this.offset += 2;
// }
writeFloat32(value: number): void {
this.claim(4);
this.view.setFloat32(this.offset, value);
this.offset += 4;
}
writeFloat64(value: number): void {
this.claim(8);
this.view.setFloat64(this.offset, value);
this.offset += 8;
}
}