| // Copyright 2020 The Wuffs Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| pub status "#bad input" | |
| pub status "#unsupported recursion depth" | |
| pri status "#internal error: inconsistent I/O" | |
| pri status "#internal error: inconsistent token length" | |
| // -------- | |
| // DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE is the largest workbuf length that a | |
| // decoder will request. | |
| pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0 | |
| // DECODER_DEPTH_MAX_INCL is the maximum supported recursion depth: how deeply | |
| // nested [] arrays and {} maps can be. | |
| // | |
| // The CBOR spec itself does not define a limit, but allows implementations to | |
| // set their own limits. | |
| pub const DECODER_DEPTH_MAX_INCL : base.u64 = 1024 | |
| // DECODER_DST_TOKEN_BUFFER_LENGTH_MIN_INCL is the minimum length of the dst | |
| // wuffs_base__token_buffer passed to the decoder. | |
| pub const DECODER_DST_TOKEN_BUFFER_LENGTH_MIN_INCL : base.u64 = 2 | |
| // DECODER_SRC_IO_BUFFER_LENGTH_MIN_INCL is the minimum length of the src | |
| // wuffs_base__io_buffer passed to the decoder. | |
| pub const DECODER_SRC_IO_BUFFER_LENGTH_MIN_INCL : base.u64 = 9 | |
| // -------- | |
| // TOKEN_VALUE_MAJOR is the base-38 encoding of "cbor". | |
| pub const TOKEN_VALUE_MAJOR : base.u32 = 0x0C_061D | |
| // TOKEN_VALUE_MINOR__DETAIL_MASK is a mask for the low 18 bits of a token's | |
| // value_minor. 18 is 64 - base.TOKEN__VALUE_EXTENSION__NUM_BITS. | |
| pub const TOKEN_VALUE_MINOR__DETAIL_MASK : base.u64 = 0x003_FFFF | |
| // TOKEN_VALUE_MINOR__MINUS_1_MINUS_X means that the 9-byte length token holds | |
| // the negative integer (-1 - x), where x is the big-endian unsigned integer in | |
| // the token's final 8 bytes. The most significant bit of x is guaranteed to be | |
| // set, so that (-1 - x) will always underflow an int64_t and its absolute | |
| // value (+1 + x) might also overflow a uint64_t. | |
| pub const TOKEN_VALUE_MINOR__MINUS_1_MINUS_X : base.u32 = 0x100_0000 | |
| // TOKEN_VALUE_MINOR__SIMPLE_VALUE means that the low 8 bits (or, equivalently, | |
| // the low 18 bits) of the token's value_minor is a CBOR simple value either in | |
| // the range 0 ..= 19 or in the range 24 ..= 255. | |
| // | |
| // Simple values in the range 20 ..= 23 (which correspond to: false, true, null | |
| // and undefined) are represented by WUFFS_BASE__TOKEN__VBC__LITERAL tokens. | |
| pub const TOKEN_VALUE_MINOR__SIMPLE_VALUE : base.u32 = 0x080_0000 | |
| // TOKEN_VALUE_MINOR__TAG means that the low 18 bits of the token's value_minor | |
| // is a CBOR tag. That token may be continued, in which case the following | |
| // token is an extended token whose value_extension holds a further | |
| // base.TOKEN__VALUE_EXTENSION__NUM_BITS bits. The 64-bit CBOR tag is either v | |
| // or ((v << base.TOKEN__VALUE_EXTENSION__NUM_BITS) | value_extension_1) where | |
| // v is (value_minor_0 & TOKEN_VALUE_MINOR__DETAIL_MASK). | |
| // | |
| // When a token chain contains extended tokens like this, all but the last | |
| // token has zero length. | |
| pub const TOKEN_VALUE_MINOR__TAG : base.u32 = 0x040_0000 | |
| // -------- | |
| pri const LITERALS : roarray[4] base.u32[..= 0x1FF_FFFF] = [ | |
| (base.TOKEN__VBC__LITERAL << 21) | base.TOKEN__VBD__LITERAL__FALSE, | |
| (base.TOKEN__VBC__LITERAL << 21) | base.TOKEN__VBD__LITERAL__TRUE, | |
| (base.TOKEN__VBC__LITERAL << 21) | base.TOKEN__VBD__LITERAL__NULL, | |
| (base.TOKEN__VBC__LITERAL << 21) | base.TOKEN__VBD__LITERAL__UNDEFINED, | |
| ] | |
| pri const TOKEN_LENGTHS : roarray[32] base.u8[..= 9] = [ | |
| 1, 1, 1, 1, 1, 1, 1, 1, | |
| 1, 1, 1, 1, 1, 1, 1, 1, | |
| 1, 1, 1, 1, 1, 1, 1, 1, | |
| 2, 3, 5, 9, 0, 0, 0, 1, | |
| ] | |
| pub struct decoder? implements base.token_decoder( | |
| end_of_data : base.bool, | |
| util : base.utility, | |
| ) + ( | |
| // stack is conceptually an array of 2-bit integers, implemented as an | |
| // array of u32. The N'th 2-bit pair is whether we're in an array or | |
| // object, where N is the recursion depth: | |
| // - 0b00 In an array. | |
| // - 0b01 In an object, expecting a value. | |
| // - 0b10 In an array. | |
| // - 0b11 In an object, expecting a key. | |
| // | |
| // The maximum recursion depth, 1024, is the same as for std/json. | |
| stack : array[1024 / 16] base.u32, | |
| // container_num_remaining[i] is the number of elements remaining in the | |
| // open containers, for i ranging in 0 .. depth. If (i < depth) and | |
| // (container_num_remaining[i] == 0) then the i'th open container has | |
| // indefinite length. | |
| container_num_remaining : array[1024] base.u64, | |
| ) | |
| pub func decoder.set_quirk!(key: base.u32, value: base.u64) base.status { | |
| return base."#unsupported option" | |
| } | |
| pub func decoder.workbuf_len() base.range_ii_u64 { | |
| return this.util.empty_range_ii_u64() | |
| } | |
| pub func decoder.decode_tokens?(dst: base.token_writer, src: base.io_reader, workbuf: slice base.u8) { | |
| var string_length : base.u64 | |
| var n64 : base.u64 | |
| var depth : base.u32[..= 1024] | |
| var stack_byte : base.u32[..= (1024 / 16) - 1] | |
| var stack_bit : base.u32[..= 30] | |
| var stack_val : base.u32[..= 3] | |
| var token_length : base.u32[..= 0xFFFF] | |
| var vminor : base.u32[..= 0x1FF_FFFF] | |
| var vminor_alt : base.u32[..= 0x1FF_FFFF] | |
| var continued : base.u32[..= 1] | |
| var c : base.u8 | |
| var c_major : base.u8[..= 0x07] | |
| var c_minor : base.u8[..= 0x1F] | |
| var tagged : base.bool | |
| // indefinite_string_major_type is 2 or 3 when we are in an | |
| // indefinite-length byte string or text string. It is 0 otherwise. | |
| var indefinite_string_major_type : base.u8[..= 3] | |
| if this.end_of_data { | |
| return base."@end of data" | |
| } | |
| while.outer true { | |
| while.goto_parsed_a_leaf_value true {{ | |
| while.goto_fail true {{ | |
| if args.dst.length() <= 1 { | |
| yield? base."$short write" | |
| continue.outer | |
| } | |
| if args.src.length() <= 0 { | |
| if args.src.is_closed() { | |
| return "#bad input" | |
| } | |
| yield? base."$short read" | |
| continue.outer | |
| } | |
| c = args.src.peek_u8() | |
| if (indefinite_string_major_type <> 0) and (indefinite_string_major_type <> (c >> 5)) { | |
| if c <> 0xFF { | |
| return "#bad input" | |
| } | |
| vminor = (base.TOKEN__VBC__STRING << 21) | | |
| base.TOKEN__VBD__STRING__CONVERT_0_DST_1_SRC_DROP | |
| if indefinite_string_major_type == 3 { | |
| vminor |= base.TOKEN__VBD__STRING__DEFINITELY_UTF_8 | | |
| base.TOKEN__VBD__STRING__CHAIN_MUST_BE_UTF_8 | | |
| base.TOKEN__VBD__STRING__DEFINITELY_ASCII | |
| } | |
| indefinite_string_major_type = 0 | |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: vminor, | |
| continued: 0, | |
| length: 1) | |
| break.goto_parsed_a_leaf_value | |
| } | |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) | |
| c_major = (c >> 5) as base.u8 | |
| c_minor = c & 0x1F | |
| if c_minor < 0x18 { | |
| string_length = c_minor as base.u64 | |
| } else { | |
| while.goto_have_string_length true, | |
| inv args.dst.length() > 1, | |
| {{ | |
| if c_minor == 0x18 { | |
| if args.src.length() >= 1 { | |
| string_length = args.src.peek_u8_as_u64() | |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) | |
| break.goto_have_string_length | |
| } | |
| } else if c_minor == 0x19 { | |
| if args.src.length() >= 2 { | |
| string_length = args.src.peek_u16be_as_u64() | |
| args.src.skip_u32_fast!(actual: 2, worst_case: 2) | |
| break.goto_have_string_length | |
| } | |
| } else if c_minor == 0x1A { | |
| if args.src.length() >= 4 { | |
| string_length = args.src.peek_u32be_as_u64() | |
| args.src.skip_u32_fast!(actual: 4, worst_case: 4) | |
| break.goto_have_string_length | |
| } | |
| } else if c_minor == 0x1B { | |
| if args.src.length() >= 8 { | |
| string_length = args.src.peek_u64be() | |
| args.src.skip_u32_fast!(actual: 8, worst_case: 8) | |
| break.goto_have_string_length | |
| } | |
| } else { | |
| string_length = 0 | |
| break.goto_have_string_length | |
| } | |
| if args.src.can_undo_byte() { | |
| args.src.undo_byte!() | |
| if args.src.is_closed() { | |
| return "#bad input" | |
| } | |
| yield? base."$short read" | |
| continue.outer | |
| } | |
| return "#internal error: inconsistent I/O" | |
| }} endwhile.goto_have_string_length | |
| } | |
| if c_major == 0 { | |
| // -------- BEGIN Major type 0: an unsigned integer. | |
| if c_minor < 0x1A { | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: (base.TOKEN__VBC__INLINE_INTEGER_UNSIGNED << 21) | | |
| ((string_length & 0xFFFF) as base.u32), | |
| continued: 0, | |
| length: TOKEN_LENGTHS[c_minor] as base.u32) | |
| break.goto_parsed_a_leaf_value | |
| } else if c_minor < 0x1C { | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: (base.TOKEN__VBC__INLINE_INTEGER_UNSIGNED << 21) | | |
| ((string_length >> base.TOKEN__VALUE_EXTENSION__NUM_BITS) as base.u32), | |
| continued: 1, | |
| length: 0) | |
| args.dst.write_extended_token_fast!( | |
| value_extension: string_length & 0x3FFF_FFFF_FFFF, | |
| continued: 0, | |
| length: TOKEN_LENGTHS[c_minor] as base.u32) | |
| break.goto_parsed_a_leaf_value | |
| } | |
| // -------- END Major type 0: an unsigned integer. | |
| } else if c_major == 1 { | |
| // -------- END Major type 1: a negative integer. | |
| if c_minor < 0x1A { | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: (base.TOKEN__VBC__INLINE_INTEGER_SIGNED << 21) | | |
| (0x1F_FFFF - ((string_length & 0xFFFF) as base.u32)), | |
| continued: 0, | |
| length: TOKEN_LENGTHS[c_minor] as base.u32) | |
| break.goto_parsed_a_leaf_value | |
| } else if c_minor < 0x1C { | |
| if string_length < 0x8000_0000_0000_0000 { | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: (base.TOKEN__VBC__INLINE_INTEGER_SIGNED << 21) | | |
| (0x1F_FFFF - ((string_length >> base.TOKEN__VALUE_EXTENSION__NUM_BITS) as base.u32)), | |
| continued: 1, | |
| length: 0) | |
| args.dst.write_extended_token_fast!( | |
| value_extension: (0xFFFF_FFFF_FFFF_FFFF - string_length) & 0x3FFF_FFFF_FFFF, | |
| continued: 0, | |
| length: TOKEN_LENGTHS[c_minor] as base.u32) | |
| } else { | |
| args.dst.write_simple_token_fast!( | |
| value_major: TOKEN_VALUE_MAJOR, | |
| value_minor: TOKEN_VALUE_MINOR__MINUS_1_MINUS_X, | |
| continued: 0, | |
| length: 9) | |
| } | |
| break.goto_parsed_a_leaf_value | |
| } | |
| // -------- END Major type 1: a negative integer. | |
| } else if c_major == 2 { | |
| // -------- BEGIN Major type 2: a byte string. | |
| if c_minor < 0x1C { | |
| if string_length == 0 { | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: (base.TOKEN__VBC__STRING << 21) | | |
| base.TOKEN__VBD__STRING__CONVERT_0_DST_1_SRC_DROP, | |
| continued: 0, | |
| length: TOKEN_LENGTHS[c_minor] as base.u32) | |
| break.goto_parsed_a_leaf_value | |
| } | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: (base.TOKEN__VBC__STRING << 21) | | |
| base.TOKEN__VBD__STRING__CONVERT_0_DST_1_SRC_DROP, | |
| continued: 1, | |
| length: TOKEN_LENGTHS[c_minor] as base.u32) | |
| } else if c_minor == 0x1F { | |
| if indefinite_string_major_type <> 0 { | |
| break.goto_fail | |
| } | |
| indefinite_string_major_type = 2 | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: (base.TOKEN__VBC__STRING << 21) | | |
| base.TOKEN__VBD__STRING__CONVERT_0_DST_1_SRC_DROP, | |
| continued: 1, | |
| length: 1) | |
| continue.outer | |
| } else { | |
| break.goto_fail | |
| } | |
| while true { | |
| if args.dst.length() <= 0 { | |
| yield? base."$short write" | |
| continue | |
| } | |
| n64 = string_length.min(no_more_than: args.src.length()) | |
| token_length = (n64 & 0xFFFF) as base.u32 | |
| if n64 > 0xFFFF { | |
| token_length = 0xFFFF | |
| } else if token_length <= 0 { | |
| if args.src.is_closed() { | |
| return "#bad input" | |
| } | |
| yield? base."$short read" | |
| continue | |
| } | |
| if args.src.length() < (token_length as base.u64) { | |
| return "#internal error: inconsistent token length" | |
| } | |
| string_length ~mod-= token_length as base.u64 | |
| continued = 0 | |
| if (string_length > 0) or (indefinite_string_major_type > 0) { | |
| continued = 1 | |
| } | |
| args.src.skip_u32_fast!(actual: token_length, worst_case: token_length) | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: (base.TOKEN__VBC__STRING << 21) | | |
| base.TOKEN__VBD__STRING__CONVERT_1_DST_1_SRC_COPY, | |
| continued: continued, | |
| length: token_length) | |
| if string_length > 0 { | |
| continue | |
| } else if indefinite_string_major_type > 0 { | |
| continue.outer | |
| } | |
| break.goto_parsed_a_leaf_value | |
| } endwhile | |
| // -------- END Major type 2: a byte string. | |
| } else if c_major == 3 { | |
| // -------- BEGIN Major type 3: a text string. | |
| if c_minor < 0x1C { | |
| if string_length == 0 { | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: (base.TOKEN__VBC__STRING << 21) | | |
| base.TOKEN__VBD__STRING__DEFINITELY_UTF_8 | | |
| base.TOKEN__VBD__STRING__CHAIN_MUST_BE_UTF_8 | | |
| base.TOKEN__VBD__STRING__DEFINITELY_ASCII | | |
| base.TOKEN__VBD__STRING__CONVERT_0_DST_1_SRC_DROP, | |
| continued: 0, | |
| length: TOKEN_LENGTHS[c_minor] as base.u32) | |
| break.goto_parsed_a_leaf_value | |
| } | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: (base.TOKEN__VBC__STRING << 21) | | |
| base.TOKEN__VBD__STRING__DEFINITELY_UTF_8 | | |
| base.TOKEN__VBD__STRING__CHAIN_MUST_BE_UTF_8 | | |
| base.TOKEN__VBD__STRING__DEFINITELY_ASCII | | |
| base.TOKEN__VBD__STRING__CONVERT_0_DST_1_SRC_DROP, | |
| continued: 1, | |
| length: TOKEN_LENGTHS[c_minor] as base.u32) | |
| } else if c_minor == 0x1F { | |
| if indefinite_string_major_type <> 0 { | |
| break.goto_fail | |
| } | |
| indefinite_string_major_type = 3 | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: (base.TOKEN__VBC__STRING << 21) | | |
| base.TOKEN__VBD__STRING__DEFINITELY_UTF_8 | | |
| base.TOKEN__VBD__STRING__CHAIN_MUST_BE_UTF_8 | | |
| base.TOKEN__VBD__STRING__DEFINITELY_ASCII | | |
| base.TOKEN__VBD__STRING__CONVERT_0_DST_1_SRC_DROP, | |
| continued: 1, | |
| length: 1) | |
| continue.outer | |
| } else { | |
| break.goto_fail | |
| } | |
| while true { | |
| if args.dst.length() <= 0 { | |
| yield? base."$short write" | |
| continue | |
| } | |
| n64 = string_length.min(no_more_than: 0xFFFF) | |
| n64 = args.src.valid_utf_8_length(up_to: n64) | |
| token_length = (n64 & 0xFFFF) as base.u32 | |
| if token_length <= 0 { | |
| // The longest UTF-8 code point is 4 bytes. | |
| if args.src.is_closed() or (args.src.length() >= 4) { | |
| return "#bad input" | |
| } | |
| yield? base."$short read" | |
| continue | |
| } | |
| if args.src.length() < (token_length as base.u64) { | |
| return "#internal error: inconsistent token length" | |
| } | |
| string_length ~mod-= token_length as base.u64 | |
| continued = 0 | |
| if (string_length > 0) or (indefinite_string_major_type > 0) { | |
| continued = 1 | |
| } | |
| args.src.skip_u32_fast!(actual: token_length, worst_case: token_length) | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: (base.TOKEN__VBC__STRING << 21) | | |
| base.TOKEN__VBD__STRING__DEFINITELY_UTF_8 | | |
| base.TOKEN__VBD__STRING__CHAIN_MUST_BE_UTF_8 | | |
| base.TOKEN__VBD__STRING__CONVERT_1_DST_1_SRC_COPY, | |
| continued: continued, | |
| length: token_length) | |
| if string_length > 0 { | |
| continue | |
| } else if indefinite_string_major_type > 0 { | |
| continue.outer | |
| } | |
| break.goto_parsed_a_leaf_value | |
| } endwhile | |
| // -------- END Major type 3: a text string. | |
| } else if c_major == 4 { | |
| // -------- BEGIN Major type 4: an array of data items. | |
| if TOKEN_LENGTHS[c_minor] == 0 { | |
| break.goto_fail | |
| } else if depth >= 1024 { | |
| token_length = TOKEN_LENGTHS[c_minor] as base.u32 | |
| while (token_length > 0) and (args.src.can_undo_byte()) { | |
| args.src.undo_byte!() | |
| token_length -= 1 | |
| } endwhile | |
| return "#unsupported recursion depth" | |
| } | |
| vminor = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__PUSH | | |
| base.TOKEN__VBD__STRUCTURE__FROM_NONE | | |
| base.TOKEN__VBD__STRUCTURE__TO_LIST | |
| vminor_alt = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__POP | | |
| base.TOKEN__VBD__STRUCTURE__FROM_LIST | | |
| base.TOKEN__VBD__STRUCTURE__TO_NONE | |
| if depth > 0 { | |
| stack_byte = (depth - 1) / 16 | |
| stack_bit = ((depth - 1) & 15) * 2 | |
| if 0 == (this.stack[stack_byte] & ((1 as base.u32) << stack_bit)) { | |
| vminor = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__PUSH | | |
| base.TOKEN__VBD__STRUCTURE__FROM_LIST | | |
| base.TOKEN__VBD__STRUCTURE__TO_LIST | |
| vminor_alt = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__POP | | |
| base.TOKEN__VBD__STRUCTURE__FROM_LIST | | |
| base.TOKEN__VBD__STRUCTURE__TO_LIST | |
| } else { | |
| vminor = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__PUSH | | |
| base.TOKEN__VBD__STRUCTURE__FROM_DICT | | |
| base.TOKEN__VBD__STRUCTURE__TO_LIST | |
| vminor_alt = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__POP | | |
| base.TOKEN__VBD__STRUCTURE__FROM_LIST | | |
| base.TOKEN__VBD__STRUCTURE__TO_DICT | |
| } | |
| } | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: vminor, | |
| continued: 0, | |
| length: TOKEN_LENGTHS[c_minor] as base.u32) | |
| if c_minor == 0x00 { | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: vminor_alt, | |
| continued: 0, | |
| length: 0) | |
| break.goto_parsed_a_leaf_value | |
| } | |
| stack_byte = depth / 16 | |
| stack_bit = (depth & 15) * 2 | |
| this.stack[stack_byte] &= 0xFFFF_FFFF ^ ((3 as base.u32) << stack_bit) | |
| this.container_num_remaining[depth] = string_length | |
| depth += 1 | |
| tagged = false | |
| continue.outer | |
| // -------- END Major type 4: an array of data items. | |
| } else if c_major == 5 { | |
| // -------- BEGIN Major type 5: a map of pairs of data items. | |
| if TOKEN_LENGTHS[c_minor] == 0 { | |
| break.goto_fail | |
| } else if depth >= 1024 { | |
| token_length = TOKEN_LENGTHS[c_minor] as base.u32 | |
| while (token_length > 0) and (args.src.can_undo_byte()) { | |
| args.src.undo_byte!() | |
| token_length -= 1 | |
| } endwhile | |
| return "#unsupported recursion depth" | |
| } | |
| vminor = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__PUSH | | |
| base.TOKEN__VBD__STRUCTURE__FROM_NONE | | |
| base.TOKEN__VBD__STRUCTURE__TO_DICT | |
| vminor_alt = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__POP | | |
| base.TOKEN__VBD__STRUCTURE__FROM_DICT | | |
| base.TOKEN__VBD__STRUCTURE__TO_NONE | |
| if depth > 0 { | |
| stack_byte = (depth - 1) / 16 | |
| stack_bit = ((depth - 1) & 15) * 2 | |
| if 0 == (this.stack[stack_byte] & ((1 as base.u32) << stack_bit)) { | |
| vminor = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__PUSH | | |
| base.TOKEN__VBD__STRUCTURE__FROM_LIST | | |
| base.TOKEN__VBD__STRUCTURE__TO_DICT | |
| vminor_alt = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__POP | | |
| base.TOKEN__VBD__STRUCTURE__FROM_DICT | | |
| base.TOKEN__VBD__STRUCTURE__TO_LIST | |
| } else { | |
| vminor = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__PUSH | | |
| base.TOKEN__VBD__STRUCTURE__FROM_DICT | | |
| base.TOKEN__VBD__STRUCTURE__TO_DICT | |
| vminor_alt = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__POP | | |
| base.TOKEN__VBD__STRUCTURE__FROM_DICT | | |
| base.TOKEN__VBD__STRUCTURE__TO_DICT | |
| } | |
| } | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: vminor, | |
| continued: 0, | |
| length: TOKEN_LENGTHS[c_minor] as base.u32) | |
| if c_minor == 0x00 { | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: vminor_alt, | |
| continued: 0, | |
| length: 0) | |
| break.goto_parsed_a_leaf_value | |
| } | |
| stack_byte = depth / 16 | |
| stack_bit = (depth & 15) * 2 | |
| this.stack[stack_byte] |= (3 as base.u32) << stack_bit | |
| this.container_num_remaining[depth] = string_length | |
| depth += 1 | |
| tagged = false | |
| continue.outer | |
| // -------- END Major type 5: a map of pairs of data items. | |
| } else if c_major == 6 { | |
| // -------- BEGIN Major type 6: tags. | |
| if c_minor >= 0x1C { | |
| break.goto_fail | |
| } | |
| // Write one token (18 bits) or two tokens (18 + 46 bits). | |
| if string_length < 0x4_0000 { | |
| args.dst.write_simple_token_fast!( | |
| value_major: TOKEN_VALUE_MAJOR, | |
| value_minor: TOKEN_VALUE_MINOR__TAG | | |
| (string_length as base.u32), | |
| continued: 0, | |
| length: TOKEN_LENGTHS[c_minor] as base.u32) | |
| } else { | |
| args.dst.write_simple_token_fast!( | |
| value_major: TOKEN_VALUE_MAJOR, | |
| value_minor: TOKEN_VALUE_MINOR__TAG | | |
| ((string_length >> base.TOKEN__VALUE_EXTENSION__NUM_BITS) as base.u32), | |
| continued: 1, | |
| length: 0) | |
| args.dst.write_extended_token_fast!( | |
| value_extension: string_length & 0x3FFF_FFFF_FFFF, | |
| continued: 0, | |
| length: TOKEN_LENGTHS[c_minor] as base.u32) | |
| } | |
| tagged = true | |
| continue.outer | |
| // -------- END Major type 6: tags. | |
| } else if c_major == 7 { | |
| // -------- BEGIN Major type 7: miscellaneous. | |
| if c_minor < 0x14 { | |
| args.dst.write_simple_token_fast!( | |
| value_major: TOKEN_VALUE_MAJOR, | |
| value_minor: TOKEN_VALUE_MINOR__SIMPLE_VALUE | | |
| ((string_length & 0xFF) as base.u32), | |
| continued: 0, | |
| length: 1) | |
| break.goto_parsed_a_leaf_value | |
| } else if c_minor < 0x18 { | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: LITERALS[c_minor & 3], | |
| continued: 0, | |
| length: 1) | |
| break.goto_parsed_a_leaf_value | |
| } else if c_minor == 0x18 { | |
| if string_length < 0x18 { | |
| if not args.src.can_undo_byte() { | |
| return "#internal error: inconsistent I/O" | |
| } | |
| args.src.undo_byte!() | |
| break.goto_fail | |
| } | |
| args.dst.write_simple_token_fast!( | |
| value_major: TOKEN_VALUE_MAJOR, | |
| value_minor: TOKEN_VALUE_MINOR__SIMPLE_VALUE | | |
| ((string_length & 0xFF) as base.u32), | |
| continued: 0, | |
| length: 2) | |
| break.goto_parsed_a_leaf_value | |
| } else if c_minor < 0x1C { | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: (base.TOKEN__VBC__NUMBER << 21) | | |
| base.TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT | | |
| base.TOKEN__VBD__NUMBER__FORMAT_BINARY_BIG_ENDIAN | | |
| base.TOKEN__VBD__NUMBER__FORMAT_IGNORE_FIRST_BYTE, | |
| continued: 0, | |
| length: TOKEN_LENGTHS[c_minor] as base.u32) | |
| break.goto_parsed_a_leaf_value | |
| } else if c_minor == 0x1F { | |
| // Indefinite-length strings were dealt with separately, above. | |
| // Here, we expect to be in an indefinite-length container. | |
| if tagged or (depth <= 0) { | |
| break.goto_fail | |
| } | |
| depth -= 1 | |
| if this.container_num_remaining[depth] <> 0 { | |
| break.goto_fail | |
| } | |
| // Check that, if we're in a map, the map contains an even | |
| // number of elements. | |
| stack_byte = depth / 16 | |
| stack_bit = (depth & 15) * 2 | |
| stack_val = 3 & (this.stack[stack_byte] >> stack_bit) | |
| if stack_val == 1 { | |
| break.goto_fail | |
| } | |
| // Fill in FROM_ETC. | |
| if stack_val <> 3 { | |
| vminor_alt = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__POP | | |
| base.TOKEN__VBD__STRUCTURE__FROM_LIST | |
| } else { | |
| vminor_alt = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__POP | | |
| base.TOKEN__VBD__STRUCTURE__FROM_DICT | |
| } | |
| // Fill in TO_ETC. | |
| if depth <= 0 { | |
| vminor_alt |= base.TOKEN__VBD__STRUCTURE__TO_NONE | |
| } else { | |
| stack_byte = (depth - 1) / 16 | |
| stack_bit = ((depth - 1) & 15) * 2 | |
| if 0 == (this.stack[stack_byte] & ((1 as base.u32) << stack_bit)) { | |
| vminor_alt |= base.TOKEN__VBD__STRUCTURE__TO_LIST | |
| } else { | |
| vminor_alt |= base.TOKEN__VBD__STRUCTURE__TO_DICT | |
| } | |
| } | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: vminor_alt, | |
| continued: 0, | |
| length: 1) | |
| break.goto_parsed_a_leaf_value | |
| } | |
| // -------- END Major type 7: miscellaneous. | |
| } | |
| break.goto_fail | |
| }} endwhile.goto_fail | |
| if args.src.can_undo_byte() { | |
| args.src.undo_byte!() | |
| return "#bad input" | |
| } | |
| return "#internal error: inconsistent I/O" | |
| }} endwhile.goto_parsed_a_leaf_value | |
| // We've just parsed a leaf (non-container) value, or the (explicit or | |
| // implicit) close of a container (array or object). | |
| tagged = false | |
| while depth > 0 { | |
| // Toggle the key/value bit for object containers. This bit is | |
| // ignored for array containers. | |
| stack_byte = (depth - 1) / 16 | |
| stack_bit = ((depth - 1) & 15) * 2 | |
| this.stack[stack_byte] ^= (1 as base.u32) << (stack_bit + 1) | |
| if 1 == (3 & (this.stack[stack_byte] >> stack_bit)) { | |
| // We just parsed the key of a key-value pair. | |
| continue.outer | |
| } | |
| if this.container_num_remaining[depth - 1] <= 0 { | |
| // We're in an indefinite-length container. | |
| continue.outer | |
| } | |
| this.container_num_remaining[depth - 1] -= 1 | |
| if this.container_num_remaining[depth - 1] > 0 { | |
| // We're in a definite-length, non-empty container and have not | |
| // seen its final value. | |
| continue.outer | |
| } | |
| while args.dst.length() <= 0, | |
| inv depth > 0, | |
| post args.dst.length() > 0, | |
| { | |
| yield? base."$short write" | |
| continue | |
| } endwhile | |
| depth -= 1 | |
| // Fill in FROM_ETC. | |
| stack_byte = depth / 16 | |
| stack_bit = (depth & 15) * 2 | |
| if 0 == (this.stack[stack_byte] & ((1 as base.u32) << stack_bit)) { | |
| vminor_alt = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__POP | | |
| base.TOKEN__VBD__STRUCTURE__FROM_LIST | |
| } else { | |
| vminor_alt = (base.TOKEN__VBC__STRUCTURE << 21) | | |
| base.TOKEN__VBD__STRUCTURE__POP | | |
| base.TOKEN__VBD__STRUCTURE__FROM_DICT | |
| } | |
| // Fill in TO_ETC. | |
| if depth <= 0 { | |
| vminor_alt |= base.TOKEN__VBD__STRUCTURE__TO_NONE | |
| } else { | |
| stack_byte = (depth - 1) / 16 | |
| stack_bit = ((depth - 1) & 15) * 2 | |
| if 0 == (this.stack[stack_byte] & ((1 as base.u32) << stack_bit)) { | |
| vminor_alt |= base.TOKEN__VBD__STRUCTURE__TO_LIST | |
| } else { | |
| vminor_alt |= base.TOKEN__VBD__STRUCTURE__TO_DICT | |
| } | |
| } | |
| args.dst.write_simple_token_fast!( | |
| value_major: 0, | |
| value_minor: vminor_alt, | |
| continued: 0, | |
| length: 0) | |
| } endwhile | |
| // We've parsed the top-level value and we're therefore done overall. | |
| break.outer | |
| } endwhile.outer | |
| this.end_of_data = true | |
| } |