Setting the file. One moment.
Ledger · Clickhouse Js Node Rowbinary · ClickHouse/agent-skills · Skills Docs
ContentsBack to the top of the page src/examples/ledger.ts
src/examples/ ledger.ts
TypeScript · 98 lines · 4 KB
12 * comparison in `ledger.bench.ts`.
13 *
14 * Columns (the trigger — generate this reader when a result has these types):
15 * txn_id UInt128
16 * account Int64
17 * amount Decimal128(18)
18 * balance Decimal128(18)
19 * fee Decimal64(4)
20 * volume UInt256
21 *
22 * Every value here exceeds what a JS `number` (IEEE-754 double, 53-bit mantissa)
23 * can hold exactly, and ClickHouse emits them as **bare JSON numbers**. So
24 * `JSON.parse` silently rounds every field — the only correct JSON path is to
25 * quote the values server-side and re-parse each string into a `bigint` /
26 * decimal pair by hand. RowBinary reads each as an exact `bigint` straight off
27 * the wire. The whole row is fixed-width (16+8+16+16+8+32 = 96 bytes).
28 */
29 export type LedgerRow = {
30 txn_id : bigint ;
31 account : bigint ;
32 amount : DecimalValue ;
33 balance : DecimalValue ;
34 fee : DecimalValue ;
35 volume : bigint ;
36 };
37
38 /**
39 * API-combinator reader: correct and clear, one leaf reader per column. A fine
40 * default; `readLedgerRowFast` is the monomorphized form `ledger.bench.ts`
41 * measures. Both return identical values.
42 */
43 export const readLedgerRow : Reader < LedgerRow > = ( s ) => ({
44 txn_id: readUInt128 (s),
45 account: readInt64 (s),
46 amount: readDecimal128 ( 18 )(s),
47 balance: readDecimal128 ( 18 )(s),
48 fee: readDecimal64 ( 4 )(s),
49 volume: readUInt256 (s),
50 });
51
52 /**
53 * Optimized, monomorphized reader: the six column bounds checks coalesce into
54 * one `advance(s, 96)`; each wide value is composed from 64-bit words read at
55 * constant offsets off that base, with the high word read **signed** for the
56 * signed types (`Int64`, the `Decimal128` unscaled value, no high word needed
57 * for the unsigned `UInt128`/`UInt256`). Stays streaming-safe (one `advance`).
58 *
59 * txn_id UInt128 @ o+0 lo + (hi<<64) unsigned
60 * account Int64 @ o+16 getBigInt64
61 * amount Decimal128(18) @ o+24 lo + (hiSigned<<64) -> [v, 18]
62 * balance Decimal128(18) @ o+40 lo + (hiSigned<<64) -> [v, 18]
63 * fee Decimal64(4) @ o+56 getBigInt64 -> [v, 4]
64 * volume UInt256 @ o+64 w0 + w1<<64 + w2<<128 + w3<<192 unsigned
65 */
66 export const readLedgerRowFast : Reader < LedgerRow > = ( s ) => {
67 const { view } = s;
68 const o = advance (s, 96 ); // one bounds check for the whole 96-byte row
69
70 // UInt128 — unsigned, both words unsigned.
71 const txn_id =
72 view. getBigUint64 (o, true ) + (view. getBigUint64 (o + 8 , true ) << 64 n );
73
74 // Int64 — signed.
75 const account = view. getBigInt64 (o + 16 , true );
76
77 // Decimal128(18) — Int128 unscaled (low word unsigned, high word signed), scale 18.
78 const amount : DecimalValue = [
79 view. getBigUint64 (o + 24 , true ) + (view. getBigInt64 (o + 32 , true ) << 64 n ),
80 18 ,
81 ];
82 const balance : DecimalValue = [
83 view. getBigUint64 (o + 40 , true ) + (view. getBigInt64 (o + 48 , true ) << 64 n ),
84 18 ,
85 ];
86
87 // Decimal64(4) — Int64 unscaled, scale 4.
88 const fee : DecimalValue = [view. getBigInt64 (o + 56 , true ), 4 ];
89
90 // UInt256 — unsigned, four unsigned words.
91 const volume =
92 view. getBigUint64 (o + 64 , true ) +
93 (view. getBigUint64 (o + 72 , true ) << 64 n ) +
94 (view. getBigUint64 (o + 80 , true ) << 128 n ) +
95 (view. getBigUint64 (o + 88 , true ) << 192 n );
96
97 return { txn_id, account, amount, balance, fee, volume };
98 };