code wiki / (root) / nx_parquet_lib.nx

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}