Setting the file. One moment.
Composite · Clickhouse Js Node Rowbinary · ClickHouse/agent-skills · Skills Docs
ContentsBack to the top of the page src/writers/ composite.ts
TypeScript · 140 lines · 5 KB
12 * inner writer, get a `Writer<T | null>`.
13 */
14 export function writeNullable < T >( writeValue : Writer < T >) : Writer < T | null > {
15 return ( sink , value ) => {
16 if (value === null ) {
17 writeUInt8 (sink, 1 );
18 } else {
19 writeUInt8 (sink, 0 );
20 writeValue (sink, value);
21 }
22 };
23 }
24
25 /**
26 * Write an `Array(T)`: a LEB128 element count, then each element back-to-back. The
27 * inverse of `readArray`; curried — pass the element writer, get a `Writer<T[]>`.
28 */
29 export function writeArray < T >( writeElement : Writer < T >) : Writer < readonly T []> {
30 return ( sink , values ) => {
31 writeUVarint (sink, values. length );
32 // C-style loop, not for-of: this is a hot path and we don't want the
33 // iterator protocol overhead on a plain array.
34 for ( let i = 0 ; i < values. length ; i ++ ) writeElement (sink, values[i] ! );
35 };
36 }
37
38 /**
39 * Write a `QBit(element_type, dimension)`: in RowBinary it is byte-for-byte an
40 * `Array(element_type)`, so this is just { @link writeArray } . The inverse of
41 * `readQBit`.
42 */
43 export function writeQBit < T >( writeElement : Writer < T >) : Writer < readonly T []> {
44 return writeArray (writeElement);
45 }
46
47 /**
48 * Write a `Tuple(...)` from a positional array: each element's value
49 * back-to-back, with NO count and NO delimiter. The inverse of `readTuple`;
50 * curried — pass one writer per element (in order), get a `Writer` of the tuple.
51 */
52 export function writeTuple < T extends readonly unknown []>( writers : {
53 [ K in keyof T ] : Writer < T [ K ]>;
54 }) : Writer < T > {
55 const fns = writers as ReadonlyArray < Writer < unknown >>;
56 return ( sink , value ) => {
57 for ( let i = 0 ; i < fns. length ; i ++ ) fns[i] ! (sink, value[i]);
58 };
59 }
60
61 /**
62 * Write a named `Tuple(name1 T1, ...)` from an object. The wire is identical to an
63 * unnamed tuple — values back-to-back, no count or delimiter — so the writers run
64 * in the `writers` object's key order, which MUST match the tuple's declared field
65 * order. The inverse of `readTupleNamed`; curried.
66 */
67 export function writeTupleNamed < T extends Record < string , unknown >>( writers : {
68 [ K in keyof T ] : Writer < T [ K ]>;
69 }) : Writer < T > {
70 const fns = writers as Record < string , Writer < unknown >>;
71 const keys = Object. keys (fns);
72 return ( sink , value ) => {
73 // C-style loop, not for-of: hot path, plain array of keys.
74 for ( let i = 0 ; i < keys. length ; i ++ ) {
75 const key = keys[i] ! ;
76 fns[key] ! (sink, value[key]);
77 }
78 };
79 }
80
81 /**
82 * Write a `Map(K, V)`: a LEB128 pair count, then key/value interleaved
83 * (k, v, k, v, ...). The inverse of `readMap`; curried — pass the key and value
84 * writers, get a `Writer<Map<K, V>>` (`Map` iteration order is preserved).
85 */
86 export function writeMap < K , V >(
87 writeKey : Writer < K >,
88 writeValue : Writer < V >,
89 ) : Writer < ReadonlyMap < K , V >> {
90 return ( sink , map ) => {
91 writeUVarint (sink, map.size);
92 // for-of is intentional here: it is the fastest way to iterate a `Map`
93 // (unlike plain arrays, where a C-style index loop wins).
94 for ( const [ key , value ] of map) {
95 writeKey (sink, key);
96 writeValue (sink, value);
97 }
98 };
99 }
100
101 /**
102 * A tagged `Variant` value for { @link writeVariant } : the active alternative's
103 * `discriminant` (its index in the sorted-type-name order) paired with its value,
104 * or `null` for a NULL.
105 *
106 * WHY TAGGED: `readVariant` returns only the decoded VALUE — the discriminant is
107 * consumed from the wire and not surfaced — so encode cannot recover which
108 * alternative a bare value belongs to (e.g. is `5` the `UInt8` or the `Int32`
109 * alternative?). The discriminant must therefore be supplied explicitly, the
110 * encode-side analog of the `readGeometry` switch.
111 */
112 export type VariantValue =
113 | readonly [ discriminant : number , value : unknown ]
114 | null ;
115
116 /**
117 * Write a `Variant(T1, ..., Tn)`: a 1-byte discriminant then the chosen
118 * alternative's value (discriminant `0xFF` = NULL, no value). The inverse of
119 * `readVariant`; curried — pass the alternative writers in sorted-type-name order
120 * (same order the reader expects), get a `Writer<VariantValue>`.
121 */
122 export function writeVariant (
123 writers : ReadonlyArray < Writer < never >>,
124 ) : Writer < VariantValue > {
125 return ( sink , value ) => {
126 if (value === null ) {
127 writeUInt8 (sink, 0xff );
128 return ;
129 }
130 const [ discriminant , inner ] = value;
131 const fn = writers[discriminant] as Writer < unknown > | undefined ;
132 if (fn === undefined ) {
133 throw new RangeError (
134 `RowBinary Variant: discriminant ${ discriminant } out of range (${ writers . length } alternatives)` ,
135 );
136 }
137 writeUInt8 (sink, discriminant);
138 fn (sink, inner);
139 };
140 }