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