nx_parquet_lib.nx source
↩ module page · 1352 lines · 55423 B
1// nx_parquet_lib.nx -- THE SOVEREIGN PARQUET READER (search R0d be_bright: the BRIGHT benchmark ships as parquet).
2//
3// WHY. BRIGHT (huggingface xlangai/BRIGHT, cc-by-4.0, search.refs brightds26) ships twelve documents files and twelve
4// examples files as Apache Parquet, and on 2026-09-14 the estate held no parquet, thrift-compact or snappy reader
5// (nx_catalog ABSENT on every name tried; a corpus-complete grep over 25,141 sources found the word only in prose).
6// A dataset the estate cannot open is a bar the estate cannot measure, so this lib is the first byte of R0d.
7// It reads what those files carry -- BYTE_ARRAY (string) columns, flat or nested as lists, PLAIN or dictionary
8// encoded, uncompressed or snappy, data pages V1 or V2 -- and refuses everything else BY NAME (PQ_E_UNSUPPORTED with
9// the offending enum value left in pq[PQ_WHAT]), never a silent zero or a quietly truncated column.
10//
11// THE FORMAT, from the mirrored primary sources read 2026-09-14 (search.refs pqthrift26 pqenc26 thriftcompact26
12// snappyfmt26):
13// file = PAR1 .. pages .. FileMetaData (thrift compact) .. i32 LE footer length .. PAR1
14// thrift compact: field header byte = (id delta << 4) | type, delta 0 => a zigzag i16 follows; ints are zigzag
15// varints; binary = varint length + bytes; list header = (size << 4) | element type, size 15 => a varint
16// size follows; a bool list element is one byte (1 or 2); a bool struct field carries its value in the
17// type nibble; STOP = 0.
18// schema = depth-first list, root first. A leaf's max definition level = its non-REQUIRED ancestors plus itself,
19// its max repetition level = its REPEATED ancestors plus itself (the Dremel shredding rules): a value is
20// present when its definition level equals the max, and a new row starts at repetition level 0.
21// page = PageHeader (thrift) then compressed_page_size bytes. DATA_PAGE (V1) payload = [repetition levels]
22// [definition levels] [values], each level block an i32 LE byte length then hybrid runs; DATA_PAGE_V2 =
23// levels first, never compressed, lengths in the header, then the values (compressed iff is_compressed);
24// DICTIONARY_PAGE = PLAIN values. Dictionary-encoded data = one byte of bit width then hybrid runs of indices.
25// hybrid = varint header; even => an RLE run of header >> 1 copies of one value (ceil(bw / 8) bytes LE);
26// odd => header >> 1 groups of eight bit-packed values, LSB first.
27// snappy = varint uncompressed length, then tagged elements: 00 literal (length in the tag up to 60, else 1..4
28// LE bytes follow), 01 copy with a 1-byte offset (length 4..11, 11-bit offset), 10 copy with a 2-byte
29// offset, 11 copy with a 4-byte offset (both length 1..64).
30// EVERY READ IS BOUNDED by the buffer it reads and every write by an arena sized from the column chunk's own
31// total_uncompressed_size (the format's bound on decoded page bytes): a decompressor is a parser pointed at
32// foreign bytes (nx_inflate's law), so a truncated stream, a back-reference before the buffer, a level count that
33// disagrees with the page header and a footer longer than the file each REFUSE with a distinct negative code.
34// COMPOSES sys_read_file (buffer sized from the file, no cap), sys_mmap, sys_munmap, sys_write. Allocation happens
35// once per open and once per column chunk read, never per page or per value. No hw writes (Rule 26).
36// license_tier: ORIGINAL
37import "nx_syscalls.nx"
38
39const PQ_I64: i64 = 8
40const PQ_BYTE: i64 = 255
41const PQ_BITS: i64 = 8
42const PQ_I32_LEN: i64 = 4
43const PQ_MAGIC_LEN: i64 = 4
44const PQ_TAIL_LEN: i64 = 8 // i32 footer length + PAR1
45const PQ_MAGIC_P: i64 = 80
46const PQ_MAGIC_A: i64 = 65
47const PQ_MAGIC_R: i64 = 82
48const PQ_MAGIC_1: i64 = 49
49const PQ_NONE: i64 = 0 - 1
50const PQ_NL: i64 = 10
51const PQ_CR: i64 = 13
52const PQ_TAB: i64 = 9
53const PQ_SPACE: i64 = 32
54const PQ_PIPE: i64 = 124
55const PQ_MINUS: i64 = 45
56const PQ_EQ: i64 = 61
57const PQ_DIGIT0: i64 = 48
58const PQ_DECIMAL: i64 = 10
59const PQ_ARENA_SLACK: i64 = 64 // headroom past the format's own bound, never a size to tune
60const PQ_MAX_BITWIDTH: i64 = 32 // a parquet level or dictionary index never exceeds 32 bits
61const PQ_HYBRID_GROUP: i64 = 8 // bit-packed values come in groups of eight
62const PQ_I64_MAX: i64 = 9223372036854775807
63
64// ---- refusals: distinct and negative, so a caller can tell WHICH wall fired
65const PQ_E_MAGIC: i64 = 0 - 1 // not a parquet file
66const PQ_E_TRUNC: i64 = 0 - 2 // a length points past the bytes
67const PQ_E_THRIFT: i64 = 0 - 3 // the footer or a page header does not decode
68const PQ_E_UNSUPPORTED: i64 = 0 - 4 // a type, encoding, codec or page kind not yet measured (pq[PQ_WHAT])
69const PQ_E_SNAPPY: i64 = 0 - 5 // the snappy stream is corrupt or disagrees with the header
70const PQ_E_CAPACITY: i64 = 0 - 6 // output would exceed the arena or the entry table
71const PQ_E_ORDER: i64 = 0 - 7 // row groups met before the schema in the footer
72const PQ_E_LEVELS: i64 = 0 - 8 // level, value or dictionary counts disagree with the page header
73const PQ_E_COLUMNS: i64 = 0 - 9 // a row group's column count differs from the schema's leaf count
74const PQ_E_ARG: i64 = 0 - 10 // a row group or leaf index out of range
75const PQ_E_OPEN: i64 = 0 - 11 // the file cannot be read
76
77// ---- thrift compact wire
78const TC_STOP: i64 = 0
79const TC_BTRUE: i64 = 1
80const TC_BFALSE: i64 = 2
81const TC_I8: i64 = 3
82const TC_I16: i64 = 4
83const TC_I32: i64 = 5
84const TC_I64: i64 = 6
85const TC_DOUBLE: i64 = 7
86const TC_BINARY: i64 = 8
87const TC_LIST: i64 = 9
88const TC_SET: i64 = 10
89const TC_MAP: i64 = 11
90const TC_STRUCT: i64 = 12
91const TC_UUID: i64 = 13
92const TC_DOUBLE_BYTES: i64 = 8
93const TC_UUID_BYTES: i64 = 16
94const TC_NIBBLE: i64 = 15
95const TC_LONG_FORM: i64 = 15
96const TC_TYPE_SHIFT: i64 = 4
97const TC_VARINT_MAX: i64 = 10
98const TC_VARINT_DATA: i64 = 127
99const TC_VARINT_MORE: i64 = 128
100const TC_VARINT_SHIFT: i64 = 7
101// cursor slots
102const TC_BUF: i64 = 0
103const TC_POS: i64 = 1
104const TC_END: i64 = 2
105const TC_ERR: i64 = 3
106const TC_FID: i64 = 4
107const TC_LAST: i64 = 5
108const TC_ET: i64 = 6
109const TC_SLOTS: i64 = 7
110// skip stack frames
111const TS_KIND: i64 = 0
112const TS_REM: i64 = 1
113const TS_ET: i64 = 2
114const TS_VT: i64 = 3
115const TS_LAST: i64 = 4
116const TS_SLOTS: i64 = 5
117const TS_DEPTH: i64 = 64 // parquet metadata nests under ten deep; beyond this is a refusal, not a guess
118const TK_STRUCT: i64 = 0
119const TK_LIST: i64 = 1
120const TK_MAP: i64 = 2
121
122// ---- parquet enums (parquet.thrift, mirrored)
123const PQ_T_BOOLEAN: i64 = 0
124const PQ_T_INT32: i64 = 1
125const PQ_T_INT64: i64 = 2
126const PQ_T_INT96: i64 = 3
127const PQ_T_FLOAT: i64 = 4
128const PQ_T_DOUBLE: i64 = 5
129const PQ_T_BYTE_ARRAY: i64 = 6
130const PQ_T_FIXED: i64 = 7
131const PQ_ENC_PLAIN: i64 = 0
132const PQ_ENC_PLAIN_DICT: i64 = 2
133const PQ_ENC_RLE: i64 = 3
134const PQ_ENC_BITPACKED: i64 = 4
135const PQ_ENC_RLE_DICT: i64 = 8
136const PQ_CODEC_NONE: i64 = 0
137const PQ_CODEC_SNAPPY: i64 = 1
138const PQ_PAGE_DATA: i64 = 0
139const PQ_PAGE_INDEX: i64 = 1
140const PQ_PAGE_DICT: i64 = 2
141const PQ_PAGE_DATA2: i64 = 3
142const PQ_REP_REQUIRED: i64 = 0
143const PQ_REP_OPTIONAL: i64 = 1
144const PQ_REP_REPEATED: i64 = 2
145// field ids
146const FMD_F_VERSION: i64 = 1
147const FMD_F_SCHEMA: i64 = 2
148const FMD_F_NROWS: i64 = 3
149const FMD_F_RGS: i64 = 4
150const FMD_F_CREATED: i64 = 6
151const SE_F_TYPE: i64 = 1
152const SE_F_REP: i64 = 3
153const SE_F_NAME: i64 = 4
154const SE_F_NCHILD: i64 = 5
155const SE_F_CONV: i64 = 6
156const RG_F_COLS: i64 = 1
157const RG_F_TOTAL: i64 = 2
158const RG_F_NROWS: i64 = 3
159const CC_F_META: i64 = 3
160const CM_F_TYPE: i64 = 1
161const CM_F_ENCS: i64 = 2
162const CM_F_PATH: i64 = 3
163const CM_F_CODEC: i64 = 4
164const CM_F_NVALS: i64 = 5
165const CM_F_UNCOMP: i64 = 6
166const CM_F_COMP: i64 = 7
167const CM_F_DATAOFF: i64 = 9
168const CM_F_DICTOFF: i64 = 11
169const PH_F_TYPE: i64 = 1
170const PH_F_UNCOMP: i64 = 2
171const PH_F_COMP: i64 = 3
172const PH_F_DPH: i64 = 5
173const PH_F_DICTH: i64 = 7
174const PH_F_DPH2: i64 = 8
175const DP_F_NVALS: i64 = 1
176const DP_F_ENC: i64 = 2
177const DP_F_DEFENC: i64 = 3
178const DP_F_REPENC: i64 = 4
179const DI_F_NVALS: i64 = 1
180const DI_F_ENC: i64 = 2
181const D2_F_NVALS: i64 = 1
182const D2_F_NNULLS: i64 = 2
183const D2_F_NROWS: i64 = 3
184const D2_F_ENC: i64 = 4
185const D2_F_DEFLEN: i64 = 5
186const D2_F_REPLEN: i64 = 6
187const D2_F_ISCOMP: i64 = 7
188
189// ---- the open-file context
190const PQ_BUF: i64 = 0
191const PQ_LEN: i64 = 1
192const PQ_META_OFF: i64 = 2
193const PQ_META_LEN: i64 = 3
194const PQ_NSE: i64 = 4
195const PQ_SE: i64 = 5
196const PQ_NLEAF: i64 = 6
197const PQ_NRG: i64 = 7
198const PQ_RG: i64 = 8
199const PQ_CC: i64 = 9
200const PQ_NROWS: i64 = 10
201const PQ_ERR: i64 = 11
202const PQ_CREATED_OFF: i64 = 12
203const PQ_CREATED_LEN: i64 = 13
204const PQ_VERSION: i64 = 14
205const PQ_WHAT: i64 = 15
206const PQ_CUR: i64 = 16
207const PQ_STK: i64 = 17
208const PQ_SLOTS: i64 = 18
209// schema element record
210const SE_TYPE: i64 = 0
211const SE_REP: i64 = 1
212const SE_NAMEOFF: i64 = 2
213const SE_NAMELEN: i64 = 3
214const SE_NCHILD: i64 = 4
215const SE_CONV: i64 = 5
216const SE_MAXDEF: i64 = 6
217const SE_MAXREP: i64 = 7
218const SE_LEAF: i64 = 8
219const SE_PARENT: i64 = 9
220const SE_SLOTS: i64 = 10
221// row group record
222const RG_NROWS: i64 = 0
223const RG_TOTAL: i64 = 1
224const RG_SLOTS: i64 = 2
225// column chunk record
226const CC_TYPE: i64 = 0
227const CC_CODEC: i64 = 1
228const CC_NVALS: i64 = 2
229const CC_UNCOMP: i64 = 3
230const CC_COMP: i64 = 4
231const CC_DATAOFF: i64 = 5
232const CC_DICTOFF: i64 = 6
233const CC_ENCMASK: i64 = 7
234const CC_PATHOFF: i64 = 8
235const CC_PATHLEN: i64 = 9
236const CC_SLOTS: i64 = 10
237// page header record
238const PH_TYPE: i64 = 0
239const PH_USZ: i64 = 1
240const PH_CSZ: i64 = 2
241const PH_NV: i64 = 3
242const PH_ENC: i64 = 4
243const PH_DEFENC: i64 = 5
244const PH_REPENC: i64 = 6
245const PH_NNULLS: i64 = 7
246const PH_NROWS: i64 = 8
247const PH_DEFLEN: i64 = 9
248const PH_REPLEN: i64 = 10
249const PH_ISCOMP: i64 = 11
250const PH_DICTN: i64 = 12
251const PH_DICTENC: i64 = 13
252const PH_SLOTS: i64 = 14
253// a decoded column chunk
254const PR_ARENA: i64 = 0
255const PR_ACAP: i64 = 1
256const PR_AUSED: i64 = 2
257const PR_OFF: i64 = 3
258const PR_LEN: i64 = 4
259const PR_DEF: i64 = 5
260const PR_REP: i64 = 6
261const PR_ROW: i64 = 7
262const PR_CAP: i64 = 8
263const PR_N: i64 = 9
264const PR_NONNULL: i64 = 10
265const PR_PAGES: i64 = 11
266const PR_DICTN: i64 = 12
267const PR_DOFF: i64 = 13
268const PR_DLEN: i64 = 14
269const PR_ROWS: i64 = 15
270const PR_ENCMASK: i64 = 16
271const PR_PTYPES: i64 = 17
272const PR_PH: i64 = 18
273const PR_SLOTS: i64 = 19
274// buffered writer
275const WB_BUF: i64 = 0
276const WB_CAP: i64 = 1
277const WB_USED: i64 = 2
278const WB_FD: i64 = 3
279const WB_ERR: i64 = 4
280const WB_TOTAL: i64 = 5
281const WB_NUM: i64 = 6
282const WB_SLOTS: i64 = 7
283const WB_DEFAULT_CAP: i64 = 1048576 // one MiB batched per write: syscalls per megabyte, never per row
284const WB_NUM_CAP: i64 = 24
285
286// ======================================================================================================
287// buffered writer
288// ======================================================================================================
289func wb_new(fd: i64) -> *i64 {
290 let w: *i64 = sys_mmap(WB_SLOTS * PQ_I64) as *i64
291 w[WB_BUF] = sys_mmap(WB_DEFAULT_CAP) as i64
292 w[WB_CAP] = WB_DEFAULT_CAP
293 w[WB_USED] = 0
294 w[WB_FD] = fd
295 w[WB_ERR] = 0
296 w[WB_TOTAL] = 0
297 w[WB_NUM] = sys_mmap(WB_NUM_CAP) as i64
298 return w
299}
300func wb_flush(w: *i64) -> i64 {
301 let b: *u8 = w[WB_BUF] as *u8
302 let n: i64 = w[WB_USED]
303 var off: i64 = 0
304 while off < n {
305 let r: i64 = sys_write(w[WB_FD], ((b as i64) + off) as *u8, n - off)
306 if r <= 0 { w[WB_ERR] = 1; w[WB_USED] = 0; return PQ_E_CAPACITY }
307 off = off + r
308 }
309 w[WB_TOTAL] = w[WB_TOTAL] + n
310 w[WB_USED] = 0
311 return 0
312}
313func wb_byte(w: *i64, ch: i64) -> i64 {
314 if w[WB_USED] >= w[WB_CAP] { wb_flush(w) }
315 let b: *u8 = w[WB_BUF] as *u8
316 b[w[WB_USED]] = ch as u8
317 w[WB_USED] = w[WB_USED] + 1
318 return 0
319}
320func wb_bytes(w: *i64, src: *u8, off: i64, n: i64) -> i64 {
321 var i: i64 = 0
322 while i < n { wb_byte(w, (src[off + i] & PQ_BYTE) as i64); i = i + 1 }
323 return 0
324}
325// the same bytes with every tab, newline and carriage return replaced by a space: a TSV cell holds ONE line
326func wb_cell(w: *i64, src: *u8, off: i64, n: i64) -> i64 {
327 var i: i64 = 0
328 while i < n {
329 var ch: i64 = (src[off + i] & PQ_BYTE) as i64
330 if ch == PQ_TAB || ch == PQ_NL || ch == PQ_CR { ch = PQ_SPACE }
331 wb_byte(w, ch)
332 i = i + 1
333 }
334 return 0
335}
336func wb_puts(w: *i64, s: *u8) -> i64 {
337 var i: i64 = 0
338 while (s[i] & PQ_BYTE) as i64 != 0 { wb_byte(w, (s[i] & PQ_BYTE) as i64); i = i + 1 }
339 return 0
340}
341func wb_putn(w: *i64, v0: i64) -> i64 {
342 var v: i64 = v0
343 if v < 0 { wb_byte(w, PQ_MINUS); v = 0 - v }
344 let tmp: *u8 = w[WB_NUM] as *u8
345 var n: i64 = 0
346 if v == 0 { tmp[0] = PQ_DIGIT0 as u8; n = 1 }
347 while v > 0 { tmp[n] = (PQ_DIGIT0 + (v % PQ_DECIMAL)) as u8; v = v / PQ_DECIMAL; n = n + 1 }
348 while n > 0 { n = n - 1; wb_byte(w, (tmp[n] & PQ_BYTE) as i64) }
349 return 0
350}
351func wb_kv(w: *i64, key: *u8, v: i64) -> i64 {
352 wb_puts(w, key)
353 wb_byte(w, PQ_EQ)
354 wb_putn(w, v)
355 wb_byte(w, PQ_SPACE)
356 return 0
357}
358
359// ======================================================================================================
360// thrift compact cursor
361// ======================================================================================================
362func tc_init(c: *i64, buf: *u8, pos: i64, end: i64) -> i64 {
363 c[TC_BUF] = buf as i64
364 c[TC_POS] = pos
365 c[TC_END] = end
366 c[TC_ERR] = 0
367 c[TC_FID] = 0
368 c[TC_LAST] = 0
369 c[TC_ET] = 0
370 return 0
371}
372func tc_byte(c: *i64) -> i64 {
373 if c[TC_ERR] != 0 { return 0 }
374 if c[TC_POS] >= c[TC_END] { c[TC_ERR] = PQ_E_TRUNC; return 0 }
375 let b: *u8 = c[TC_BUF] as *u8
376 let v: i64 = (b[c[TC_POS]] & PQ_BYTE) as i64
377 c[TC_POS] = c[TC_POS] + 1
378 return v
379}
380func tc_varint(c: *i64) -> i64 {
381 var v: i64 = 0
382 var sh: i64 = 0
383 var n: i64 = 0
384 var go: i64 = 1
385 while go == 1 {
386 let b: i64 = tc_byte(c)
387 if c[TC_ERR] != 0 { return 0 }
388 v = v | ((b & TC_VARINT_DATA) << sh)
389 sh = sh + TC_VARINT_SHIFT
390 n = n + 1
391 if (b & TC_VARINT_MORE) == 0 { go = 0 } else { if n >= TC_VARINT_MAX { c[TC_ERR] = PQ_E_THRIFT; return 0 } }
392 }
393 return v
394}
395// zigzag: (u >>> 1) ^ -(u & 1); the logical shift is the arithmetic shift with the sign bit cleared
396func tc_zz(c: *i64) -> i64 {
397 let u: i64 = tc_varint(c)
398 let half: i64 = (u >> 1) & PQ_I64_MAX
399 return half ^ (0 - (u & 1))
400}
401// returns the field type (TC_STOP at the end of a struct); the field id lands in c[TC_FID]
402func tc_field(c: *i64) -> i64 {
403 let b: i64 = tc_byte(c)
404 if c[TC_ERR] != 0 { return TC_STOP }
405 let t: i64 = b & TC_NIBBLE
406 if t == TC_STOP { return TC_STOP }
407 let delta: i64 = (b >> TC_TYPE_SHIFT) & TC_NIBBLE
408 if delta == 0 { c[TC_FID] = tc_zz(c) } else { c[TC_FID] = c[TC_LAST] + delta }
409 c[TC_LAST] = c[TC_FID]
410 return t
411}
412// list or set header: returns the size, the element type lands in c[TC_ET]
413func tc_list_head(c: *i64) -> i64 {
414 let b: i64 = tc_byte(c)
415 if c[TC_ERR] != 0 { return 0 }
416 c[TC_ET] = b & TC_NIBBLE
417 var n: i64 = (b >> TC_TYPE_SHIFT) & TC_NIBBLE
418 if n == TC_LONG_FORM { n = tc_varint(c) }
419 return n
420}
421func tc_skip_n(c: *i64, n: i64) -> i64 {
422 if n < 0 { c[TC_ERR] = PQ_E_THRIFT; return 0 }
423 if c[TC_POS] + n > c[TC_END] { c[TC_ERR] = PQ_E_TRUNC; return 0 }
424 c[TC_POS] = c[TC_POS] + n
425 return 0
426}
427// binary: returns the length, the byte offset lands in c[TC_ET] (a scratch slot; no allocation)
428func tc_binary(c: *i64) -> i64 {
429 let n: i64 = tc_varint(c)
430 if c[TC_ERR] != 0 { return 0 }
431 c[TC_ET] = c[TC_POS]
432 tc_skip_n(c, n)
433 return n
434}
435// skip a scalar of type t; elem=1 when it is a list element (a bool element is one byte). Returns 1 for a container.
436func tc_skip_scalar(c: *i64, t: i64, elem: i64) -> i64 {
437 if t == TC_BTRUE || t == TC_BFALSE { if elem == 1 { tc_skip_n(c, 1) } return 0 }
438 if t == TC_I8 { tc_skip_n(c, 1); return 0 }
439 if t == TC_I16 || t == TC_I32 || t == TC_I64 { tc_varint(c); return 0 }
440 if t == TC_DOUBLE { tc_skip_n(c, TC_DOUBLE_BYTES); return 0 }
441 if t == TC_UUID { tc_skip_n(c, TC_UUID_BYTES); return 0 }
442 if t == TC_BINARY { let n: i64 = tc_varint(c); tc_skip_n(c, n); return 0 }
443 if t == TC_LIST || t == TC_SET || t == TC_MAP || t == TC_STRUCT { return 1 }
444 c[TC_ERR] = PQ_E_THRIFT
445 return 0
446}
447// open a container frame at depth d (reads its header)
448func tc_open(c: *i64, st: *i64, d: i64, t: i64) -> i64 {
449 let f: i64 = d * TS_SLOTS
450 st[f + TS_LAST] = c[TC_LAST]
451 st[f + TS_ET] = 0
452 st[f + TS_VT] = 0
453 if t == TC_STRUCT { st[f + TS_KIND] = TK_STRUCT; st[f + TS_REM] = 0; c[TC_LAST] = 0; return 0 }
454 if t == TC_MAP {
455 st[f + TS_KIND] = TK_MAP
456 let n: i64 = tc_varint(c)
457 st[f + TS_REM] = n * 2
458 if n > 0 {
459 let kv: i64 = tc_byte(c)
460 st[f + TS_ET] = (kv >> TC_TYPE_SHIFT) & TC_NIBBLE
461 st[f + TS_VT] = kv & TC_NIBBLE
462 }
463 return 0
464 }
465 st[f + TS_KIND] = TK_LIST
466 st[f + TS_REM] = tc_list_head(c)
467 st[f + TS_ET] = c[TC_ET]
468 return 0
469}
470// skip one value of type t, containers included, without recursion (an explicit frame stack, bounded by TS_DEPTH)
471func tc_skip(c: *i64, t: i64, st: *i64) -> i64 {
472 if c[TC_ERR] != 0 { return 0 }
473 if tc_skip_scalar(c, t, 0) == 0 { return 0 }
474 tc_open(c, st, 0, t)
475 var d: i64 = 1
476 while d > 0 {
477 if c[TC_ERR] != 0 { return 0 }
478 let f: i64 = (d - 1) * TS_SLOTS
479 let k: i64 = st[f + TS_KIND]
480 var nt: i64 = TC_STOP
481 var elem: i64 = 0
482 var pop: i64 = 0
483 if k == TK_STRUCT {
484 nt = tc_field(c)
485 if nt == TC_STOP { pop = 1 }
486 } else {
487 if st[f + TS_REM] <= 0 { pop = 1 } else {
488 elem = 1
489 if k == TK_LIST { nt = st[f + TS_ET] } else {
490 if (st[f + TS_REM] & 1) == 0 { nt = st[f + TS_ET] } else { nt = st[f + TS_VT] }
491 }
492 st[f + TS_REM] = st[f + TS_REM] - 1
493 }
494 }
495 if pop == 1 {
496 c[TC_LAST] = st[f + TS_LAST]
497 d = d - 1
498 } else {
499 if tc_skip_scalar(c, nt, elem) == 1 {
500 if d >= TS_DEPTH { c[TC_ERR] = PQ_E_THRIFT; return 0 }
501 tc_open(c, st, d, nt)
502 d = d + 1
503 }
504 }
505 }
506 return 0
507}
508
509// ======================================================================================================
510// small helpers
511// ======================================================================================================
512func pq_le32(b: *u8, p: i64) -> i64 {
513 return ((b[p] & PQ_BYTE) as i64) | (((b[p + 1] & PQ_BYTE) as i64) << 8) | (((b[p + 2] & PQ_BYTE) as i64) << 16) | (((b[p + 3] & PQ_BYTE) as i64) << 24)
514}
515// bits needed to hold values 0..maxv
516func pq_bitwidth(maxv: i64) -> i64 {
517 var w: i64 = 0
518 var v: i64 = maxv
519 while v > 0 { w = w + 1; v = v >> 1 }
520 return w
521}
522func pq_fill(a: *i64, base: i64, n: i64, v: i64) -> i64 {
523 var i: i64 = 0
524 while i < n { a[base + i] = v; i = i + 1 }
525 return 0
526}
527func pq_se(pq: *i64, i: i64) -> *i64 {
528 return ((pq[PQ_SE] as i64) + i * SE_SLOTS * PQ_I64) as *i64
529}
530func pq_rg(pq: *i64, r: i64) -> *i64 {
531 return ((pq[PQ_RG] as i64) + r * RG_SLOTS * PQ_I64) as *i64
532}
533func pq_cc(pq: *i64, r: i64, leaf: i64) -> *i64 {
534 return ((pq[PQ_CC] as i64) + (r * pq[PQ_NLEAF] + leaf) * CC_SLOTS * PQ_I64) as *i64
535}
536// the schema element index of a leaf, or PQ_NONE
537func pq_leaf_index(pq: *i64, leaf: i64) -> i64 {
538 var i: i64 = 0
539 while i < pq[PQ_NSE] {
540 let se: *i64 = pq_se(pq, i)
541 if se[SE_LEAF] == leaf { return i }
542 i = i + 1
543 }
544 return PQ_NONE
545}
546// the leaf whose TOP-LEVEL field name equals s (the ancestor directly under the root; the leaf itself when flat),
547// or PQ_NONE. A list column's leaf is named element by the writer (measured on BRIGHT: three leaves named element),
548// so callers name the column and never the element.
549func pq_leaf_named(pq: *i64, s: *u8) -> i64 {
550 var sl: i64 = 0
551 while (s[sl] & PQ_BYTE) as i64 != 0 { sl = sl + 1 }
552 let b: *u8 = pq[PQ_BUF] as *u8
553 var i: i64 = 0
554 while i < pq[PQ_NSE] {
555 let se: *i64 = pq_se(pq, i)
556 if se[SE_LEAF] >= 0 {
557 var ti: i64 = i
558 var guard: i64 = 0
559 while guard < pq[PQ_NSE] {
560 let te: *i64 = pq_se(pq, ti)
561 if te[SE_PARENT] <= 0 { guard = pq[PQ_NSE] } else { ti = te[SE_PARENT]; guard = guard + 1 }
562 }
563 let top: *i64 = pq_se(pq, ti)
564 if top[SE_NAMELEN] == sl {
565 var k: i64 = 0
566 var same: i64 = 1
567 while k < sl { if (b[top[SE_NAMEOFF] + k] & PQ_BYTE) as i64 != (s[k] & PQ_BYTE) as i64 { same = 0 } k = k + 1 }
568 if same == 1 { return se[SE_LEAF] }
569 }
570 }
571 i = i + 1
572 }
573 return PQ_NONE
574}
575
576// ======================================================================================================
577// footer
578// ======================================================================================================
579func pq_parse_columnmeta(pq: *i64, c: *i64, st: *i64, cc: *i64) -> i64 {
580 let saved: i64 = c[TC_LAST]
581 c[TC_LAST] = 0
582 var go: i64 = 1
583 while go == 1 {
584 let ft: i64 = tc_field(c)
585 if c[TC_ERR] != 0 { return c[TC_ERR] }
586 if ft == TC_STOP { go = 0 } else {
587 let fid: i64 = c[TC_FID]
588 var handled: i64 = 0
589 if fid == CM_F_TYPE { cc[CC_TYPE] = tc_zz(c); handled = 1 }
590 if fid == CM_F_ENCS && ft == TC_LIST {
591 let n: i64 = tc_list_head(c)
592 var i: i64 = 0
593 while i < n { let e: i64 = tc_zz(c); if e >= 0 && e < PQ_MAX_BITWIDTH { cc[CC_ENCMASK] = cc[CC_ENCMASK] | (1 << e) } i = i + 1 }
594 handled = 1
595 }
596 if fid == CM_F_PATH && ft == TC_LIST {
597 let n2: i64 = tc_list_head(c)
598 var j: i64 = 0
599 while j < n2 { let l: i64 = tc_binary(c); cc[CC_PATHOFF] = c[TC_ET]; cc[CC_PATHLEN] = l; j = j + 1 }
600 handled = 1
601 }
602 if fid == CM_F_CODEC { cc[CC_CODEC] = tc_zz(c); handled = 1 }
603 if fid == CM_F_NVALS { cc[CC_NVALS] = tc_zz(c); handled = 1 }
604 if fid == CM_F_UNCOMP { cc[CC_UNCOMP] = tc_zz(c); handled = 1 }
605 if fid == CM_F_COMP { cc[CC_COMP] = tc_zz(c); handled = 1 }
606 if fid == CM_F_DATAOFF { cc[CC_DATAOFF] = tc_zz(c); handled = 1 }
607 if fid == CM_F_DICTOFF { cc[CC_DICTOFF] = tc_zz(c); handled = 1 }
608 if handled == 0 { tc_skip(c, ft, st) }
609 }
610 }
611 c[TC_LAST] = saved
612 return 0
613}
614func pq_parse_columnchunk(pq: *i64, c: *i64, st: *i64, cc: *i64) -> i64 {
615 let saved: i64 = c[TC_LAST]
616 c[TC_LAST] = 0
617 var go: i64 = 1
618 while go == 1 {
619 let ft: i64 = tc_field(c)
620 if c[TC_ERR] != 0 { return c[TC_ERR] }
621 if ft == TC_STOP { go = 0 } else {
622 if c[TC_FID] == CC_F_META && ft == TC_STRUCT { let rc: i64 = pq_parse_columnmeta(pq, c, st, cc); if rc < 0 { return rc } } else { tc_skip(c, ft, st) }
623 }
624 }
625 c[TC_LAST] = saved
626 return 0
627}
628func pq_parse_rowgroups(pq: *i64, c: *i64, st: *i64) -> i64 {
629 let nrg: i64 = tc_list_head(c)
630 if c[TC_ERR] != 0 { return c[TC_ERR] }
631 let nleaf: i64 = pq[PQ_NLEAF]
632 if nleaf <= 0 { return PQ_E_ORDER }
633 pq[PQ_NRG] = nrg
634 pq[PQ_RG] = sys_mmap((nrg + 1) * RG_SLOTS * PQ_I64) as i64
635 pq[PQ_CC] = sys_mmap((nrg * nleaf + 1) * CC_SLOTS * PQ_I64) as i64
636 var r: i64 = 0
637 while r < nrg {
638 let rg: *i64 = pq_rg(pq, r)
639 rg[RG_NROWS] = PQ_NONE
640 rg[RG_TOTAL] = PQ_NONE
641 var j0: i64 = 0
642 while j0 < nleaf {
643 let cc0: *i64 = pq_cc(pq, r, j0)
644 var s: i64 = 0
645 while s < CC_SLOTS { cc0[s] = PQ_NONE; s = s + 1 }
646 cc0[CC_ENCMASK] = 0
647 j0 = j0 + 1
648 }
649 let saved: i64 = c[TC_LAST]
650 c[TC_LAST] = 0
651 var go: i64 = 1
652 while go == 1 {
653 let ft: i64 = tc_field(c)
654 if c[TC_ERR] != 0 { return c[TC_ERR] }
655 if ft == TC_STOP { go = 0 } else {
656 let fid: i64 = c[TC_FID]
657 var handled: i64 = 0
658 if fid == RG_F_COLS && ft == TC_LIST {
659 let m: i64 = tc_list_head(c)
660 if m != nleaf { pq[PQ_WHAT] = m; return PQ_E_COLUMNS }
661 var j: i64 = 0
662 while j < m { let rc: i64 = pq_parse_columnchunk(pq, c, st, pq_cc(pq, r, j)); if rc < 0 { return rc } j = j + 1 }
663 handled = 1
664 }
665 if fid == RG_F_TOTAL { rg[RG_TOTAL] = tc_zz(c); handled = 1 }
666 if fid == RG_F_NROWS { rg[RG_NROWS] = tc_zz(c); handled = 1 }
667 if handled == 0 { tc_skip(c, ft, st) }
668 }
669 }
670 c[TC_LAST] = saved
671 r = r + 1
672 }
673 return 0
674}
675// max definition and repetition levels from the depth-first list, parents by a stack of (index, children left)
676func pq_schema_levels(pq: *i64) -> i64 {
677 let nse: i64 = pq[PQ_NSE]
678 if nse <= 0 { return PQ_E_THRIFT }
679 let sidx: *i64 = sys_mmap((nse + 1) * PQ_I64) as *i64
680 let srem: *i64 = sys_mmap((nse + 1) * PQ_I64) as *i64
681 let root: *i64 = pq_se(pq, 0)
682 root[SE_MAXDEF] = 0
683 root[SE_MAXREP] = 0
684 root[SE_PARENT] = PQ_NONE
685 root[SE_LEAF] = PQ_NONE
686 var depth: i64 = 0
687 if root[SE_NCHILD] > 0 { sidx[0] = 0; srem[0] = root[SE_NCHILD]; depth = 1 }
688 var leafn: i64 = 0
689 var i: i64 = 1
690 while i < nse {
691 if depth <= 0 { return PQ_E_THRIFT }
692 let se: *i64 = pq_se(pq, i)
693 let pi: i64 = sidx[depth - 1]
694 let parent: *i64 = pq_se(pq, pi)
695 se[SE_PARENT] = pi
696 var d: i64 = parent[SE_MAXDEF]
697 var rp: i64 = parent[SE_MAXREP]
698 if se[SE_REP] != PQ_REP_REQUIRED { d = d + 1 }
699 if se[SE_REP] == PQ_REP_REPEATED { rp = rp + 1 }
700 se[SE_MAXDEF] = d
701 se[SE_MAXREP] = rp
702 srem[depth - 1] = srem[depth - 1] - 1
703 if se[SE_NCHILD] > 0 {
704 se[SE_LEAF] = PQ_NONE
705 sidx[depth] = i
706 srem[depth] = se[SE_NCHILD]
707 depth = depth + 1
708 } else {
709 se[SE_LEAF] = leafn
710 leafn = leafn + 1
711 }
712 while depth > 0 && srem[depth - 1] <= 0 { depth = depth - 1 }
713 i = i + 1
714 }
715 pq[PQ_NLEAF] = leafn
716 return 0
717}
718func pq_parse_schema(pq: *i64, c: *i64, st: *i64) -> i64 {
719 let nse: i64 = tc_list_head(c)
720 if c[TC_ERR] != 0 { return c[TC_ERR] }
721 pq[PQ_NSE] = nse
722 pq[PQ_SE] = sys_mmap((nse + 1) * SE_SLOTS * PQ_I64) as i64
723 var i: i64 = 0
724 while i < nse {
725 let se: *i64 = pq_se(pq, i)
726 se[SE_TYPE] = PQ_NONE
727 se[SE_REP] = PQ_REP_REQUIRED
728 se[SE_NAMEOFF] = 0
729 se[SE_NAMELEN] = 0
730 se[SE_NCHILD] = 0
731 se[SE_CONV] = PQ_NONE
732 let saved: i64 = c[TC_LAST]
733 c[TC_LAST] = 0
734 var go: i64 = 1
735 while go == 1 {
736 let ft: i64 = tc_field(c)
737 if c[TC_ERR] != 0 { return c[TC_ERR] }
738 if ft == TC_STOP { go = 0 } else {
739 let fid: i64 = c[TC_FID]
740 var handled: i64 = 0
741 if fid == SE_F_TYPE { se[SE_TYPE] = tc_zz(c); handled = 1 }
742 if fid == SE_F_REP { se[SE_REP] = tc_zz(c); handled = 1 }
743 if fid == SE_F_NAME && ft == TC_BINARY { let l: i64 = tc_binary(c); se[SE_NAMEOFF] = c[TC_ET]; se[SE_NAMELEN] = l; handled = 1 }
744 if fid == SE_F_NCHILD { se[SE_NCHILD] = tc_zz(c); handled = 1 }
745 if fid == SE_F_CONV { se[SE_CONV] = tc_zz(c); handled = 1 }
746 if handled == 0 { tc_skip(c, ft, st) }
747 }
748 }
749 c[TC_LAST] = saved
750 i = i + 1
751 }
752 return pq_schema_levels(pq)
753}
754func pq_parse_footer(pq: *i64) -> i64 {
755 let c: *i64 = pq[PQ_CUR] as *i64
756 let st: *i64 = pq[PQ_STK] as *i64
757 tc_init(c, pq[PQ_BUF] as *u8, pq[PQ_META_OFF], pq[PQ_META_OFF] + pq[PQ_META_LEN])
758 var go: i64 = 1
759 while go == 1 {
760 let ft: i64 = tc_field(c)
761 if c[TC_ERR] != 0 { return c[TC_ERR] }
762 if ft == TC_STOP { go = 0 } else {
763 let fid: i64 = c[TC_FID]
764 var handled: i64 = 0
765 if fid == FMD_F_VERSION { pq[PQ_VERSION] = tc_zz(c); handled = 1 }
766 if fid == FMD_F_SCHEMA && ft == TC_LIST { let rc: i64 = pq_parse_schema(pq, c, st); if rc < 0 { return rc } handled = 1 }
767 if fid == FMD_F_NROWS { pq[PQ_NROWS] = tc_zz(c); handled = 1 }
768 if fid == FMD_F_RGS && ft == TC_LIST { let rc2: i64 = pq_parse_rowgroups(pq, c, st); if rc2 < 0 { return rc2 } handled = 1 }
769 if fid == FMD_F_CREATED && ft == TC_BINARY { let l: i64 = tc_binary(c); pq[PQ_CREATED_OFF] = c[TC_ET]; pq[PQ_CREATED_LEN] = l; handled = 1 }
770 if handled == 0 { tc_skip(c, ft, st) }
771 }
772 }
773 if c[TC_ERR] != 0 { return c[TC_ERR] }
774 if pq[PQ_NLEAF] <= 0 { return PQ_E_THRIFT }
775 if pq[PQ_NRG] < 0 { return PQ_E_THRIFT }
776 return 0
777}
778// open: read the whole file (sized from the file), check both magics, decode the footer. Returns 0 or a refusal,
779// mirrored into pq[PQ_ERR].
780func pq_open(pq: *i64, path: *u8) -> i64 {
781 var s: i64 = 0
782 while s < PQ_SLOTS { pq[s] = 0; s = s + 1 }
783 pq[PQ_NRG] = PQ_NONE
784 pq[PQ_WHAT] = PQ_NONE
785 pq[PQ_CUR] = sys_mmap(TC_SLOTS * PQ_I64) as i64
786 pq[PQ_STK] = sys_mmap(TS_DEPTH * TS_SLOTS * PQ_I64) as i64
787 let lenp: *i64 = sys_mmap(PQ_I64) as *i64
788 lenp[0] = 0
789 let buf: *u8 = sys_read_file(path, lenp)
790 let len: i64 = lenp[0]
791 if (buf as i64) == 0 || len <= 0 { pq[PQ_ERR] = PQ_E_OPEN; return PQ_E_OPEN }
792 pq[PQ_BUF] = buf as i64
793 pq[PQ_LEN] = len
794 if len < PQ_MAGIC_LEN + PQ_TAIL_LEN { pq[PQ_ERR] = PQ_E_MAGIC; return PQ_E_MAGIC }
795 var ok: i64 = 1
796 if (buf[0] & PQ_BYTE) as i64 != PQ_MAGIC_P { ok = 0 }
797 if (buf[1] & PQ_BYTE) as i64 != PQ_MAGIC_A { ok = 0 }
798 if (buf[2] & PQ_BYTE) as i64 != PQ_MAGIC_R { ok = 0 }
799 if (buf[3] & PQ_BYTE) as i64 != PQ_MAGIC_1 { ok = 0 }
800 let t: i64 = len - PQ_MAGIC_LEN
801 if (buf[t] & PQ_BYTE) as i64 != PQ_MAGIC_P { ok = 0 }
802 if (buf[t + 1] & PQ_BYTE) as i64 != PQ_MAGIC_A { ok = 0 }
803 if (buf[t + 2] & PQ_BYTE) as i64 != PQ_MAGIC_R { ok = 0 }
804 if (buf[t + 3] & PQ_BYTE) as i64 != PQ_MAGIC_1 { ok = 0 }
805 if ok == 0 { pq[PQ_ERR] = PQ_E_MAGIC; return PQ_E_MAGIC }
806 let flen: i64 = pq_le32(buf, len - PQ_TAIL_LEN)
807 if flen <= 0 || flen > len - PQ_MAGIC_LEN - PQ_TAIL_LEN { pq[PQ_ERR] = PQ_E_TRUNC; return PQ_E_TRUNC }
808 pq[PQ_META_LEN] = flen
809 pq[PQ_META_OFF] = len - PQ_TAIL_LEN - flen
810 let rc: i64 = pq_parse_footer(pq)
811 pq[PQ_ERR] = rc
812 return rc
813}
814
815// ======================================================================================================
816// pages
817// ======================================================================================================
818func pq_parse_sub(c: *i64, st: *i64, ph: *i64, which: i64) -> i64 {
819 let saved: i64 = c[TC_LAST]
820 c[TC_LAST] = 0
821 var go: i64 = 1
822 while go == 1 {
823 let ft: i64 = tc_field(c)
824 if c[TC_ERR] != 0 { return c[TC_ERR] }
825 if ft == TC_STOP { go = 0 } else {
826 let fid: i64 = c[TC_FID]
827 var handled: i64 = 0
828 if which == PQ_PAGE_DATA {
829 if fid == DP_F_NVALS { ph[PH_NV] = tc_zz(c); handled = 1 }
830 if fid == DP_F_ENC { ph[PH_ENC] = tc_zz(c); handled = 1 }
831 if fid == DP_F_DEFENC { ph[PH_DEFENC] = tc_zz(c); handled = 1 }
832 if fid == DP_F_REPENC { ph[PH_REPENC] = tc_zz(c); handled = 1 }
833 }
834 if which == PQ_PAGE_DICT {
835 if fid == DI_F_NVALS { ph[PH_DICTN] = tc_zz(c); handled = 1 }
836 if fid == DI_F_ENC { ph[PH_DICTENC] = tc_zz(c); handled = 1 }
837 }
838 if which == PQ_PAGE_DATA2 {
839 if fid == D2_F_NVALS { ph[PH_NV] = tc_zz(c); handled = 1 }
840 if fid == D2_F_NNULLS { ph[PH_NNULLS] = tc_zz(c); handled = 1 }
841 if fid == D2_F_NROWS { ph[PH_NROWS] = tc_zz(c); handled = 1 }
842 if fid == D2_F_ENC { ph[PH_ENC] = tc_zz(c); handled = 1 }
843 if fid == D2_F_DEFLEN { ph[PH_DEFLEN] = tc_zz(c); handled = 1 }
844 if fid == D2_F_REPLEN { ph[PH_REPLEN] = tc_zz(c); handled = 1 }
845 if fid == D2_F_ISCOMP { if ft == TC_BTRUE { ph[PH_ISCOMP] = 1 } else { ph[PH_ISCOMP] = 0 } handled = 1 }
846 }
847 if handled == 0 { tc_skip(c, ft, st) }
848 }
849 }
850 c[TC_LAST] = saved
851 return 0
852}
853// decode the PageHeader at pos; returns the offset of the page payload (header end) or a refusal
854func pq_parse_pageheader(pq: *i64, pos: i64, ph: *i64) -> i64 {
855 let c: *i64 = pq[PQ_CUR] as *i64
856 let st: *i64 = pq[PQ_STK] as *i64
857 if pos < 0 || pos >= pq[PQ_LEN] { return PQ_E_TRUNC }
858 tc_init(c, pq[PQ_BUF] as *u8, pos, pq[PQ_LEN])
859 var i: i64 = 0
860 while i < PH_SLOTS { ph[i] = PQ_NONE; i = i + 1 }
861 ph[PH_ISCOMP] = 1
862 var go: i64 = 1
863 while go == 1 {
864 let ft: i64 = tc_field(c)
865 if c[TC_ERR] != 0 { return c[TC_ERR] }
866 if ft == TC_STOP { go = 0 } else {
867 let fid: i64 = c[TC_FID]
868 var handled: i64 = 0
869 if fid == PH_F_TYPE { ph[PH_TYPE] = tc_zz(c); handled = 1 }
870 if fid == PH_F_UNCOMP { ph[PH_USZ] = tc_zz(c); handled = 1 }
871 if fid == PH_F_COMP { ph[PH_CSZ] = tc_zz(c); handled = 1 }
872 if fid == PH_F_DPH && ft == TC_STRUCT { let r1: i64 = pq_parse_sub(c, st, ph, PQ_PAGE_DATA); if r1 < 0 { return r1 } handled = 1 }
873 if fid == PH_F_DICTH && ft == TC_STRUCT { let r2: i64 = pq_parse_sub(c, st, ph, PQ_PAGE_DICT); if r2 < 0 { return r2 } handled = 1 }
874 if fid == PH_F_DPH2 && ft == TC_STRUCT { let r3: i64 = pq_parse_sub(c, st, ph, PQ_PAGE_DATA2); if r3 < 0 { return r3 } handled = 1 }
875 if handled == 0 { tc_skip(c, ft, st) }
876 }
877 }
878 if c[TC_ERR] != 0 { return c[TC_ERR] }
879 return c[TC_POS]
880}
881// snappy raw stream src[s..e) into out[o..): returns bytes produced or a refusal
882func pq_snappy(src: *u8, s: i64, e: i64, out: *u8, o: i64, ocap: i64) -> i64 {
883 var p: i64 = s
884 var ulen: i64 = 0
885 var sh: i64 = 0
886 var more: i64 = 1
887 var nb: i64 = 0
888 while more == 1 {
889 if p >= e { return PQ_E_TRUNC }
890 let b: i64 = (src[p] & PQ_BYTE) as i64
891 p = p + 1
892 ulen = ulen | ((b & TC_VARINT_DATA) << sh)
893 sh = sh + TC_VARINT_SHIFT
894 nb = nb + 1
895 if (b & TC_VARINT_MORE) == 0 { more = 0 } else { if nb >= TC_VARINT_MAX { return PQ_E_SNAPPY } }
896 }
897 if ulen < 0 || o + ulen > ocap { return PQ_E_CAPACITY }
898 let oend: i64 = o + ulen
899 var op: i64 = o
900 while p < e {
901 let tag: i64 = (src[p] & PQ_BYTE) as i64
902 p = p + 1
903 let kind: i64 = tag & 3
904 var len: i64 = 0
905 var off: i64 = 0
906 if kind == 0 {
907 let lc: i64 = tag >> 2
908 if lc < 60 { len = lc + 1 } else {
909 let nbytes: i64 = lc - 59
910 if p + nbytes > e { return PQ_E_TRUNC }
911 var k: i64 = 0
912 while k < nbytes { len = len | (((src[p + k] & PQ_BYTE) as i64) << (k * PQ_BITS)); k = k + 1 }
913 p = p + nbytes
914 len = len + 1
915 }
916 if p + len > e { return PQ_E_TRUNC }
917 if op + len > oend { return PQ_E_SNAPPY }
918 var i: i64 = 0
919 while i < len { out[op + i] = src[p + i]; i = i + 1 }
920 p = p + len
921 op = op + len
922 } else {
923 if kind == 1 {
924 len = ((tag >> 2) & 7) + 4
925 if p + 1 > e { return PQ_E_TRUNC }
926 off = ((tag >> 5) << PQ_BITS) | ((src[p] & PQ_BYTE) as i64)
927 p = p + 1
928 }
929 if kind == 2 {
930 len = (tag >> 2) + 1
931 if p + 2 > e { return PQ_E_TRUNC }
932 off = ((src[p] & PQ_BYTE) as i64) | (((src[p + 1] & PQ_BYTE) as i64) << PQ_BITS)
933 p = p + 2
934 }
935 if kind == 3 {
936 len = (tag >> 2) + 1
937 if p + PQ_I32_LEN > e { return PQ_E_TRUNC }
938 off = pq_le32(src, p)
939 p = p + PQ_I32_LEN
940 }
941 // the back-reference must live inside what this stream has already produced
942 if off <= 0 || off > op - o { return PQ_E_SNAPPY }
943 if op + len > oend { return PQ_E_SNAPPY }
944 var j: i64 = 0
945 while j < len { out[op] = out[op - off]; op = op + 1; j = j + 1 }
946 }
947 }
948 if op != oend { return PQ_E_SNAPPY }
949 return ulen
950}
951// a page payload src[s..e) into the arena at a0 under the page's declared uncompressed size
952func pq_inflate_page(pq: *i64, codec: i64, src: *u8, s: i64, e: i64, arena: *u8, a0: i64, acap: i64, usz: i64) -> i64 {
953 if codec == PQ_CODEC_NONE {
954 let n: i64 = e - s
955 if n < 0 { return PQ_E_TRUNC }
956 if a0 + n > acap { return PQ_E_CAPACITY }
957 var i: i64 = 0
958 while i < n { arena[a0 + i] = src[s + i]; i = i + 1 }
959 return n
960 }
961 if codec != PQ_CODEC_SNAPPY { pq[PQ_WHAT] = codec; return PQ_E_UNSUPPORTED }
962 if a0 + usz > acap { return PQ_E_CAPACITY }
963 let got: i64 = pq_snappy(src, s, e, arena, a0, acap)
964 if got < 0 { return got }
965 if got != usz { return PQ_E_SNAPPY }
966 return got
967}
968// RLE / bit-packed hybrid: n values of width bw from src[s..e) into out[0..n); returns bytes consumed or a refusal
969func pq_hybrid(src: *u8, s: i64, e: i64, bw: i64, n: i64, out: *i64, cap: i64) -> i64 {
970 if n > cap { return PQ_E_CAPACITY }
971 if bw < 0 || bw > PQ_MAX_BITWIDTH { return PQ_E_LEVELS }
972 var got: i64 = 0
973 if bw == 0 { while got < n { out[got] = 0; got = got + 1 } return 0 }
974 var p: i64 = s
975 let vbytes: i64 = (bw + PQ_BITS - 1) / PQ_BITS
976 while got < n {
977 var h: i64 = 0
978 var sh: i64 = 0
979 var more: i64 = 1
980 var nb: i64 = 0
981 while more == 1 {
982 if p >= e { return PQ_E_TRUNC }
983 let b: i64 = (src[p] & PQ_BYTE) as i64
984 p = p + 1
985 h = h | ((b & TC_VARINT_DATA) << sh)
986 sh = sh + TC_VARINT_SHIFT
987 nb = nb + 1
988 if (b & TC_VARINT_MORE) == 0 { more = 0 } else { if nb >= TC_VARINT_MAX { return PQ_E_LEVELS } }
989 }
990 if (h & 1) == 0 {
991 var cnt: i64 = h >> 1
992 if p + vbytes > e { return PQ_E_TRUNC }
993 var v: i64 = 0
994 var k: i64 = 0
995 while k < vbytes { v = v | (((src[p + k] & PQ_BYTE) as i64) << (k * PQ_BITS)); k = k + 1 }
996 p = p + vbytes
997 if got + cnt > n { cnt = n - got }
998 var j: i64 = 0
999 while j < cnt { out[got + j] = v; j = j + 1 }
1000 got = got + cnt
1001 } else {
1002 let groups: i64 = h >> 1
1003 var g: i64 = 0
1004 while g < groups {
1005 if p + bw > e { return PQ_E_TRUNC }
1006 var bit: i64 = p * PQ_BITS
1007 var k2: i64 = 0
1008 while k2 < PQ_HYBRID_GROUP {
1009 var v2: i64 = 0
1010 var bi: i64 = 0
1011 while bi < bw {
1012 let byte: i64 = (src[bit >> 3] & PQ_BYTE) as i64
1013 v2 = v2 | (((byte >> (bit & 7)) & 1) << bi)
1014 bit = bit + 1
1015 bi = bi + 1
1016 }
1017 if got < n { out[got] = v2; got = got + 1 }
1018 k2 = k2 + 1
1019 }
1020 p = p + bw
1021 g = g + 1
1022 }
1023 }
1024 }
1025 return p - s
1026}
1027// PLAIN byte arrays: n values from arena[s..e) into off[]/len[] (arena offsets); returns bytes consumed or a refusal
1028func pq_plain_bytes(arena: *u8, s: i64, e: i64, n: i64, off: *i64, len: *i64) -> i64 {
1029 var p: i64 = s
1030 var i: i64 = 0
1031 while i < n {
1032 if p + PQ_I32_LEN > e { return PQ_E_TRUNC }
1033 let l: i64 = pq_le32(arena, p)
1034 p = p + PQ_I32_LEN
1035 if l < 0 || p + l > e { return PQ_E_TRUNC }
1036 off[i] = p
1037 len[i] = l
1038 p = p + l
1039 i = i + 1
1040 }
1041 return p - s
1042}
1043// allocate a decode context sized from the column chunk record (entries from num_values, arena from
1044// total_uncompressed_size); the dictionary tables are sized from the dictionary page header when one is met
1045func pr_alloc(pq: *i64, r: i64, leaf: i64) -> *i64 {
1046 let cc: *i64 = pq_cc(pq, r, leaf)
1047 let pr: *i64 = sys_mmap(PR_SLOTS * PQ_I64) as *i64
1048 var s: i64 = 0
1049 while s < PR_SLOTS { pr[s] = 0; s = s + 1 }
1050 var nv: i64 = cc[CC_NVALS]
1051 if nv < 0 { nv = 0 }
1052 var acap: i64 = cc[CC_UNCOMP]
1053 if acap < 0 { acap = 0 }
1054 acap = acap + PQ_ARENA_SLACK
1055 pr[PR_ARENA] = sys_mmap(acap) as i64
1056 pr[PR_ACAP] = acap
1057 pr[PR_OFF] = sys_mmap((nv + 1) * PQ_I64) as i64
1058 pr[PR_LEN] = sys_mmap((nv + 1) * PQ_I64) as i64
1059 pr[PR_DEF] = sys_mmap((nv + 1) * PQ_I64) as i64
1060 pr[PR_REP] = sys_mmap((nv + 1) * PQ_I64) as i64
1061 pr[PR_ROW] = sys_mmap((nv + 1) * PQ_I64) as i64
1062 pr[PR_CAP] = nv
1063 pr[PR_PH] = sys_mmap(PH_SLOTS * PQ_I64) as i64
1064 return pr
1065}
1066func pr_free(pr: *i64) -> i64 {
1067 let nv: i64 = pr[PR_CAP]
1068 sys_munmap(pr[PR_ARENA] as *u8, pr[PR_ACAP])
1069 sys_munmap(pr[PR_OFF] as *u8, (nv + 1) * PQ_I64)
1070 sys_munmap(pr[PR_LEN] as *u8, (nv + 1) * PQ_I64)
1071 sys_munmap(pr[PR_DEF] as *u8, (nv + 1) * PQ_I64)
1072 sys_munmap(pr[PR_REP] as *u8, (nv + 1) * PQ_I64)
1073 sys_munmap(pr[PR_ROW] as *u8, (nv + 1) * PQ_I64)
1074 if pr[PR_DICTN] > 0 {
1075 sys_munmap(pr[PR_DOFF] as *u8, (pr[PR_DICTN] + 1) * PQ_I64)
1076 sys_munmap(pr[PR_DLEN] as *u8, (pr[PR_DICTN] + 1) * PQ_I64)
1077 }
1078 return 0
1079}
1080// read one BYTE_ARRAY column chunk (row group r, leaf) into pr: every entry gets def, rep, row and, when present,
1081// an arena offset and length. Returns the entry count or a refusal.
1082func pq_read_column(pq: *i64, r: i64, leaf: i64, pr: *i64) -> i64 {
1083 if r < 0 || r >= pq[PQ_NRG] { return PQ_E_ARG }
1084 if leaf < 0 || leaf >= pq[PQ_NLEAF] { return PQ_E_ARG }
1085 let cc: *i64 = pq_cc(pq, r, leaf)
1086 let sei: i64 = pq_leaf_index(pq, leaf)
1087 if sei < 0 { return PQ_E_ARG }
1088 let se: *i64 = pq_se(pq, sei)
1089 if cc[CC_TYPE] != PQ_T_BYTE_ARRAY { pq[PQ_WHAT] = cc[CC_TYPE]; return PQ_E_UNSUPPORTED }
1090 if cc[CC_CODEC] != PQ_CODEC_NONE && cc[CC_CODEC] != PQ_CODEC_SNAPPY { pq[PQ_WHAT] = cc[CC_CODEC]; return PQ_E_UNSUPPORTED }
1091 let maxdef: i64 = se[SE_MAXDEF]
1092 let maxrep: i64 = se[SE_MAXREP]
1093 let bwdef: i64 = pq_bitwidth(maxdef)
1094 let bwrep: i64 = pq_bitwidth(maxrep)
1095 let buf: *u8 = pq[PQ_BUF] as *u8
1096 let flen: i64 = pq[PQ_LEN]
1097 let nvals: i64 = cc[CC_NVALS]
1098 var pos: i64 = cc[CC_DATAOFF]
1099 if cc[CC_DICTOFF] >= 0 && cc[CC_DICTOFF] < pos { pos = cc[CC_DICTOFF] }
1100 if pos < 0 { return PQ_E_TRUNC }
1101 let ph: *i64 = pr[PR_PH] as *i64
1102 let arena: *u8 = pr[PR_ARENA] as *u8
1103 let off: *i64 = pr[PR_OFF] as *i64
1104 let len: *i64 = pr[PR_LEN] as *i64
1105 let def: *i64 = pr[PR_DEF] as *i64
1106 let rep: *i64 = pr[PR_REP] as *i64
1107 let row: *i64 = pr[PR_ROW] as *i64
1108 var done: i64 = 0
1109 while done < nvals {
1110 let hend: i64 = pq_parse_pageheader(pq, pos, ph)
1111 if hend < 0 { return hend }
1112 let csz: i64 = ph[PH_CSZ]
1113 let usz: i64 = ph[PH_USZ]
1114 if csz < 0 || usz < 0 || hend + csz > flen { return PQ_E_TRUNC }
1115 let ptype: i64 = ph[PH_TYPE]
1116 if ptype < 0 || ptype > PQ_PAGE_DATA2 { pq[PQ_WHAT] = ptype; return PQ_E_UNSUPPORTED }
1117 pr[PR_PTYPES] = pr[PR_PTYPES] | (1 << ptype)
1118 pr[PR_PAGES] = pr[PR_PAGES] + 1
1119 let a0: i64 = pr[PR_AUSED]
1120 if ptype == PQ_PAGE_DICT {
1121 let got: i64 = pq_inflate_page(pq, cc[CC_CODEC], buf, hend, hend + csz, arena, a0, pr[PR_ACAP], usz)
1122 if got < 0 { return got }
1123 pr[PR_AUSED] = a0 + got
1124 if ph[PH_DICTENC] != PQ_ENC_PLAIN && ph[PH_DICTENC] != PQ_ENC_PLAIN_DICT { pq[PQ_WHAT] = ph[PH_DICTENC]; return PQ_E_UNSUPPORTED }
1125 let dn: i64 = ph[PH_DICTN]
1126 if dn < 0 { return PQ_E_THRIFT }
1127 if pr[PR_DICTN] > 0 { return PQ_E_LEVELS }
1128 pr[PR_DOFF] = sys_mmap((dn + 1) * PQ_I64) as i64
1129 pr[PR_DLEN] = sys_mmap((dn + 1) * PQ_I64) as i64
1130 let rc: i64 = pq_plain_bytes(arena, a0, a0 + got, dn, pr[PR_DOFF] as *i64, pr[PR_DLEN] as *i64)
1131 if rc < 0 { return rc }
1132 pr[PR_DICTN] = dn
1133 }
1134 if ptype == PQ_PAGE_DATA || ptype == PQ_PAGE_DATA2 {
1135 let nv: i64 = ph[PH_NV]
1136 if nv < 0 { return PQ_E_THRIFT }
1137 if pr[PR_N] + nv > pr[PR_CAP] { return PQ_E_CAPACITY }
1138 let base: i64 = pr[PR_N]
1139 var vstart: i64 = 0
1140 var vend: i64 = 0
1141 if ptype == PQ_PAGE_DATA {
1142 let got2: i64 = pq_inflate_page(pq, cc[CC_CODEC], buf, hend, hend + csz, arena, a0, pr[PR_ACAP], usz)
1143 if got2 < 0 { return got2 }
1144 pr[PR_AUSED] = a0 + got2
1145 var p: i64 = a0
1146 let pend: i64 = a0 + got2
1147 if maxrep > 0 {
1148 if ph[PH_REPENC] != PQ_ENC_RLE { pq[PQ_WHAT] = ph[PH_REPENC]; return PQ_E_UNSUPPORTED }
1149 if p + PQ_I32_LEN > pend { return PQ_E_TRUNC }
1150 let rl: i64 = pq_le32(arena, p)
1151 p = p + PQ_I32_LEN
1152 if rl < 0 || p + rl > pend { return PQ_E_TRUNC }
1153 let rc2: i64 = pq_hybrid(arena, p, p + rl, bwrep, nv, ((rep as i64) + base * PQ_I64) as *i64, nv)
1154 if rc2 < 0 { return rc2 }
1155 p = p + rl
1156 } else { pq_fill(rep, base, nv, 0) }
1157 if maxdef > 0 {
1158 if ph[PH_DEFENC] != PQ_ENC_RLE { pq[PQ_WHAT] = ph[PH_DEFENC]; return PQ_E_UNSUPPORTED }
1159 if p + PQ_I32_LEN > pend { return PQ_E_TRUNC }
1160 let dl: i64 = pq_le32(arena, p)
1161 p = p + PQ_I32_LEN
1162 if dl < 0 || p + dl > pend { return PQ_E_TRUNC }
1163 let rc3: i64 = pq_hybrid(arena, p, p + dl, bwdef, nv, ((def as i64) + base * PQ_I64) as *i64, nv)
1164 if rc3 < 0 { return rc3 }
1165 p = p + dl
1166 } else { pq_fill(def, base, nv, maxdef) }
1167 vstart = p
1168 vend = pend
1169 } else {
1170 let rl2: i64 = ph[PH_REPLEN]
1171 let dl2: i64 = ph[PH_DEFLEN]
1172 if rl2 < 0 || dl2 < 0 || rl2 + dl2 > csz { return PQ_E_TRUNC }
1173 var p2: i64 = hend
1174 if maxrep > 0 {
1175 let rc4: i64 = pq_hybrid(buf, p2, p2 + rl2, bwrep, nv, ((rep as i64) + base * PQ_I64) as *i64, nv)
1176 if rc4 < 0 { return rc4 }
1177 } else { pq_fill(rep, base, nv, 0) }
1178 p2 = p2 + rl2
1179 if maxdef > 0 {
1180 let rc5: i64 = pq_hybrid(buf, p2, p2 + dl2, bwdef, nv, ((def as i64) + base * PQ_I64) as *i64, nv)
1181 if rc5 < 0 { return rc5 }
1182 } else { pq_fill(def, base, nv, maxdef) }
1183 p2 = p2 + dl2
1184 let vcsz: i64 = csz - rl2 - dl2
1185 let vusz: i64 = usz - rl2 - dl2
1186 if vusz < 0 { return PQ_E_TRUNC }
1187 var codec: i64 = cc[CC_CODEC]
1188 if ph[PH_ISCOMP] == 0 { codec = PQ_CODEC_NONE }
1189 let got3: i64 = pq_inflate_page(pq, codec, buf, p2, p2 + vcsz, arena, a0, pr[PR_ACAP], vusz)
1190 if got3 < 0 { return got3 }
1191 pr[PR_AUSED] = a0 + got3
1192 vstart = a0
1193 vend = a0 + got3
1194 }
1195 var nn: i64 = 0
1196 var i: i64 = 0
1197 while i < nv { if def[base + i] == maxdef { nn = nn + 1 } i = i + 1 }
1198 if ptype == PQ_PAGE_DATA2 && ph[PH_NNULLS] >= 0 { if nv - ph[PH_NNULLS] != nn { return PQ_E_LEVELS } }
1199 let enc: i64 = ph[PH_ENC]
1200 if enc < 0 || enc >= PQ_MAX_BITWIDTH { pq[PQ_WHAT] = enc; return PQ_E_UNSUPPORTED }
1201 pr[PR_ENCMASK] = pr[PR_ENCMASK] | (1 << enc)
1202 if enc == PQ_ENC_PLAIN {
1203 var p3: i64 = vstart
1204 var i2: i64 = 0
1205 while i2 < nv {
1206 if def[base + i2] == maxdef {
1207 if p3 + PQ_I32_LEN > vend { return PQ_E_TRUNC }
1208 let l: i64 = pq_le32(arena, p3)
1209 p3 = p3 + PQ_I32_LEN
1210 if l < 0 || p3 + l > vend { return PQ_E_TRUNC }
1211 off[base + i2] = p3
1212 len[base + i2] = l
1213 p3 = p3 + l
1214 } else { off[base + i2] = PQ_NONE; len[base + i2] = 0 }
1215 i2 = i2 + 1
1216 }
1217 } else {
1218 if enc != PQ_ENC_RLE_DICT && enc != PQ_ENC_PLAIN_DICT { pq[PQ_WHAT] = enc; return PQ_E_UNSUPPORTED }
1219 if nn > 0 {
1220 if pr[PR_DICTN] <= 0 { return PQ_E_LEVELS }
1221 if vstart >= vend { return PQ_E_TRUNC }
1222 let bwi: i64 = (arena[vstart] & PQ_BYTE) as i64
1223 if bwi > PQ_MAX_BITWIDTH { return PQ_E_LEVELS }
1224 let rc6: i64 = pq_hybrid(arena, vstart + 1, vend, bwi, nn, ((off as i64) + base * PQ_I64) as *i64, nv)
1225 if rc6 < 0 { return rc6 }
1226 }
1227 let doff: *i64 = pr[PR_DOFF] as *i64
1228 let dlen: *i64 = pr[PR_DLEN] as *i64
1229 // distribute from the back so an index is read before its slot is overwritten
1230 var i3: i64 = nv - 1
1231 var vi: i64 = nn - 1
1232 while i3 >= 0 {
1233 if def[base + i3] == maxdef {
1234 let ix: i64 = off[base + vi]
1235 if ix < 0 || ix >= pr[PR_DICTN] { return PQ_E_LEVELS }
1236 off[base + i3] = doff[ix]
1237 len[base + i3] = dlen[ix]
1238 vi = vi - 1
1239 } else { off[base + i3] = PQ_NONE; len[base + i3] = 0 }
1240 i3 = i3 - 1
1241 }
1242 }
1243 var i4: i64 = 0
1244 while i4 < nv {
1245 if maxrep == 0 { row[base + i4] = pr[PR_ROWS]; pr[PR_ROWS] = pr[PR_ROWS] + 1 } else {
1246 if rep[base + i4] == 0 { pr[PR_ROWS] = pr[PR_ROWS] + 1 }
1247 row[base + i4] = pr[PR_ROWS] - 1
1248 }
1249 i4 = i4 + 1
1250 }
1251 pr[PR_N] = base + nv
1252 pr[PR_NONNULL] = pr[PR_NONNULL] + nn
1253 done = done + nv
1254 }
1255 pos = hend + csz
1256 }
1257 if done != nvals { return PQ_E_LEVELS }
1258 return pr[PR_N]
1259}
1260// the name of a refusal, for callers that print one
1261func pq_errname(rc: i64) -> *u8 {
1262 if rc == PQ_E_MAGIC { return "NOT-PARQUET" as *u8 }
1263 if rc == PQ_E_TRUNC { return "TRUNCATED" as *u8 }
1264 if rc == PQ_E_THRIFT { return "THRIFT-UNDECODABLE" as *u8 }
1265 if rc == PQ_E_UNSUPPORTED { return "UNSUPPORTED" as *u8 }
1266 if rc == PQ_E_SNAPPY { return "SNAPPY-CORRUPT" as *u8 }
1267 if rc == PQ_E_CAPACITY { return "CAPACITY" as *u8 }
1268 if rc == PQ_E_ORDER { return "FOOTER-ORDER" as *u8 }
1269 if rc == PQ_E_LEVELS { return "LEVELS-DISAGREE" as *u8 }
1270 if rc == PQ_E_COLUMNS { return "COLUMN-COUNT" as *u8 }
1271 if rc == PQ_E_ARG { return "OUT-OF-RANGE" as *u8 }
1272 if rc == PQ_E_OPEN { return "CANNOT-OPEN" as *u8 }
1273 return "OK" as *u8
1274}
1275
1276// ======================================================================================================
1277// tsv: two flat leaves as <a>TAB<b> rows
1278// ======================================================================================================
1279const PT_ROWS: i64 = 0
1280const PT_WRITTEN: i64 = 1
1281const PT_NULLROWS: i64 = 2
1282const PT_BYTES: i64 = 3
1283const PT_SLOTS: i64 = 4
1284const PQ_TMP_SUFFIX: *u8 = ".tmp"
1285const PQ_PATH_CAP: i64 = 1024
1286// write leaves la and lb as <a>TAB<b> rows to out (staged at out.tmp, renamed into place), one row group in memory at
1287// a time; tabs, newlines and carriage returns inside a cell become spaces; a row with a null side is COUNTED in
1288// stats[PT_NULLROWS] and skipped, never written as an empty cell. Returns 0 or a refusal (PQ_E_ARG for a list leaf,
1289// with pq[PQ_WHAT] naming it; PQ_E_OPEN when the staging file cannot be created or renamed).
1290func pq_tsv(pq: *i64, la: i64, lb: i64, out: *u8, stats: *i64) -> i64 {
1291 var s0: i64 = 0
1292 while s0 < PT_SLOTS { stats[s0] = 0; s0 = s0 + 1 }
1293 if la < 0 || la >= pq[PQ_NLEAF] { pq[PQ_WHAT] = la; return PQ_E_ARG }
1294 if lb < 0 || lb >= pq[PQ_NLEAF] { pq[PQ_WHAT] = lb; return PQ_E_ARG }
1295 let sa: *i64 = pq_se(pq, pq_leaf_index(pq, la))
1296 let sb: *i64 = pq_se(pq, pq_leaf_index(pq, lb))
1297 if sa[SE_MAXREP] != 0 { pq[PQ_WHAT] = la; return PQ_E_ARG }
1298 if sb[SE_MAXREP] != 0 { pq[PQ_WHAT] = lb; return PQ_E_ARG }
1299 let tmp: *u8 = sys_mmap(PQ_PATH_CAP)
1300 var ol: i64 = 0
1301 while (out[ol] & PQ_BYTE) as i64 != 0 { ol = ol + 1 }
1302 var sl: i64 = 0
1303 while (PQ_TMP_SUFFIX[sl] & PQ_BYTE) as i64 != 0 { sl = sl + 1 }
1304 if ol + sl + 1 > PQ_PATH_CAP { return PQ_E_CAPACITY }
1305 var k: i64 = 0
1306 while k < ol { tmp[k] = out[k]; k = k + 1 }
1307 var k2: i64 = 0
1308 while k2 < sl { tmp[ol + k2] = PQ_TMP_SUFFIX[k2]; k2 = k2 + 1 }
1309 tmp[ol + sl] = 0 as u8
1310 let fd: i64 = sys_openat_wr(tmp, MODE_0644)
1311 if fd < 0 { return PQ_E_OPEN }
1312 let wo: *i64 = wb_new(fd)
1313 var r: i64 = 0
1314 var rc_worst: i64 = 0
1315 while r < pq[PQ_NRG] && rc_worst == 0 {
1316 let pa: *i64 = pr_alloc(pq, r, la)
1317 let pb: *i64 = pr_alloc(pq, r, lb)
1318 let ra: i64 = pq_read_column(pq, r, la, pa)
1319 let rb: i64 = pq_read_column(pq, r, lb, pb)
1320 if ra < 0 { rc_worst = ra }
1321 if rb < 0 { rc_worst = rb }
1322 if rc_worst == 0 && pa[PR_N] != pb[PR_N] { rc_worst = PQ_E_LEVELS }
1323 if rc_worst == 0 {
1324 let offa: *i64 = pa[PR_OFF] as *i64
1325 let lena: *i64 = pa[PR_LEN] as *i64
1326 let offb: *i64 = pb[PR_OFF] as *i64
1327 let lenb: *i64 = pb[PR_LEN] as *i64
1328 var i: i64 = 0
1329 while i < pa[PR_N] {
1330 stats[PT_ROWS] = stats[PT_ROWS] + 1
1331 if offa[i] < 0 || offb[i] < 0 { stats[PT_NULLROWS] = stats[PT_NULLROWS] + 1 } else {
1332 wb_cell(wo, pa[PR_ARENA] as *u8, offa[i], lena[i])
1333 wb_byte(wo, PQ_TAB)
1334 wb_cell(wo, pb[PR_ARENA] as *u8, offb[i], lenb[i])
1335 wb_byte(wo, PQ_NL)
1336 stats[PT_WRITTEN] = stats[PT_WRITTEN] + 1
1337 }
1338 i = i + 1
1339 }
1340 }
1341 pr_free(pa)
1342 pr_free(pb)
1343 r = r + 1
1344 }
1345 wb_flush(wo)
1346 sys_close(fd)
1347 stats[PT_BYTES] = wo[WB_TOTAL]
1348 if rc_worst < 0 { return rc_worst }
1349 if wo[WB_ERR] != 0 { return PQ_E_CAPACITY }
1350 if sys_renameat(tmp, out) != 0 { return PQ_E_OPEN }
1351 return 0
1352}