blob: b193900fe0f2bf60e2d507f16082b3682f89d159 [file] [log] [blame]
Nigel Tao79a94552017-11-30 16:37:20 +11001// Copyright 2017 The Wuffs Authors.
Nigel Taod4372cb2017-10-12 11:17:41 +11002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Nigel Tao90e49ca2017-06-08 17:37:04 +100014
Nigel Tao045f1802019-02-23 17:06:24 +110015pub status "#bad code"
Nigel Tao48e69da2023-01-31 14:11:06 +110016pub status "#truncated input"
Nigel Tao38634272017-06-09 12:26:00 +100017
Nigel Tao045f1802019-02-23 17:06:24 +110018pri status "#internal error: inconsistent I/O"
Nigel Tao4e27b122018-10-14 17:53:20 +110019
Nigel Tao3056a842019-02-03 15:25:53 +110020// TODO: move bulk data buffers like decoder.suffixes or decoder.output into
Nigel Taod9c36922019-02-03 15:32:40 +110021// the workbuf? The first attempt at this was a performance regression for
22// decoding all but the smallest GIFs. See these git commits for numbers:
23// - 49627b4 Flatten the lzw.decoder.suffixes array
24// - f877fb2 Use the workbuf instead of lzw.decoder.suffixes
25// - 85be5b9 Delete the obsolete lzw.decoder.suffixes array
26// and the roll back has combined numbers:
27// - 3056a84 Roll back 3 recent lzw.decoder.suffixes commits
Nigel Tao532e18c2020-04-14 14:38:07 +100028pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0
Nigel Taobe2d96b2019-02-02 15:51:04 +110029
Nigel Tao92a5bfd2020-01-11 22:25:31 +110030pub struct decoder? implements base.io_transformer(
Nigel Taod41430c2019-05-26 15:51:46 +100031 // set_literal_width_arg is 1 plus the saved argument passed to
32 // set_literal_width. This is assigned to the literal_width field at the
Nigel Tao5f8d2da2020-01-10 22:06:50 +110033 // start of transform_io. During that method, calling set_literal_width
Nigel Taod41430c2019-05-26 15:51:46 +100034 // will change set_literal_width_arg but not literal_width.
Nigel Tao1520a1f2019-10-12 21:49:04 +110035 set_literal_width_arg : base.u32[..= 9],
Nigel Taoc2826e82018-12-26 11:01:08 +110036
37 // read_from state that does not change during a decode call.
Nigel Tao1520a1f2019-10-12 21:49:04 +110038 literal_width : base.u32[..= 8],
39 clear_code : base.u32[..= 256],
40 end_code : base.u32[..= 257],
Nigel Taoc2826e82018-12-26 11:01:08 +110041
42 // read_from state that does change during a decode call.
Nigel Tao1520a1f2019-10-12 21:49:04 +110043 save_code : base.u32[..= 4096],
44 prev_code : base.u32[..= 4095],
45 width : base.u32[..= 12],
46 bits : base.u32,
47 n_bits : base.u32[..= 31],
48 output_ri : base.u32[..= 8191],
49 output_wi : base.u32[..= 8191],
Nigel Taoc2826e82018-12-26 11:01:08 +110050
51 // read_from return value. The read_from method effectively returns a
52 // base.u32 to show how decode should continue after calling write_to. That
53 // value needs to be saved across write_to's possible suspension, so we
54 // might as well save it explicitly as a decoder field.
Nigel Tao1520a1f2019-10-12 21:49:04 +110055 read_from_return_value : base.u32,
Nigel Taoc2826e82018-12-26 11:01:08 +110056
Nigel Tao6974c4d2019-02-23 13:13:26 +110057 // read_from per-code state.
Nigel Tao1520a1f2019-10-12 21:49:04 +110058 prefixes : array[4096] base.u16[..= 4095],
Nigel Tao6974c4d2019-02-23 13:13:26 +110059
Nigel Tao1520a1f2019-10-12 21:49:04 +110060 util : base.utility,
Nigel Tao438fc102019-02-10 21:36:13 +110061)(
Nigel Taoc2826e82018-12-26 11:01:08 +110062 // read_from per-code state.
Nigel Tao1520a1f2019-10-12 21:49:04 +110063 suffixes : array[4096] array[8] base.u8,
Nigel Tao479326c2018-10-21 16:07:36 +110064 // lm1s is the "length minus 1"s of the values for the implicit key-value
65 // table in this decoder. See std/lzw/README.md for more detail.
Nigel Tao1520a1f2019-10-12 21:49:04 +110066 lm1s : array[4096] base.u16,
Nigel Taoc2826e82018-12-26 11:01:08 +110067
Nigel Tao314b1202018-12-29 14:23:27 +110068 // output[output_ri:output_wi] is the buffered output, connecting read_from
69 // with write_to and flush.
Nigel Tao1520a1f2019-10-12 21:49:04 +110070 output : array[8192 + 7] base.u8,
Nigel Taob640dac2017-05-05 18:07:09 +100071)
72
Nigel Tao4bc42db2020-04-12 20:53:54 +100073pub func decoder.set_quirk_enabled!(quirk: base.u32, enabled: base.bool) {
74}
75
Nigel Tao1520a1f2019-10-12 21:49:04 +110076pub func decoder.set_literal_width!(lw: base.u32[..= 8]) {
Nigel Taod41430c2019-05-26 15:51:46 +100077 this.set_literal_width_arg = args.lw + 1
Nigel Tao019e83f2017-06-11 13:58:12 +100078}
79
Nigel Taob9cbce92019-02-02 16:34:40 +110080pub func decoder.workbuf_len() base.range_ii_u64 {
Nigel Tao5b4e3642019-10-12 21:21:08 +110081 return this.util.make_range_ii_u64(min_incl: 0, max_incl: 0)
Nigel Taob9cbce92019-02-02 16:34:40 +110082}
83
Nigel Tao5f8d2da2020-01-10 22:06:50 +110084pub func decoder.transform_io?(dst: base.io_writer, src: base.io_reader, workbuf: slice base.u8) {
Nigel Taoa21ad252019-10-12 21:55:13 +110085 var i : base.u32[..= 8191]
Nigel Tao9bcdd1e2018-12-26 19:35:28 +110086
Nigel Taoc2826e82018-12-26 11:01:08 +110087 // Initialize read_from state.
88 this.literal_width = 8
Nigel Tao5bea8672019-05-12 19:18:30 +100089 if this.set_literal_width_arg > 0 {
Nigel Taod41430c2019-05-26 15:51:46 +100090 this.literal_width = this.set_literal_width_arg - 1
Nigel Tao0e498cb2018-05-29 09:07:23 +100091 }
Nigel Taoc2826e82018-12-26 11:01:08 +110092 this.clear_code = (1 as base.u32) << this.literal_width
93 this.end_code = this.clear_code + 1
94 this.save_code = this.end_code
95 this.prev_code = this.end_code
96 this.width = this.literal_width + 1
97 this.bits = 0
98 this.n_bits = 0
Nigel Tao314b1202018-12-29 14:23:27 +110099 this.output_ri = 0
Nigel Tao00c5f772018-12-26 11:15:22 +1100100 this.output_wi = 0
Nigel Tao9bcdd1e2018-12-26 19:35:28 +1100101 i = 0
Nigel Tao3056a842019-02-03 15:25:53 +1100102 while i < this.clear_code {
Nigel Tao5b4e3642019-10-12 21:21:08 +1100103 assert i < 256 via "a < b: a < c; c <= b"(c: this.clear_code)
Nigel Taoc2826e82018-12-26 11:01:08 +1100104 this.lm1s[i] = 0
Nigel Tao3056a842019-02-03 15:25:53 +1100105 this.suffixes[i][0] = i as base.u8
Nigel Taoc2826e82018-12-26 11:01:08 +1100106 i += 1
Nigel Tao58351d42020-03-25 21:38:31 +1100107 } endwhile
Nigel Tao4e27b122018-10-14 17:53:20 +1100108
Nigel Taoc2826e82018-12-26 11:01:08 +1100109 while true {
Nigel Tao5b4e3642019-10-12 21:21:08 +1100110 this.read_from!(src: args.src)
Nigel Taoc2826e82018-12-26 11:01:08 +1100111
Nigel Tao00c5f772018-12-26 11:15:22 +1100112 if this.output_wi > 0 {
Nigel Tao5b4e3642019-10-12 21:21:08 +1100113 this.write_to?(dst: args.dst)
Nigel Taoc2826e82018-12-26 11:01:08 +1100114 }
115
116 if this.read_from_return_value == 0 {
117 break
118 } else if this.read_from_return_value == 1 {
119 continue
120 } else if this.read_from_return_value == 2 {
Nigel Tao931fdce2019-02-23 17:15:57 +1100121 yield? base."$short read"
Nigel Taoc2826e82018-12-26 11:01:08 +1100122 } else if this.read_from_return_value == 3 {
Nigel Tao48e69da2023-01-31 14:11:06 +1100123 return "#truncated input"
124 } else if this.read_from_return_value == 4 {
Nigel Tao045f1802019-02-23 17:06:24 +1100125 return "#bad code"
Nigel Taoc2826e82018-12-26 11:01:08 +1100126 } else {
Nigel Tao045f1802019-02-23 17:06:24 +1100127 return "#internal error: inconsistent I/O"
Nigel Taoc2826e82018-12-26 11:01:08 +1100128 }
Nigel Tao58351d42020-03-25 21:38:31 +1100129 } endwhile
Nigel Taoc2826e82018-12-26 11:01:08 +1100130}
131
Nigel Tao1520a1f2019-10-12 21:49:04 +1100132pri func decoder.read_from!(src: base.io_reader) {
Nigel Taoa21ad252019-10-12 21:55:13 +1100133 var clear_code : base.u32[..= 256]
134 var end_code : base.u32[..= 257]
Nigel Taoc2826e82018-12-26 11:01:08 +1100135
Nigel Taoa21ad252019-10-12 21:55:13 +1100136 var save_code : base.u32[..= 4096]
137 var prev_code : base.u32[..= 4095]
138 var width : base.u32[..= 12]
139 var bits : base.u32
140 var n_bits : base.u32[..= 31]
141 var output_wi : base.u32[..= 8191]
Nigel Tao9bcdd1e2018-12-26 19:35:28 +1100142
Nigel Taoa21ad252019-10-12 21:55:13 +1100143 var code : base.u32[..= 4095]
144 var c : base.u32[..= 4095]
145 var o : base.u32[..= 8191]
146 var steps : base.u32
147 var first_byte : base.u8
148 var lm1_b : base.u16[..= 4095]
149 var lm1_a : base.u16[..= 4095]
Nigel Tao9bcdd1e2018-12-26 19:35:28 +1100150
151 clear_code = this.clear_code
152 end_code = this.end_code
153
154 save_code = this.save_code
155 prev_code = this.prev_code
156 width = this.width
157 bits = this.bits
158 n_bits = this.n_bits
159 output_wi = this.output_wi
Nigel Tao05a217e2017-05-18 15:15:58 +1000160
Nigel Tao3056a842019-02-03 15:25:53 +1100161 while true {
Nigel Tao6945bdc2018-10-20 07:22:23 +1100162 if n_bits < width {
Nigel Tao5b4e3642019-10-12 21:21:08 +1100163 assert n_bits < 12 via "a < b: a < c; c <= b"(c: width)
Nigel Taobe8542e2020-08-13 23:26:08 +1000164 if args.src.length() >= 4 {
Nigel Tao6945bdc2018-10-20 07:22:23 +1100165 // Read 4 bytes, using the "Variant 4" technique of
166 // https://fgiesen.wordpress.com/2018/02/20/reading-bits-in-far-too-many-ways-part-2/
Nigel Tao2f4c9312018-12-23 09:12:56 +1100167 bits |= args.src.peek_u32le() ~mod<< n_bits
Nigel Tao8b70ad02020-05-27 23:28:44 +1000168 args.src.skip_u32_fast!(actual: (31 - n_bits) >> 3, worst_case: 3)
Nigel Tao6945bdc2018-10-20 07:22:23 +1100169 n_bits |= 24
Nigel Tao5b4e3642019-10-12 21:21:08 +1100170 assert width <= n_bits via "a <= b: a <= c; c <= b"(c: 12)
Nigel Tao6945bdc2018-10-20 07:22:23 +1100171 assert n_bits >= width via "a >= b: b <= a"()
Nigel Taobe8542e2020-08-13 23:26:08 +1000172 } else if args.src.length() <= 0 {
Nigel Tao48e69da2023-01-31 14:11:06 +1100173 if args.src.is_closed() {
174 this.read_from_return_value = 3
175 } else {
176 this.read_from_return_value = 2
177 }
Nigel Taoc2826e82018-12-26 11:01:08 +1100178 break
Nigel Tao6945bdc2018-10-20 07:22:23 +1100179 } else {
Nigel Taoc2826e82018-12-26 11:01:08 +1100180 bits |= args.src.peek_u8_as_u32() << n_bits
Nigel Tao8b70ad02020-05-27 23:28:44 +1000181 args.src.skip_u32_fast!(actual: 1, worst_case: 1)
Nigel Taoc2826e82018-12-26 11:01:08 +1100182 n_bits += 8
183 if n_bits >= width {
184 // No-op.
Nigel Taobe8542e2020-08-13 23:26:08 +1000185 } else if args.src.length() <= 0 {
Nigel Tao48e69da2023-01-31 14:11:06 +1100186 if args.src.is_closed() {
187 this.read_from_return_value = 3
188 } else {
189 this.read_from_return_value = 2
190 }
Nigel Taoc2826e82018-12-26 11:01:08 +1100191 break
192 } else {
Nigel Tao2f4c9312018-12-23 09:12:56 +1100193 bits |= args.src.peek_u8_as_u32() << n_bits
Nigel Tao8b70ad02020-05-27 23:28:44 +1000194 args.src.skip_u32_fast!(actual: 1, worst_case: 1)
Nigel Tao6945bdc2018-10-20 07:22:23 +1100195 n_bits += 8
Nigel Tao5b4e3642019-10-12 21:21:08 +1100196 assert width <= n_bits via "a <= b: a <= c; c <= b"(c: 12)
Nigel Tao74053072018-12-26 16:52:55 +1100197 assert n_bits >= width via "a >= b: b <= a"()
198
199 // This if condition is always false, but for some unknown
200 // reason, removing it worsens the benchmarks slightly.
201 if n_bits < width {
Nigel Tao48e69da2023-01-31 14:11:06 +1100202 this.read_from_return_value = 5
Nigel Tao6945bdc2018-10-20 07:22:23 +1100203 break
204 }
Nigel Tao24e7ee02018-10-14 22:35:42 +1100205 }
Nigel Taoff92d222018-10-14 22:14:02 +1100206 }
Nigel Taobcf6a7e2017-06-01 14:55:06 +1000207 }
Nigel Taoff92d222018-10-14 22:14:02 +1100208
Nigel Tao5b4e3642019-10-12 21:21:08 +1100209 code = bits.low_bits(n: width)
Nigel Tao5a0aea62017-06-02 10:57:56 +1000210 bits >>= width
Nigel Tao80a39bf2017-06-02 11:39:51 +1000211 n_bits -= width
Nigel Taobcf6a7e2017-06-01 14:55:06 +1000212
Nigel Taoa8175262017-06-08 15:49:52 +1000213 if code < clear_code {
Nigel Tao5b4e3642019-10-12 21:21:08 +1100214 assert code < 256 via "a < b: a < c; c <= b"(c: clear_code)
Nigel Tao00c5f772018-12-26 11:15:22 +1100215 this.output[output_wi] = code as base.u8
216 output_wi = (output_wi + 1) & 8191
Nigel Tao3a46d6c2017-06-30 15:43:40 +1000217 if save_code <= 4095 {
Nigel Tao92959fb2019-02-16 15:40:02 +1100218 lm1_a = (this.lm1s[prev_code] ~mod+ 1) & 4095
Nigel Tao479326c2018-10-21 16:07:36 +1100219 this.lm1s[save_code] = lm1_a
Nigel Tao3338d6d2018-10-21 14:48:40 +1100220
Nigel Taoef7aa0d2019-03-02 14:49:02 +1100221 if (lm1_a % 8) <> 0 {
Nigel Tao3338d6d2018-10-21 14:48:40 +1100222 this.prefixes[save_code] = this.prefixes[prev_code]
Nigel Tao3056a842019-02-03 15:25:53 +1100223 this.suffixes[save_code] = this.suffixes[prev_code]
224 this.suffixes[save_code][lm1_a % 8] = code as base.u8
Nigel Tao3338d6d2018-10-21 14:48:40 +1100225 } else {
226 this.prefixes[save_code] = prev_code as base.u16
Nigel Tao3056a842019-02-03 15:25:53 +1100227 this.suffixes[save_code][0] = code as base.u8
Nigel Tao3338d6d2018-10-21 14:48:40 +1100228 }
229
Nigel Tao91bf5552018-06-30 08:33:23 +1000230 save_code += 1
Nigel Taoabeff312019-01-12 09:15:30 +1100231 if width < 12 {
232 width += 1 & (save_code >> width)
Nigel Tao91bf5552018-06-30 08:33:23 +1000233 }
Nigel Tao92b56632018-06-30 08:47:45 +1000234 prev_code = code
Nigel Tao587bc2a2017-06-09 18:00:33 +1000235 }
Nigel Tao711adf52017-06-09 15:21:03 +1000236
Nigel Tao9cd37ad2018-06-30 08:40:24 +1000237 } else if code <= end_code {
238 if code == end_code {
Nigel Taoc2826e82018-12-26 11:01:08 +1100239 this.read_from_return_value = 0
Nigel Tao4e27b122018-10-14 17:53:20 +1100240 break
Nigel Tao9cd37ad2018-06-30 08:40:24 +1000241 }
Nigel Tao0b034d72017-06-09 12:34:32 +1000242 save_code = end_code
Nigel Tao3338d6d2018-10-21 14:48:40 +1100243 prev_code = end_code
Nigel Taoc2826e82018-12-26 11:01:08 +1100244 width = this.literal_width + 1
Nigel Tao3338d6d2018-10-21 14:48:40 +1100245
Nigel Tao0b034d72017-06-09 12:34:32 +1000246 } else if code <= save_code {
Nigel Tao9bcdd1e2018-12-26 19:35:28 +1100247 c = code
Nigel Tao3a46d6c2017-06-30 15:43:40 +1000248 if code == save_code {
Nigel Tao8d6694a2017-06-10 15:23:43 +1000249 c = prev_code
Nigel Tao98a07922017-06-10 14:11:24 +1000250 }
251
Nigel Tao00c5f772018-12-26 11:15:22 +1100252 // Letting old_wi and new_wi denote the values of output_wi before
253 // and after these two lines of code, the decoded bytes will be
254 // written to output[old_wi:new_wi]. They will be written
255 // back-to-front, 8 bytes at a time, starting by writing
256 // output[o:o + 8], which will contain output[new_wi - 1].
Nigel Tao4e27b122018-10-14 17:53:20 +1100257 //
258 // In the special case that code == save_code, the decoded bytes
259 // contain an extra copy (at the end) of the first byte, and will
Nigel Tao00c5f772018-12-26 11:15:22 +1100260 // be written to output[old_wi:new_wi + 1].
Nigel Taob40671e2020-02-08 18:21:29 +1100261 o = (output_wi + ((this.lm1s[c] as base.u32) & 0xFFFF_FFF8)) & 8191
Nigel Tao00c5f772018-12-26 11:15:22 +1100262 output_wi = (output_wi + 1 + (this.lm1s[c] as base.u32)) & 8191
Nigel Tao4e27b122018-10-14 17:53:20 +1100263
Nigel Tao9bcdd1e2018-12-26 19:35:28 +1100264 steps = (this.lm1s[c] as base.u32) >> 3
Nigel Tao3056a842019-02-03 15:25:53 +1100265 while true {
Nigel Tao5b4e3642019-10-12 21:21:08 +1100266 assert o <= (o + 8) via "a <= (a + b): 0 <= b"(b: 8)
Nigel Taof60723f2018-10-21 18:03:54 +1100267
268 // The final "8" is redundant semantically, but helps the
269 // wuffs-c code generator recognize that both slices have the
270 // same constant length, and hence produce efficient C code.
Nigel Tao5b4e3642019-10-12 21:21:08 +1100271 this.output[o .. o + 8].copy_from_slice!(s: this.suffixes[c][.. 8])
Nigel Taof60723f2018-10-21 18:03:54 +1100272
Nigel Tao3338d6d2018-10-21 14:48:40 +1100273 if steps <= 0 {
274 break
275 }
276 steps -= 1
277
278 // This line is essentially "o -= 8". The "& 8191" is a no-op
Nigel Tao4e27b122018-10-14 17:53:20 +1100279 // in practice, but is necessary for the overflow checker.
Nigel Tao3338d6d2018-10-21 14:48:40 +1100280 o = (o ~mod- 8) & 8191
Nigel Tao6974c4d2019-02-23 13:13:26 +1100281 c = this.prefixes[c] as base.u32
Nigel Tao58351d42020-03-25 21:38:31 +1100282 } endwhile
Nigel Tao3056a842019-02-03 15:25:53 +1100283 first_byte = this.suffixes[c][0]
Nigel Tao8d6694a2017-06-10 15:23:43 +1000284
Nigel Tao3a46d6c2017-06-30 15:43:40 +1000285 if code == save_code {
Nigel Tao00c5f772018-12-26 11:15:22 +1100286 this.output[output_wi] = first_byte
287 output_wi = (output_wi + 1) & 8191
Nigel Tao66f71202017-10-13 10:30:22 +1100288 }
Nigel Tao8d6694a2017-06-10 15:23:43 +1000289
Nigel Tao3a46d6c2017-06-30 15:43:40 +1000290 if save_code <= 4095 {
Nigel Tao92959fb2019-02-16 15:40:02 +1100291 lm1_b = (this.lm1s[prev_code] ~mod+ 1) & 4095
Nigel Tao479326c2018-10-21 16:07:36 +1100292 this.lm1s[save_code] = lm1_b
Nigel Tao3338d6d2018-10-21 14:48:40 +1100293
Nigel Taoef7aa0d2019-03-02 14:49:02 +1100294 if (lm1_b % 8) <> 0 {
Nigel Tao3338d6d2018-10-21 14:48:40 +1100295 this.prefixes[save_code] = this.prefixes[prev_code]
Nigel Tao3056a842019-02-03 15:25:53 +1100296 this.suffixes[save_code] = this.suffixes[prev_code]
297 this.suffixes[save_code][lm1_b % 8] = first_byte
Nigel Tao3338d6d2018-10-21 14:48:40 +1100298 } else {
299 this.prefixes[save_code] = prev_code as base.u16
Nigel Tao3056a842019-02-03 15:25:53 +1100300 this.suffixes[save_code][0] = first_byte as base.u8
Nigel Tao3338d6d2018-10-21 14:48:40 +1100301 }
302
Nigel Tao91bf5552018-06-30 08:33:23 +1000303 save_code += 1
Nigel Taoabeff312019-01-12 09:15:30 +1100304 if width < 12 {
305 width += 1 & (save_code >> width)
Nigel Tao91bf5552018-06-30 08:33:23 +1000306 }
Nigel Tao92b56632018-06-30 08:47:45 +1000307 prev_code = code
Nigel Tao8d6694a2017-06-10 15:23:43 +1000308 }
Nigel Tao711adf52017-06-09 15:21:03 +1000309
Nigel Tao38634272017-06-09 12:26:00 +1000310 } else {
Nigel Tao48e69da2023-01-31 14:11:06 +1100311 this.read_from_return_value = 4
Nigel Tao179f7af2018-12-22 14:45:42 +1100312 break
Nigel Tao84bf4372017-06-06 19:01:48 +1000313 }
Nigel Tao4e27b122018-10-14 17:53:20 +1100314
315 // Flush the output if it could be too full to contain the entire
316 // decoding of the next code. The longest possible decoding is slightly
317 // less than 4096 and output's length is 8192, so a conservative
Nigel Tao00c5f772018-12-26 11:15:22 +1100318 // threshold is ensuring that output_wi <= 4095.
319 if output_wi > 4095 {
Nigel Taoc2826e82018-12-26 11:01:08 +1100320 this.read_from_return_value = 1
321 break
322 }
Nigel Tao58351d42020-03-25 21:38:31 +1100323 } endwhile
Nigel Taoc2826e82018-12-26 11:01:08 +1100324
325 // Rewind args.src, if we're not in "$short read" and we've read too many
326 // bits.
Nigel Taoef7aa0d2019-03-02 14:49:02 +1100327 if this.read_from_return_value <> 2 {
Nigel Taoc2826e82018-12-26 11:01:08 +1100328 while n_bits >= 8 {
329 n_bits -= 8
330 if args.src.can_undo_byte() {
331 args.src.undo_byte!()
332 } else {
Nigel Tao48e69da2023-01-31 14:11:06 +1100333 this.read_from_return_value = 5
Nigel Taoc2826e82018-12-26 11:01:08 +1100334 break
Nigel Taoff92d222018-10-14 22:14:02 +1100335 }
Nigel Tao58351d42020-03-25 21:38:31 +1100336 } endwhile
Nigel Tao4e27b122018-10-14 17:53:20 +1100337 }
338
Nigel Taoc2826e82018-12-26 11:01:08 +1100339 this.save_code = save_code
340 this.prev_code = prev_code
341 this.width = width
342 this.bits = bits
343 this.n_bits = n_bits
Nigel Tao00c5f772018-12-26 11:15:22 +1100344 this.output_wi = output_wi
Nigel Tao4e27b122018-10-14 17:53:20 +1100345}
346
Nigel Tao1520a1f2019-10-12 21:49:04 +1100347pri func decoder.write_to?(dst: base.io_writer) {
Nigel Taoa21ad252019-10-12 21:55:13 +1100348 var s : slice base.u8
349 var n : base.u64
Nigel Tao9bcdd1e2018-12-26 19:35:28 +1100350
Nigel Tao00c5f772018-12-26 11:15:22 +1100351 while this.output_wi > 0 {
Nigel Tao314b1202018-12-29 14:23:27 +1100352 if this.output_ri > this.output_wi {
Nigel Tao045f1802019-02-23 17:06:24 +1100353 return "#internal error: inconsistent I/O"
Nigel Tao4e27b122018-10-14 17:53:20 +1100354 }
Nigel Taoa9ea63f2019-10-12 21:14:18 +1100355 s = this.output[this.output_ri .. this.output_wi]
Nigel Tao5b4e3642019-10-12 21:21:08 +1100356 n = args.dst.copy_from_slice!(s: s)
Nigel Tao4e27b122018-10-14 17:53:20 +1100357 if n == s.length() {
Nigel Tao314b1202018-12-29 14:23:27 +1100358 this.output_ri = 0
Nigel Tao00c5f772018-12-26 11:15:22 +1100359 this.output_wi = 0
Nigel Tao762b3202018-11-24 15:09:28 +1100360 return ok
Nigel Tao4e27b122018-10-14 17:53:20 +1100361 }
Nigel Taob40671e2020-02-08 18:21:29 +1100362 this.output_ri = (this.output_ri ~mod+ ((n & 0xFFFF_FFFF) as base.u32)) & 8191
Nigel Tao931fdce2019-02-23 17:15:57 +1100363 yield? base."$short write"
Nigel Tao58351d42020-03-25 21:38:31 +1100364 } endwhile
Nigel Taob640dac2017-05-05 18:07:09 +1000365}
Nigel Tao622a76d2018-12-16 09:57:15 +1100366
Nigel Tao9311f7a2018-12-16 10:23:53 +1100367pub func decoder.flush!() slice base.u8 {
Nigel Taoa21ad252019-10-12 21:55:13 +1100368 var s : slice base.u8
Nigel Tao9bcdd1e2018-12-26 19:35:28 +1100369
Nigel Tao314b1202018-12-29 14:23:27 +1100370 if this.output_ri <= this.output_wi {
Nigel Taoa9ea63f2019-10-12 21:14:18 +1100371 s = this.output[this.output_ri .. this.output_wi]
Nigel Tao314b1202018-12-29 14:23:27 +1100372 }
373 this.output_ri = 0
Nigel Tao00c5f772018-12-26 11:15:22 +1100374 this.output_wi = 0
Nigel Tao622a76d2018-12-16 09:57:15 +1100375 return s
376}