decoder.ha (19166B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use bytes; 5 use io; 6 use math::{bit_size}; 7 use os; 8 use strings; 9 use time::chrono; 10 use time::date; 11 use types; 12 13 14 def TAGMASK: u8 = 0x1f; 15 def MAX_CONS_DEPTH: size = 32; 16 17 // Each DER entry starts with an header that describes the content. 18 export type head = struct { 19 20 // Tells whether the data is constructed and encapsulates multiple 21 // other data fields; or primitive and the value follows. 22 cons: bool, 23 24 // Class info 25 class: class, 26 27 // Tag id of the data 28 tagid: u32, 29 30 // Start position in stream 31 start: size, 32 33 // Start position of data in stream 34 data: size, 35 36 // End position in stream 37 end: size, 38 39 implicit: bool, 40 }; 41 42 fn head_endpos(d: head) size = d.end; 43 44 // Size of current element (header size + data size) 45 export fn sz(d: head) size = d.end - d.start; 46 47 // Size of the encoded data. 48 export fn dsz(d: head) size = d.end - d.data; 49 50 export type decoder = struct { 51 src: io::handle, 52 pos: size, 53 cstack: [MAX_CONS_DEPTH]head, 54 cstackp: size, 55 next: (void | head), 56 cur: (void | head), 57 unbuf: [3]u8, 58 unbufn: u8, 59 implicit: bool, 60 }; 61 62 // Creates a new DER decoder that reads from 'src'. A buffered stream (see 63 // [[bufio::]]) is recommended for efficiency, as the decoder performs mostly 64 // short reads. 65 // 66 // Each entry must be read in its entirety before the next one is attended to. 67 // The user must call [[finish]] when finished with the decoder to ensure that 68 // the entire input was read correctly. 69 export fn derdecoder(src: io::handle) decoder = { 70 return decoder { 71 src = src, 72 pos = 0, 73 cstackp = 0, 74 cur = void, 75 next = void, 76 implicit = false, 77 ... 78 }; 79 }; 80 81 // Verifies that the entire input to the decoder was read. 82 export fn finish(d: *decoder) (void | error) = { 83 if (d.cstackp != 0 || d.next is head) return invalid; 84 match (d.cur) { 85 case void => 86 return; 87 case let h: head => 88 if (h.end != d.pos) return invalid; 89 }; 90 }; 91 92 // Returns last opened cons or void if none is open. 93 fn curcons(d: *decoder) (void | head) = { 94 if (d.cstackp == 0) { 95 return; 96 }; 97 return d.cstack[d.cstackp-1]; 98 }; 99 100 // Peeks the header of the next data field. Fails with [[badformat]] if no data 101 // follows. 102 export fn peek(d: *decoder) (head | error) = { 103 match (trypeek(d)?) { 104 case io::EOF => 105 return badformat; 106 case let h: head => 107 return h; 108 }; 109 }; 110 111 // Tries to peek the header of the next data field, or returns EOF if none 112 // exists. 113 export fn trypeek(d: *decoder) (head | error | io::EOF) = { 114 if (!(d.next is void)) { 115 return d.next: head; 116 }; 117 118 if (is_endofcons(d)) return io::EOF; 119 120 match (parse_header(d)?) { 121 case io::EOF => 122 const unreaddata = d.unbufn > 0; 123 if (d.cstackp != 0 || unreaddata) { 124 return badformat; 125 }; 126 return io::EOF; 127 case let dh: head => 128 d.next = dh; 129 return dh; 130 }; 131 }; 132 133 // Cons is open and end is reached. 134 fn is_endofcons(d: *decoder) bool = { 135 match (curcons(d)) { 136 case void => 137 return false; 138 case let cur: head => 139 return d.pos == head_endpos(cur); 140 }; 141 }; 142 143 // Returns the next data element, or [[badformat]] on EOF. 144 fn next(d: *decoder) (head | error) = { 145 match (trynext(d)?) { 146 case io::EOF => 147 return badformat; 148 case let dh: head => 149 return dh; 150 }; 151 }; 152 153 fn trynext(d: *decoder) (head | error | io::EOF) = { 154 if (d.next is head) { 155 let dh = d.next: head; 156 d.cur = dh; 157 d.next = void; 158 dh.implicit = d.implicit; 159 d.implicit = false; 160 return dh; 161 }; 162 163 if (is_endofcons(d)) return io::EOF; 164 165 let dh = match (parse_header(d)?) { 166 case io::EOF => 167 return io::EOF; 168 case let dh: head => 169 yield dh; 170 }; 171 172 d.cur = dh; 173 dh.implicit = d.implicit; 174 d.implicit = false; 175 return dh; 176 }; 177 178 fn parse_header(d: *decoder) (head | error | io::EOF) = { 179 const consend = match (curcons(d)) { 180 case void => 181 yield types::SIZE_MAX; 182 case let h: head => 183 yield h.end; 184 }; 185 186 if (d.pos == consend) return invalid; 187 188 const epos = d.pos; 189 const id = match (tryscan_byte(d)?) { 190 case io::EOF => 191 d.cur = void; 192 return io::EOF; 193 case let id: u8 => 194 yield id; 195 }; 196 197 const class = ((id & 0xc0) >> 6): class; 198 199 let tagid: u32 = id & TAGMASK; 200 if (tagid == TAGMASK) { 201 tagid = parse_longtag(d, consend - d.pos)?; 202 }; 203 const l = parse_len(d, consend - d.pos)?; 204 const hl = d.pos - epos; 205 206 const end = epos + hl + l; 207 if (end > consend) return invalid; 208 209 return head { 210 class = class, 211 cons = ((id >> 5) & 1) == 1, 212 tagid = tagid, 213 start = epos, 214 data = epos + hl, 215 end = end, 216 implicit = d.implicit, 217 ... 218 }; 219 }; 220 221 fn tryscan_byte(d: *decoder) (u8 | io::EOF | error) = { 222 let buf: [1]u8 = [0...]; 223 match (io::readall(d.src, buf)?) { 224 case io::EOF => 225 return io::EOF; 226 case size => 227 d.pos += 1; 228 return buf[0]; 229 }; 230 }; 231 232 fn scan_byte(d: *decoder) (u8 | error) = { 233 match (tryscan_byte(d)?) { 234 case io::EOF => 235 return truncated; 236 case let b: u8 => 237 return b; 238 }; 239 }; 240 241 // Reads data of current entry and advances pointer. Data must have been opened 242 // using [[next]] or [[trynext]]. [[io::EOF]] is returned on end of data. 243 fn dataread(d: *decoder, buf: []u8) (size | io::EOF | io::error) = { 244 let cur = match (d.cur) { 245 case void => 246 abort("primitive must be opened with [[next]] or [[trynext]]"); 247 case let dh: head => 248 yield dh; 249 }; 250 251 const dataleft = head_endpos(cur) - d.pos + d.unbufn; 252 if (dataleft == 0) { 253 return io::EOF; 254 }; 255 256 let n = 0z; 257 if (d.unbufn > 0) { 258 const max = if (d.unbufn > len(buf)) len(buf): u8 else d.unbufn; 259 buf[..max] = d.unbuf[..max]; 260 d.unbufn -= max; 261 n += max; 262 }; 263 264 const max = if (dataleft < len(buf) - n) dataleft else len(buf) - n; 265 266 match (io::read(d.src, buf[n..n + max])?) { 267 case io::EOF => 268 // there should be data left 269 return wrap_err(truncated); 270 case let sz: size => 271 d.pos += sz; 272 return n + sz; 273 }; 274 }; 275 276 // unread incomplete utf8 runes. 277 fn dataunread(d: *decoder, buf: []u8) void = { 278 assert(len(buf) + d.unbufn <= len(d.unbuf)); 279 280 d.unbuf[d.unbufn..d.unbufn + len(buf)] = buf; 281 d.unbufn += len(buf): u8; 282 }; 283 284 fn dataeof(d: *decoder) bool = { 285 match (d.cur) { 286 case void => 287 return true; 288 case let h: head => 289 return d.pos + d.unbufn == head_endpos(h); 290 }; 291 }; 292 293 fn parse_longtag(p: *decoder, max: size) (u32 | error) = { 294 // XXX: u32 too much? 295 let tag: u32 = 0; 296 let maxbits = size(u32) * 8; 297 let nbits = 0z; 298 299 for (let i = 0z; i < max; i += 1) { 300 let b = scan_byte(p)?; 301 const part = b & 0x7f; 302 303 nbits += if (tag == 0) bit_size(part) else 7; 304 if (nbits > maxbits) { 305 // overflows u32 306 return invalid; 307 }; 308 309 tag = (tag << 7) + part; 310 if (tag == 0) { 311 // first tag part must not be 0 312 return invalid; 313 }; 314 315 if ((b >> 7) == 0) { 316 return tag; 317 }; 318 }; 319 return invalid; // max has been reached 320 }; 321 322 fn parse_len(p: *decoder, max: size) (size | error) = { 323 if (max == 0) return invalid; 324 325 const b = scan_byte(p)?; 326 if (b == 0xff) { 327 return invalid; 328 }; 329 if (b >> 7 == 0) { 330 // short form 331 return b: size; 332 }; 333 334 let l = 0z; 335 const n = b & 0x7f; 336 if (n == 0) { 337 // Indefinite encoding is not supported in DER. 338 return invalid; 339 }; 340 341 if (n > size(size)) { 342 // would cause a size overflow 343 return invalid; 344 }; 345 346 if (n + 1 > max) return invalid; 347 348 for (let i = 0z; i < n; i += 1) { 349 const b = scan_byte(p)?; 350 l = (l << 8) + b; 351 if (l == 0) { 352 // Leading zeroes means minimum number of bytes for 353 // length encoding has not been used. 354 return invalid; 355 }; 356 }; 357 358 if (l <= 0x7f) { 359 // Could've used short form. 360 return invalid; 361 }; 362 363 return l; 364 }; 365 366 // Expects an IMPLICIT defined data field having class 'c' and tag 'tag'. 367 // If the requirements are met, a read call (i.e. one of the "read_" or "reader" 368 // functions) must follow to read the actual data as stored. 369 export fn expect_implicit(d: *decoder, c: class, tag: u32) (void | error) = { 370 let h = peek(d)?; 371 expect_tag(h, c, tag)?; 372 d.implicit = true; 373 }; 374 375 // Opens an EXPLICIT encoded field of given class 'c' and 'tag'. The user must 376 // call [[close_explicit]] after containing data has been read. 377 export fn open_explicit(d: *decoder, c: class, tag: u32) (void | error) = 378 open_cons(d, c, tag); 379 380 // Closes an EXPLICIT encoded field. 381 export fn close_explicit(d: *decoder) (void | badformat) = close_cons(d); 382 383 384 // Opens a constructed value of given 'class' and 'tagid'. Fails if not a 385 // constructed value or the encoded value has an unexpected tag. 386 fn open_cons(d: *decoder, class: class, tagid: u32) (void | error) = { 387 let dh = next(d)?; 388 if (!dh.cons) { 389 return invalid; 390 }; 391 392 expect_tag(dh, class, tagid)?; 393 394 if (d.cstackp == len(d.cstack)) { 395 return badformat; 396 }; 397 398 d.cstack[d.cstackp] = dh; 399 d.cstackp += 1; 400 }; 401 402 // Closes current constructed value. badformat is returend, if not all data has 403 // been read. 404 fn close_cons(d: *decoder) (void | badformat) = { 405 if (d.implicit) { 406 // a datafield marked implicit has not been read 407 return badformat; 408 }; 409 410 match (curcons(d)) { 411 case void => 412 abort("No constructed value open"); 413 case let h: head => 414 if (d.pos != head_endpos(h) || d.unbufn > 0) { 415 // All data must have been read before closing the seq 416 return badformat; 417 }; 418 }; 419 420 d.cstackp -= 1; 421 }; 422 423 // Opens a sequence. Call [[close_seq]] after reading. 424 export fn open_seq(d: *decoder) (void | error) = 425 open_cons(d, class::UNIVERSAL, utag::SEQUENCE: u32)?; 426 427 // Closes the current sequence. If the caller has not read all of the data 428 // present in the encoded seqeunce, [[badformat]] is returned. 429 export fn close_seq(d: *decoder) (void | badformat) = close_cons(d); 430 431 // Opens a set. Note that sets must be ordered according to DER, but this module 432 // does not validate this constraint. Call [[close_set]] after reading. 433 export fn open_set(d: *decoder) (void | error) = 434 open_cons(d, class::UNIVERSAL, utag::SET: u32)?; 435 436 // Closes the current set. If the caller has not read all of the data present in 437 // the encoded seqeunce, [[badformat]] is returned. 438 export fn close_set(d: *decoder) (void | badformat) = close_cons(d); 439 440 fn expect_tag(h: head, class: class, tagid: u32) (void | invalid | badformat) = { 441 if (class == class::UNIVERSAL && (tagid == utag::SEQUENCE 442 || tagid == utag::SET) && !h.cons) { 443 return invalid; 444 }; 445 446 if (h.implicit) { 447 return; 448 }; 449 450 if (h.class != class || h.tagid != tagid) { 451 return badformat; 452 }; 453 }; 454 455 fn expect_utag(dh: head, tag: utag) (void | invalid | badformat) = 456 expect_tag(dh, class::UNIVERSAL, tag: u32); 457 458 fn read_bytes(d: *decoder, buf: []u8) (size | error) = { 459 match (dataread(d, buf)) { 460 case io::EOF => 461 return 0z; 462 case let n: size => 463 if (!dataeof(d)) { 464 return badformat; 465 }; 466 return n; 467 }; 468 }; 469 470 fn read_nbytes(d: *decoder, buf: []u8) (size | error) = { 471 const n = read_bytes(d, buf)?; 472 if (n != len(buf)) { 473 return badformat; 474 }; 475 return n; 476 }; 477 478 // Reads a boolean value. 479 export fn read_bool(d: *decoder) (bool | error) = { 480 let dh = next(d)?; 481 expect_utag(dh, utag::BOOLEAN)?; 482 if (dsz(dh) != 1) { 483 return invalid; 484 }; 485 486 let b = scan_byte(d)?; 487 488 if (b != 0x00 && b != 0xff) { 489 return invalid; 490 }; 491 492 return b == 0xff; 493 }; 494 495 fn validate_intprefix(i: []u8) (void | error) = { 496 switch (len(i)) { 497 case 0 => 498 return invalid; 499 case 1 => 500 return; 501 case => 502 // An int must be encoded using the minimal number of bytes 503 // possible as defined in X.690 s8.3.2 504 if ((i[0] == 0x00 && i[1] >> 7 == 0) 505 || (i[0] == 0xff && i[1] >> 7 == 1)) { 506 return invalid; 507 }; 508 }; 509 }; 510 511 // Reads an arbitrary-length integer into 'buf' and returns its length in bytes. 512 // Fails if the encoded integer size exceeds the buffer size. The integer is 513 // stored in big endian, and negative values are stored with two's compliment. 514 // The minimum integer size is one byte. 515 export fn read_int(d: *decoder, buf: []u8) (size | error) = { 516 assert(len(buf) > 0); 517 518 let dh = next(d)?; 519 expect_utag(dh, utag::INTEGER)?; 520 const n = read_bytes(d, buf)?; 521 validate_intprefix(buf[..n])?; 522 return n; 523 }; 524 525 // Similar to [[read_int]], but returns [[badformat]] if the encoded value is 526 // signed. Discards the most significant zero bytes. 527 export fn read_uint(d: *decoder, buf: []u8) (size | error) = { 528 let s = read_int(d, buf)?; 529 if (buf[0] & 0x80 == 0x80) { 530 return badformat; 531 }; 532 if (buf[0] == 0) { 533 buf[..s-1] = buf[1..s]; 534 s -= 1; 535 }; 536 return s; 537 }; 538 539 fn read_ux(d: *decoder, x: u8) (u64 | error) = { 540 assert(x <= 8); 541 let b: [9]u8 = [0...]; 542 const n = read_int(d, b[..x+1])?; 543 544 if (b[0] & 0x80 != 0) { 545 // sign bit is set 546 return invalid; 547 }; 548 549 const s = if (b[0] == 0x00) 1u8 else 0u8; 550 if (n - s > x) { 551 return invalid; 552 }; 553 554 let r = 0u64; 555 for (let i = s; i < n; i += 1) { 556 r <<= 8; 557 r += b[i]; 558 }; 559 return r; 560 }; 561 562 // Reads an integer that is expected to fit into u8. 563 export fn read_u8(d: *decoder) (u8 | error) = read_ux(d, 1)?: u8; 564 565 // Reads an integer that is expected to fit into u16. 566 export fn read_u16(d: *decoder) (u16 | error) = read_ux(d, 2)?: u16; 567 568 // Reads an integer that is expected to fit into u32. 569 export fn read_u32(d: *decoder) (u32 | error) = read_ux(d, 4)?: u32; 570 571 // Reads an integer that is expected to fit into u64. 572 export fn read_u64(d: *decoder) (u64 | error) = read_ux(d, 8)?; 573 574 // Reads a bitstring value. The result tuple contains the bitstring and the 575 // number of unused bits in the last byte. The [[bitstr_isset]] function may be 576 // used to check for set bits. 577 export fn read_bitstr(d: *decoder, buf: []u8) (([]u8, u8) | error) = { 578 let dh = next(d)?; 579 expect_utag(dh, utag::BITSTRING)?; 580 581 let unused: [1]u8 = [0...]; 582 match (dataread(d, unused)?) { 583 case io::EOF => 584 return invalid; 585 case let n: size => 586 if (n != 1) { 587 return invalid; 588 }; 589 }; 590 const unused = unused[0]; 591 if (unused > 7) { 592 return invalid; 593 }; 594 595 const n = read_bytes(d, buf)?; 596 const mask = (1 << unused) - 1; 597 if (n > 0 && buf[n-1] & mask != 0) { 598 // unused bits must be zero 599 return invalid; 600 }; 601 return (buf[..n], unused); 602 }; 603 604 // Checks whether bit at 'pos' is set in given bitstring. 'pos' starts from 0, 605 // which is the highest order bit in the first byte. 606 export fn bitstr_isset(bitstr: ([]u8, u8), pos: size) (bool | invalid) = { 607 const i = pos / 8; 608 if (i >= len(bitstr.0)) { 609 return false; 610 }; 611 let b = bitstr.0[i]; 612 613 const j = pos - i * 8; 614 if (i == len(bitstr.0) - 1 && j >= (8 - bitstr.1)) { 615 return invalid; 616 }; 617 const mask = (1 << (7 - j)); 618 return mask & b == mask; 619 }; 620 621 // Returns an [[io::reader]] for octet string data. 622 export fn octetstrreader(d: *decoder) (bytestream | error) = { 623 // TODO add limit? 624 let dh = next(d)?; 625 expect_utag(dh, utag::OCTET_STRING)?; 626 return newbytereader(d); 627 }; 628 629 // Read an octet string into 'buf', returning its length. Returns [[badformat]] 630 // if 'buf' is too small. 631 export fn read_octetstr(d: *decoder, buf: []u8) (size | error) = { 632 assert(len(buf) > 0); 633 634 let dh = next(d)?; 635 expect_utag(dh, utag::OCTET_STRING)?; 636 return read_bytes(d, buf); 637 }; 638 639 // Reads a null entry. 640 export fn read_null(d: *decoder) (void | error) = { 641 let dh = next(d)?; 642 expect_utag(dh, utag::NULL)?; 643 if (dsz(dh) != 0) { 644 return invalid; 645 }; 646 }; 647 648 export type bytestream = struct { 649 stream: io::stream, 650 d: *decoder, 651 }; 652 653 fn newbytereader(d: *decoder) bytestream = { 654 return bytestream { 655 stream = &bytestream_vtable, 656 d = d, 657 ... 658 }; 659 }; 660 661 const bytestream_vtable: io::vtable = io::vtable { 662 reader = &bytestream_reader, 663 ... 664 }; 665 666 fn bytestream_reader(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = 667 dataread((s: *bytestream).d, buf); 668 669 // Returns an [[io::reader]] that reads raw data (in its ASN.1 encoded form) 670 // from a [[decoder]]. Note that this reader will not perform any kind of 671 // validation. 672 export fn bytereader(d: *decoder, c: class, tagid: u32) (bytestream | error) = { 673 let dh = next(d)?; 674 expect_tag(dh, c, tagid)?; 675 return newbytereader(d); 676 }; 677 678 // Reads an UTC time. Since the stored date only has a two digit year, 'maxyear' 679 // is required to define the epoch. For example 'maxyear' = 2046 causes all 680 // encoded years <= 46 to be after 2000 and all values > 46 will have 1900 as 681 // the century. 682 export fn read_utctime(d: *decoder, maxyear: u16) (date::date | error) = { 683 assert(maxyear > 100); 684 685 let dh = next(d)?; 686 expect_utag(dh, utag::UTC_TIME)?; 687 688 let time: [13]u8 = [0...]; 689 read_nbytes(d, time[..])?; 690 691 if (time[len(time)-1] != 'Z') { 692 return invalid; 693 }; 694 695 let year: u16 = (time[0] - 0x30): u16 * 10 + (time[1] - 0x30): u16; 696 let cent = maxyear - (maxyear % 100); 697 if (year > maxyear % 100) { 698 cent -= 100; 699 }; 700 701 let v = date::newvirtual(); 702 v.vloc = chrono::UTC; 703 v.year = (year + cent): int; 704 v.zoff = 0; 705 v.nanosecond = 0; 706 707 let datestr = strings::fromutf8(time[2..])!; 708 if (!(date::parse(&v, "%m%d%H%M%S%Z", datestr) is void)) { 709 return invalid; 710 }; 711 712 let dt = match (date::realize(v)) { 713 case let dt: date::date => 714 yield dt; 715 case let e: (date::insufficient | date::invalid) => 716 return invalid; 717 }; 718 719 return dt; 720 }; 721 722 // Reads a generalized datetime value. 723 export fn read_gtime(d: *decoder) (date::date | error) = { 724 let dh = next(d)?; 725 expect_utag(dh, utag::GENERALIZED_TIME)?; 726 727 // The date begins with the encoded datetime 728 def DATESZ = 14z; 729 // followed by optional fractional seconds separated by '.' 730 def NANOSZ = 10z; 731 def NANOSEPPOS = 14; 732 // and ends with the zone info 'Z' 733 def ZONESZ = 1z; 734 735 let time: [DATESZ + NANOSZ + ZONESZ]u8 = [0...]; 736 let n = read_bytes(d, time[..])?; 737 738 // zone info and seconds must always be present 739 if (time[n-1] != 'Z' || n < DATESZ + ZONESZ) { 740 return invalid; 741 }; 742 743 // validate fractional seconds 744 if (n > DATESZ + ZONESZ) { 745 // fractional seconds must not be empty 746 if (time[NANOSEPPOS] != '.' || n == DATESZ + ZONESZ + 1) { 747 return invalid; 748 }; 749 // fractional seconds must not end with 0 and must be > 0 750 if (time[n-2] == '0') return invalid; 751 }; 752 753 // right pad fractional seconds to make them valid nanoseconds 754 time[n-1..] = ['0'...]; 755 time[NANOSEPPOS] = '.'; 756 757 match (date::from_str("%Y%m%d%H%M%S.%N", strings::fromutf8(time)!)) { 758 case let d: date::date => 759 return d; 760 case => 761 return invalid; 762 }; 763 }; 764 765 // Skips an element and returns the size of the data that has been skipped. 766 // Returns an error if the skipped data is invalid. 767 // 768 // Presently only supports BOOLEAN, INTEGER, NULL, OCTET_STRING, and BITSTRING 769 // utags, and will abort when attempting to skip anything else. 770 export fn skip(d: *decoder, tag: utag, max: size) (size | error) = { 771 static let buf: [os::BUFSZ]u8 = [0...]; 772 let s = 0z; 773 switch (tag) { 774 case utag::BOOLEAN => 775 read_bool(d)?; 776 return 1z; 777 case utag::INTEGER => 778 let br = bytereader(d, class::UNIVERSAL, utag::INTEGER)?; 779 let n = match (io::read(&br, buf)?) { 780 case let n: size => 781 yield n; 782 case io::EOF => 783 return invalid; 784 }; 785 validate_intprefix(buf[..n])?; 786 n += streamskip(&br, max, buf)?; 787 return n; 788 case utag::NULL => 789 read_null(d)?; 790 return 0z; 791 case utag::OCTET_STRING => 792 let r = octetstrreader(d)?; 793 return streamskip(&r, max, buf)?; 794 case utag::BITSTRING => 795 assert(max <= len(buf)); 796 let buf = buf[..max]; 797 let p = read_bitstr(d, buf)?; 798 bytes::zero(p.0); 799 return len(p.0) + 1; 800 case => 801 abort("skip for given utag not implemented"); 802 }; 803 }; 804 805 fn streamskip(r: io::handle, max: size, buf: []u8) (size | error) = { 806 defer bytes::zero(buf); 807 let buf = if (max < len(buf)) buf[..max] else buf[..]; 808 let s = 0z; 809 for (true) { 810 match (io::read(r, buf)?) { 811 case let n: size => 812 s += n; 813 case io::EOF => 814 return s; 815 }; 816 817 if (s > max) { 818 return badformat; 819 }; 820 }; 821 };