Setting the file. One moment.
Ip · Clickhouse Js Node Rowbinary · ClickHouse/agent-skills · Skills Docs
ContentsBack to the top of the page src/writers/ ip.ts
TypeScript · 121 lines · 4 KB
9
sink.view.
setUint32
(
reserve
(sink,
4
), value,
true
);
10 }
11
12 /**
13 * Write an `IPv6`: the raw 16 bytes (network order, as produced by `readIPv6`)
14 * copied verbatim. The inverse of `readIPv6`; pair with { @link parseIPv6 } to
15 * start from a string. Throws unless exactly 16 bytes.
16 */
17 export function writeIPv6 ( sink : Sink , value : Uint8Array ) : void {
18 if (value. length !== 16 ) {
19 throw new RangeError (
20 `RowBinary: IPv6 must be 16 bytes, got ${ value . length }` ,
21 );
22 }
23 const o = reserve (sink, 16 );
24 sink.buf. set (value, o);
25 }
26
27 /** Parse one dotted-quad field into a 0..255 octet, throwing on anything else. */
28 function parseOctet ( part : string ) : number {
29 const octet = Number (part);
30 if ( ! Number. isInteger (octet) || octet < 0 || octet > 255 ) {
31 throw new RangeError (
32 `RowBinary: invalid IPv4 octet ${ JSON . stringify ( part ) }` ,
33 );
34 }
35 return octet;
36 }
37
38 /**
39 * Parse a dotted-quad IPv4 string into the raw 32-bit value — the inverse of
40 * `formatIPv4`. `"1.2.3.4"` -> `0x01020304`. Pair with { @link writeIPv4 } .
41 *
42 * The four octets are read explicitly rather than in a loop (only ever four);
43 * `>>> 0` coerces the assembled value back to an unsigned 32-bit number.
44 */
45 export function parseIPv4 ( text : string ) : number {
46 const parts = text. split ( "." );
47 if (parts. length !== 4 ) {
48 throw new RangeError (
49 `RowBinary: invalid IPv4 string ${ JSON . stringify ( text ) }` ,
50 );
51 }
52 const a = parseOctet (parts[ 0 ] ! );
53 const b = parseOctet (parts[ 1 ] ! );
54 const c = parseOctet (parts[ 2 ] ! );
55 const d = parseOctet (parts[ 3 ] ! );
56 return ((a << 24 ) | (b << 16 ) | (c << 8 ) | d) >>> 0 ;
57 }
58
59 /**
60 * Parse an IPv6 string into its raw 16 bytes (network order) — the inverse of
61 * `formatIPv6`, accepting the canonical forms it emits (`::` zero-run compression
62 * and the `::ffff:a.b.c.d` IPv4-mapped form) as well as the fully expanded form.
63 * Pair with { @link writeIPv6 } .
64 *
65 * Rejects malformed input (throws): a parse-time helper validates because it
66 * must produce exactly 16 well-defined bytes and there is no hot loop or server
67 * to fall back on — see the "No defensive validation" exceptions in AGENTS.md.
68 */
69 export function parseIPv6 ( text : string ) : Buffer {
70 const halves = text. split ( "::" );
71 if (halves. length > 2 ) {
72 throw new RangeError (
73 `RowBinary: invalid IPv6 string ${ JSON . stringify ( text ) }` ,
74 );
75 }
76
77 // Expand a colon-separated side into 16-bit groups, splitting a trailing
78 // embedded IPv4 (a.b.c.d) into its two groups.
79 const toGroups = ( side : string ) : number [] => {
80 if (side === "" ) return [];
81 const groups : number [] = [];
82 for ( const part of side. split ( ":" )) {
83 if (part. includes ( "." )) {
84 const v4 = parseIPv4 (part);
85 groups. push ((v4 >>> 16 ) & 0xffff , v4 & 0xffff );
86 } else {
87 // 1–4 hex digits only. Parsing strictly rather than `parseInt(...) &
88 // 0xffff` rejects malformed groups instead of silently turning them
89 // into 0 (`NaN & 0xffff`) or wrapping negatives like "-1" to 0xffff.
90 if ( ! / ^ [0-9a-fA-F] {1,4}$ / . test (part)) {
91 throw new RangeError (
92 `RowBinary: invalid IPv6 group ${ JSON . stringify ( part ) }` ,
93 );
94 }
95 groups. push ( parseInt (part, 16 ));
96 }
97 }
98 return groups;
99 };
100
101 const head = toGroups (halves[ 0 ] ! );
102 const tail = halves. length === 2 ? toGroups (halves[ 1 ] ! ) : [];
103 const groups =
104 halves. length === 2
105 ? [ ... head, ...new Array ( 8 - head. length - tail. length ). fill ( 0 ), ... tail]
106 : head;
107 if (groups. length !== 8 ) {
108 throw new RangeError (
109 `RowBinary: invalid IPv6 string ${ JSON . stringify ( text ) }` ,
110 );
111 }
112 // SAFE: allocUnsafe — the loop below writes all 16 bytes (out[0..15] for the
113 // 8 groups), and `out` is only allocated here, past every throw, so an
114 // uninitialized buffer is never returned.
115 const out = Buffer. allocUnsafe ( 16 );
116 for ( let i = 0 ; i < 8 ; i ++ ) {
117 out[ 2 * i] = (groups[i] ! >>> 8 ) & 0xff ;
118 out[ 2 * i + 1 ] = groups[i] ! & 0xff ;
119 }
120 return out;
121 }