| // Copyright 2022 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 header" |
| pub status "#bad run length encoding" |
| pub status "#truncated input" |
| pub status "#unsupported TGA file" |
| |
| pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0 |
| |
| pub struct decoder? implements base.image_decoder( |
| width : base.u32[..= 0xFFFF], |
| height : base.u32[..= 0xFFFF], |
| |
| // The call sequence state machine is discussed in |
| // (/doc/std/image-decoders-call-sequence.md). |
| call_sequence : base.u8, |
| |
| header_id_length : base.u8, |
| header_color_map_type : base.u8, |
| header_image_type : base.u8, |
| header_color_map_first_entry_index : base.u16, |
| header_color_map_length : base.u16, |
| header_color_map_entry_size : base.u8, |
| header_pixel_depth : base.u8, |
| header_image_descriptor : base.u8, |
| |
| opaque : base.bool, |
| |
| scratch_bytes_per_pixel : base.u32[..= 4], |
| src_bytes_per_pixel : base.u32[..= 4], |
| src_pixfmt : base.u32, |
| |
| frame_config_io_position : base.u64, |
| |
| swizzler : base.pixel_swizzler, |
| util : base.utility, |
| )( |
| dst_palette : array[4 * 256] base.u8, |
| src_palette : array[4 * 256] base.u8, |
| scratch : array[4] base.u8, |
| ) |
| |
| pub func decoder.set_quirk_enabled!(quirk: base.u32, enabled: base.bool) { |
| } |
| |
| pub func decoder.decode_image_config?(dst: nptr base.image_config, src: base.io_reader) { |
| var status : base.status |
| |
| while true { |
| status =? this.do_decode_image_config?(dst: args.dst, src: args.src) |
| if (status == base."$short read") and args.src.is_closed() { |
| return "#truncated input" |
| } |
| yield? status |
| } endwhile |
| } |
| |
| pri func decoder.do_decode_image_config?(dst: nptr base.image_config, src: base.io_reader) { |
| var c : base.u32 |
| var c5 : base.u32[..= 0x1F] |
| var i : base.u32 |
| |
| if this.call_sequence <> 0x00 { |
| return base."#bad call sequence" |
| } |
| |
| this.header_id_length = args.src.read_u8?() |
| this.header_color_map_type = args.src.read_u8?() |
| if this.header_color_map_type > 1 { |
| return "#bad header" |
| } |
| |
| this.header_image_type = args.src.read_u8?() |
| if (this.header_image_type == 0x01) or |
| (this.header_image_type == 0x02) or |
| (this.header_image_type == 0x03) or |
| (this.header_image_type == 0x09) or |
| (this.header_image_type == 0x0A) or |
| (this.header_image_type == 0x0B) { |
| // No-op. |
| } else { |
| // TODO: 0x20 and 0x21 are invalid, according to the spec, but are |
| // apparently unofficial extensions. |
| return "#bad header" |
| } |
| |
| this.header_color_map_first_entry_index = args.src.read_u16le?() |
| this.header_color_map_length = args.src.read_u16le?() |
| this.header_color_map_entry_size = args.src.read_u8?() |
| if this.header_color_map_type <> 0 { |
| // We have a color-mapped image (in Wuffs, an indexed pixel format). |
| if (this.header_color_map_first_entry_index <> 0) or |
| (this.header_color_map_length > 0x100) { |
| return "#unsupported TGA file" |
| } else if (this.header_color_map_entry_size <> 0x0F) and |
| (this.header_color_map_entry_size <> 0x10) and |
| (this.header_color_map_entry_size <> 0x18) and |
| (this.header_color_map_entry_size <> 0x20) { |
| return "#bad header" |
| } |
| } else { |
| // The color-map fields must be zero. |
| if (this.header_color_map_first_entry_index <> 0) or |
| (this.header_color_map_length <> 0) or |
| (this.header_color_map_entry_size <> 0) { |
| return "#bad header" |
| } |
| } |
| |
| // Ignore the X-Origin and Y-Origin fields. |
| args.src.skip_u32?(n: 4) |
| |
| this.width = args.src.read_u16le_as_u32?() |
| this.height = args.src.read_u16le_as_u32?() |
| |
| this.header_pixel_depth = args.src.read_u8?() |
| if (this.header_pixel_depth <> 0x01) and |
| (this.header_pixel_depth <> 0x08) and |
| (this.header_pixel_depth <> 0x0F) and |
| (this.header_pixel_depth <> 0x10) and |
| (this.header_pixel_depth <> 0x18) and |
| (this.header_pixel_depth <> 0x20) { |
| return "#bad header" |
| } |
| |
| if (this.header_image_type | 8) == 0x09 { |
| this.scratch_bytes_per_pixel = 1 |
| this.src_bytes_per_pixel = 1 |
| this.src_pixfmt = base.PIXEL_FORMAT__INDEXED__BGRA_NONPREMUL |
| this.opaque = |
| (this.header_color_map_entry_size == 0x0F) or |
| (this.header_color_map_entry_size == 0x18) |
| |
| } else if (this.header_image_type | 8) == 0x0A { |
| if (this.header_pixel_depth == 0x0F) or |
| (this.header_pixel_depth == 0x10) { |
| // Wuffs' base.pixel_swizzler doesn't support BGRX5551, so |
| // scratch_bytes_per_pixel and src_bytes_per_pixel are different. |
| this.scratch_bytes_per_pixel = 4 |
| this.src_bytes_per_pixel = 0 |
| this.src_pixfmt = base.PIXEL_FORMAT__BGRA_NONPREMUL |
| } else if this.header_pixel_depth == 0x18 { |
| this.scratch_bytes_per_pixel = 3 |
| this.src_bytes_per_pixel = 3 |
| this.src_pixfmt = base.PIXEL_FORMAT__BGR |
| this.opaque = true |
| } else if this.header_pixel_depth == 0x20 { |
| this.scratch_bytes_per_pixel = 4 |
| this.src_bytes_per_pixel = 4 |
| this.src_pixfmt = base.PIXEL_FORMAT__BGRA_NONPREMUL |
| } else { |
| return "#unsupported TGA file" |
| } |
| |
| } else { |
| if this.header_pixel_depth == 0x08 { |
| this.scratch_bytes_per_pixel = 1 |
| this.src_bytes_per_pixel = 1 |
| this.src_pixfmt = base.PIXEL_FORMAT__Y |
| this.opaque = true |
| } else { |
| return "#unsupported TGA file" |
| } |
| } |
| |
| this.header_image_descriptor = args.src.read_u8?() |
| if (this.header_image_descriptor & 0x10) <> 0 { |
| // We don't support right-to-left, only left-to-right pixel order. |
| return "#unsupported TGA file" |
| } |
| |
| // Skip the Image ID. |
| args.src.skip_u32?(n: this.header_id_length as base.u32) |
| |
| // Read the color map. |
| if this.header_color_map_type <> 0 { |
| while i < (this.header_color_map_length as base.u32) { |
| assert i <= 0xFFFF via "a <= b: a <= c; c <= b"(c: this.header_color_map_length as base.u32) |
| if this.header_color_map_entry_size == 0x18 { |
| c = args.src.read_u24le_as_u32?() |
| this.src_palette[((i & 0xFF) * 4) + 0] = ((c >> 0x00) & 0xFF) as base.u8 |
| this.src_palette[((i & 0xFF) * 4) + 1] = ((c >> 0x08) & 0xFF) as base.u8 |
| this.src_palette[((i & 0xFF) * 4) + 2] = ((c >> 0x10) & 0xFF) as base.u8 |
| this.src_palette[((i & 0xFF) * 4) + 3] = 0xFF |
| } else if this.header_color_map_entry_size == 0x20 { |
| c = args.src.read_u32le?() |
| this.src_palette[((i & 0xFF) * 4) + 0] = ((c >> 0x00) & 0xFF) as base.u8 |
| this.src_palette[((i & 0xFF) * 4) + 1] = ((c >> 0x08) & 0xFF) as base.u8 |
| this.src_palette[((i & 0xFF) * 4) + 2] = ((c >> 0x10) & 0xFF) as base.u8 |
| this.src_palette[((i & 0xFF) * 4) + 3] = ((c >> 0x18) & 0xFF) as base.u8 |
| } else { |
| // Expand 15-bit or 16-bit color map entries. |
| c = args.src.read_u16le_as_u32?() |
| c5 = 0x1F & (c >> 0) |
| this.src_palette[((i & 0xFF) * 4) + 0] = ((c5 << 3) | (c5 >> 2)) as base.u8 |
| c5 = 0x1F & (c >> 5) |
| this.src_palette[((i & 0xFF) * 4) + 1] = ((c5 << 3) | (c5 >> 2)) as base.u8 |
| c5 = 0x1F & (c >> 10) |
| this.src_palette[((i & 0xFF) * 4) + 2] = ((c5 << 3) | (c5 >> 2)) as base.u8 |
| // TODO: can the alpha value be zero (BGRA5551 not BGRX5551)? |
| this.src_palette[((i & 0xFF) * 4) + 3] = 0xFF |
| } |
| i += 1 |
| } endwhile |
| while i < 0x100 { |
| this.src_palette[(i * 4) + 0] = 0x00 |
| this.src_palette[(i * 4) + 1] = 0x00 |
| this.src_palette[(i * 4) + 2] = 0x00 |
| this.src_palette[(i * 4) + 3] = 0xFF |
| i += 1 |
| } endwhile |
| } |
| |
| this.frame_config_io_position = args.src.position() |
| |
| if args.dst <> nullptr { |
| args.dst.set!( |
| pixfmt: this.src_pixfmt, |
| pixsub: 0, |
| width: this.width, |
| height: this.height, |
| first_frame_io_position: this.frame_config_io_position, |
| first_frame_is_opaque: this.opaque) |
| } |
| |
| this.call_sequence = 0x20 |
| } |
| |
| pub func decoder.decode_frame_config?(dst: nptr base.frame_config, src: base.io_reader) { |
| var status : base.status |
| |
| while true { |
| status =? this.do_decode_frame_config?(dst: args.dst, src: args.src) |
| if (status == base."$short read") and args.src.is_closed() { |
| return "#truncated input" |
| } |
| yield? status |
| } endwhile |
| } |
| |
| pri func decoder.do_decode_frame_config?(dst: nptr base.frame_config, src: base.io_reader) { |
| if this.call_sequence == 0x20 { |
| // No-op. |
| } else if this.call_sequence < 0x20 { |
| this.do_decode_image_config?(dst: nullptr, src: args.src) |
| } else if this.call_sequence == 0x28 { |
| if this.frame_config_io_position <> args.src.position() { |
| return base."#bad restart" |
| } |
| } else if this.call_sequence == 0x40 { |
| this.call_sequence = 0x60 |
| return base."@end of data" |
| } else { |
| return base."@end of data" |
| } |
| |
| if args.dst <> nullptr { |
| args.dst.set!(bounds: this.util.make_rect_ie_u32( |
| min_incl_x: 0, |
| min_incl_y: 0, |
| max_excl_x: this.width, |
| max_excl_y: this.height), |
| duration: 0, |
| index: 0, |
| io_position: this.frame_config_io_position, |
| disposal: 0, |
| opaque_within_bounds: this.opaque, |
| overwrite_instead_of_blend: false, |
| background_color: 0xFF00_0000) |
| } |
| |
| this.call_sequence = 0x40 |
| } |
| |
| pub func decoder.decode_frame?(dst: ptr base.pixel_buffer, src: base.io_reader, blend: base.pixel_blend, workbuf: slice base.u8, opts: nptr base.decode_frame_options) { |
| var status : base.status |
| |
| while true { |
| status =? this.do_decode_frame?(dst: args.dst, src: args.src, blend: args.blend, workbuf: args.workbuf, opts: args.opts) |
| if (status == base."$short read") and args.src.is_closed() { |
| return "#truncated input" |
| } |
| yield? status |
| } endwhile |
| } |
| |
| pri func decoder.do_decode_frame?(dst: ptr base.pixel_buffer, src: base.io_reader, blend: base.pixel_blend, workbuf: slice base.u8, opts: nptr base.decode_frame_options) { |
| var status : base.status |
| var dst_pixfmt : base.pixel_format |
| var dst_bits_per_pixel : base.u32[..= 256] |
| var dst_bytes_per_pixel : base.u64[..= 32] |
| var dst_x : base.u32 |
| var dst_y : base.u32 |
| var tab : table base.u8 |
| var dst_palette : slice base.u8 |
| var dst : slice base.u8 |
| var dst_start : base.u64 |
| var src_palette : slice base.u8 |
| var mark : base.u64 |
| var num_pixels64 : base.u64 |
| var num_pixels32 : base.u32[..= 0xFFFF] |
| var lit_length : base.u32[..= 0xFFFF] |
| var run_length : base.u32[..= 0xFFFF] |
| var num_dst_bytes : base.u64[..= 0x1F_FFE0] |
| var num_src_bytes : base.u32[..= 0x3_FFFC] |
| var c : base.u32 |
| var c5 : base.u32[..= 0x1F] |
| |
| if this.call_sequence == 0x40 { |
| // No-op. |
| } else if this.call_sequence < 0x40 { |
| this.do_decode_frame_config?(dst: nullptr, src: args.src) |
| } else { |
| return base."@end of data" |
| } |
| |
| if this.header_color_map_type <> 0 { |
| src_palette = this.src_palette[..] |
| } |
| status = this.swizzler.prepare!( |
| dst_pixfmt: args.dst.pixel_format(), |
| dst_palette: args.dst.palette_or_else(fallback: this.dst_palette[..]), |
| src_pixfmt: this.util.make_pixel_format(repr: this.src_pixfmt), |
| src_palette: src_palette, |
| blend: args.blend) |
| if not status.is_ok() { |
| return status |
| } |
| |
| // TODO: the dst_pixfmt variable shouldn't be necessary. We should be able |
| // to chain the two calls: "args.dst.pixel_format().bits_per_pixel()". |
| dst_pixfmt = args.dst.pixel_format() |
| dst_bits_per_pixel = dst_pixfmt.bits_per_pixel() |
| if (dst_bits_per_pixel & 7) <> 0 { |
| return base."#unsupported option" |
| } |
| dst_bytes_per_pixel = (dst_bits_per_pixel / 8) as base.u64 |
| |
| if (this.header_image_descriptor & 0x20) == 0 { // Bottom-to-top. |
| dst_y = this.height ~mod- 1 |
| } |
| if (this.header_image_type & 8) == 0 { |
| // No RLE (run length encoding) means that the entire row is |
| // effectively literals. |
| lit_length = this.width |
| } |
| |
| while.resume true { |
| tab = args.dst.plane(p: 0) |
| dst_palette = args.dst.palette_or_else(fallback: this.dst_palette[..]) |
| |
| while dst_y < this.height { |
| dst = tab.row_u32(y: dst_y) |
| dst_start = (dst_x as base.u64) * dst_bytes_per_pixel |
| if dst_start <= dst.length() { |
| dst = dst[dst_start ..] |
| } else { |
| dst = this.util.empty_slice_u8() |
| } |
| |
| while dst_x < this.width { |
| assert dst_x <= 0xFFFF via "a <= b: a <= c; c <= b"(c: this.width) |
| |
| if this.src_bytes_per_pixel > 0 { |
| if lit_length > 0 { |
| mark = args.src.mark() |
| num_pixels64 = args.src.length() / (this.src_bytes_per_pixel as base.u64) |
| num_pixels32 = num_pixels64.min(a: lit_length as base.u64) as base.u32 |
| num_dst_bytes = (num_pixels32 as base.u64) * dst_bytes_per_pixel |
| num_src_bytes = num_pixels32 * this.src_bytes_per_pixel |
| args.src.skip_u32?(n: num_src_bytes) |
| this.swizzler.swizzle_interleaved_from_slice!( |
| dst: dst, |
| dst_palette: dst_palette, |
| src: args.src.since(mark: mark)) |
| if num_dst_bytes <= dst.length() { |
| dst = dst[num_dst_bytes ..] |
| } else { |
| dst = this.util.empty_slice_u8() |
| } |
| dst_x += num_pixels32 |
| lit_length = (lit_length ~mod- num_pixels32) & 0xFFFF |
| if lit_length > 0 { |
| yield? base."$short read" |
| continue.resume |
| } |
| |
| } else if run_length > 0 { |
| run_length -= 1 |
| this.swizzler.swizzle_interleaved_from_slice!( |
| dst: dst, |
| dst_palette: dst_palette, |
| src: this.scratch[.. this.scratch_bytes_per_pixel]) |
| if dst_bytes_per_pixel <= dst.length() { |
| dst = dst[dst_bytes_per_pixel ..] |
| } |
| dst_x += 1 |
| |
| } else { |
| // Handle Raw vs RLE packets. |
| if args.src.length() <= 0 { |
| yield? base."$short read" |
| continue.resume |
| } |
| if args.src.peek_u8_as_u32() < 0x80 { |
| lit_length = args.src.peek_u8_as_u32() + 1 |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| if (lit_length + dst_x) > this.width { |
| return "#bad run length encoding" |
| } |
| |
| } else { |
| if this.src_bytes_per_pixel == 1 { |
| if args.src.length() < 2 { |
| yield? base."$short read" |
| continue.resume |
| } |
| run_length = (args.src.peek_u8_as_u32() & 0x7F) + 1 |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| this.scratch[0] = args.src.peek_u8() |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| } else if this.src_bytes_per_pixel == 3 { |
| if args.src.length() < 4 { |
| yield? base."$short read" |
| continue.resume |
| } |
| run_length = (args.src.peek_u8_as_u32() & 0x7F) + 1 |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| this.scratch[0] = args.src.peek_u8() |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| this.scratch[1] = args.src.peek_u8() |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| this.scratch[2] = args.src.peek_u8() |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| } else { // this.src_bytes_per_pixel == 4 |
| if args.src.length() < 5 { |
| yield? base."$short read" |
| continue.resume |
| } |
| run_length = (args.src.peek_u8_as_u32() & 0x7F) + 1 |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| this.scratch[0] = args.src.peek_u8() |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| this.scratch[1] = args.src.peek_u8() |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| this.scratch[2] = args.src.peek_u8() |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| this.scratch[3] = args.src.peek_u8() |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| } |
| |
| if (run_length + dst_x) > this.width { |
| return "#bad run length encoding" |
| } |
| } |
| } |
| |
| } else { |
| // Wuffs' base.pixel_swizzler doesn't support BGRX5551, so |
| // we manually convert to BGRX8888, one pixel at a time. |
| |
| if lit_length > 0 { |
| if args.src.length() < 2 { |
| yield? base."$short read" |
| continue.resume |
| } |
| c = args.src.peek_u16le_as_u32() |
| args.src.skip_u32_fast!(actual: 2, worst_case: 2) |
| c5 = 0x1F & (c >> 0) |
| this.scratch[0] = ((c5 << 3) | (c5 >> 2)) as base.u8 |
| c5 = 0x1F & (c >> 5) |
| this.scratch[1] = ((c5 << 3) | (c5 >> 2)) as base.u8 |
| c5 = 0x1F & (c >> 10) |
| this.scratch[2] = ((c5 << 3) | (c5 >> 2)) as base.u8 |
| // TODO: can the alpha value be zero (BGRA5551 not BGRX5551)? |
| this.scratch[3] = 0xFF |
| this.swizzler.swizzle_interleaved_from_slice!( |
| dst: dst, |
| dst_palette: dst_palette, |
| src: this.scratch[.. 4]) |
| if dst_bytes_per_pixel <= dst.length() { |
| dst = dst[dst_bytes_per_pixel ..] |
| } |
| dst_x += 1 |
| lit_length -= 1 |
| |
| } else if run_length > 0 { |
| run_length -= 1 |
| this.swizzler.swizzle_interleaved_from_slice!( |
| dst: dst, |
| dst_palette: dst_palette, |
| src: this.scratch[.. this.scratch_bytes_per_pixel]) |
| if dst_bytes_per_pixel <= dst.length() { |
| dst = dst[dst_bytes_per_pixel ..] |
| } |
| dst_x += 1 |
| |
| } else { |
| // Handle Raw vs RLE packets. |
| if args.src.length() <= 0 { |
| yield? base."$short read" |
| continue.resume |
| } |
| if args.src.peek_u8_as_u32() < 0x80 { |
| lit_length = args.src.peek_u8_as_u32() + 1 |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| if (lit_length + dst_x) > this.width { |
| return "#bad run length encoding" |
| } |
| |
| } else { |
| if args.src.length() < 3 { |
| yield? base."$short read" |
| continue.resume |
| } |
| run_length = (args.src.peek_u8_as_u32() & 0x7F) + 1 |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| c = args.src.peek_u16le_as_u32() |
| args.src.skip_u32_fast!(actual: 2, worst_case: 2) |
| c5 = 0x1F & (c >> 0) |
| this.scratch[0] = ((c5 << 3) | (c5 >> 2)) as base.u8 |
| c5 = 0x1F & (c >> 5) |
| this.scratch[1] = ((c5 << 3) | (c5 >> 2)) as base.u8 |
| c5 = 0x1F & (c >> 10) |
| this.scratch[2] = ((c5 << 3) | (c5 >> 2)) as base.u8 |
| // TODO: can the alpha value be zero (BGRA5551 not BGRX5551)? |
| this.scratch[3] = 0xFF |
| |
| if (run_length + dst_x) > this.width { |
| return "#bad run length encoding" |
| } |
| } |
| } |
| } |
| } endwhile |
| dst_x = 0 |
| |
| if (this.header_image_descriptor & 0x20) == 0 { // Bottom-to-top. |
| dst_y ~mod-= 1 |
| } else { // Top-to-bottom. |
| dst_y ~mod+= 1 |
| } |
| if (this.header_image_type & 8) == 0 { |
| // No RLE (run length encoding) means that the entire row is |
| // effectively literals. |
| lit_length = this.width |
| } |
| } endwhile |
| break.resume |
| } endwhile.resume |
| |
| this.call_sequence = 0x60 |
| } |
| |
| pub func decoder.frame_dirty_rect() base.rect_ie_u32 { |
| return this.util.make_rect_ie_u32( |
| min_incl_x: 0, |
| min_incl_y: 0, |
| max_excl_x: this.width, |
| max_excl_y: this.height) |
| } |
| |
| pub func decoder.num_animation_loops() base.u32 { |
| return 0 |
| } |
| |
| pub func decoder.num_decoded_frame_configs() base.u64 { |
| if this.call_sequence > 0x20 { |
| return 1 |
| } |
| return 0 |
| } |
| |
| pub func decoder.num_decoded_frames() base.u64 { |
| if this.call_sequence > 0x40 { |
| return 1 |
| } |
| return 0 |
| } |
| |
| pub func decoder.restart_frame!(index: base.u64, io_position: base.u64) base.status { |
| if this.call_sequence < 0x20 { |
| return base."#bad call sequence" |
| } |
| if args.index <> 0 { |
| return base."#bad argument" |
| } |
| this.call_sequence = 0x28 |
| this.frame_config_io_position = args.io_position |
| return ok |
| } |
| |
| pub func decoder.set_report_metadata!(fourcc: base.u32, report: base.bool) { |
| // TODO: implement. |
| } |
| |
| pub func decoder.tell_me_more?(dst: base.io_writer, minfo: nptr base.more_information, src: base.io_reader) { |
| return base."#no more information" |
| } |
| |
| pub func decoder.workbuf_len() base.range_ii_u64 { |
| return this.util.make_range_ii_u64(min_incl: 0, max_incl: 0) |
| } |