Setting the file. One moment.
Core · Clickhouse Js Node Rowbinary · ClickHouse/agent-skills · Skills Docs
ContentsBack to the top of the page src/writers/core.ts
src/writers/ core.ts
TypeScript · 92 lines · 4 KB
13 */
14 export const BufferFull = Symbol ( "RowBinary.BufferFull" );
15
16 /**
17 * The write-side mirror of the reader's `Cursor`: the cursor every writer threads
18 * through. A `Buffer` to write into, the current write position, and a
19 * `DataView` over the same bytes. The encode counterpart of decode's `Cursor`.
20 *
21 * Deliberately STATE only — no write methods. Encoding lives in the free
22 * `writeX(sink, value)` functions in the sibling modules, so a generated encoder
23 * pulls in only the per-type writers a result needs (exactly like the reader
24 * side). `view`/`buf` are public so those free functions can reach them.
25 *
26 * Like a `Cursor`, a `Sink` wraps a FIXED-length buffer (supplied by the caller):
27 * it never reallocates. { @link reserve } throws { @link BufferFull } when the next
28 * write would overflow, the encode mirror of the reader's `advance` throwing
29 * `NeedMoreData` on underflow. Size the buffer to a chunk you intend to flush, and
30 * pull the written bytes with { @link Sink.bytes } .
31 */
32 export class Sink {
33 pos = 0 ;
34
35 /**
36 * The buffer being written into. Only `buf.subarray(0, pos)` (see
37 * { @link Sink.bytes } ) holds written bytes; the tail is unwritten headroom.
38 * Built with the buffer's own `byteOffset`/`byteLength` view in
39 * { @link Sink.view } , exactly like the reader's `Cursor`.
40 */
41 readonly buf : Buffer ;
42
43 /**
44 * `DataView` over { @link Sink.buf } , for fixed-width integer/float writes. Built
45 * with the buffer's own `byteOffset`/`byteLength`: a `Buffer` is often a window
46 * into a larger pooled `ArrayBuffer`, so `new DataView(buf.buffer)` alone would
47 * point at the wrong bytes.
48 */
49 readonly view : DataView ;
50
51 constructor ( buf : Buffer ) {
52 this .buf = buf;
53 this .view = new DataView (buf.buffer, buf.byteOffset, buf.byteLength);
54 }
55
56 /**
57 * The written bytes — `buf.subarray(0, pos)`. A zero-copy VIEW into the sink's
58 * buffer, so use `Buffer.from(sink.bytes())` if you need an independent copy.
59 */
60 bytes () : Buffer {
61 return this .buf. subarray ( 0 , this .pos);
62 }
63 }
64
65 /**
66 * A `Writer<T>` encodes one value of type `T` into the sink, advancing it — the
67 * mirror of the reader's `Reader<T>`. Leaf writers (e.g. `writeUInt32`) are
68 * `Writer`s directly; combinators (e.g. `writeArray`) take sub-`Writer`s and
69 * return a `Writer`, so types compose with no per-element closures.
70 */
71 export type Writer < T > = ( sink : Sink , value : T ) => void ;
72
73 /**
74 * Reserve `n` bytes for the next write: bounds-check them, advance the position
75 * past them, and return the offset the write starts at (the value BEFORE
76 * advancing). The write-side mirror of the reader's `advance`: every fixed-width
77 * write goes through it, so the capacity check and position bookkeeping live in
78 * one place:
79 *
80 * function writeInt32(s, v) { s.view.setInt32(reserve(s, 4), v, true); }
81 *
82 * Throws { @link BufferFull } when fewer than `n` bytes remain, WITHOUT moving
83 * the position — the buffer is fixed-length, exactly as the reader's input is, so
84 * a driver flushes what is written and retries the row into a fresh buffer.
85 */
86 export function reserve ( sink : Sink , n : number ) : number {
87 const start = sink.pos;
88 const next = start + n;
89 if (next > sink.buf. length ) throw BufferFull;
90 sink.pos = next;
91 return start;
92 }