core/io: drop useless duplicated statement in test
[nihav.git] / nihav-core / src / io / intcode.rs
CommitLineData
5dd0d462 1//! Some universal integer codes support for bitstream reader.
aca89041 2use crate::io::bitreader::{BitReader, BitReaderError, BitReaderResult};
e450979b 3use crate::io::bitwriter::BitWriter;
d7fcdd86 4
5dd0d462 5/// Unsigned integer code types.
d7fcdd86
KS
6#[derive(Debug)]
7pub enum UintCodeType {
5dd0d462 8 /// Code where number is represented as run of ones with terminating zero.
d7fcdd86 9 UnaryOnes,
5dd0d462 10 /// Code where number is represented as run of zeroes with terminating one.
d7fcdd86 11 UnaryZeroes,
5dd0d462 12 /// Code for 0, 1 and 2 coded as `0`, `10` and `11`
d7fcdd86 13 Unary012,
5dd0d462 14 /// Code for 0, 1 and 2 coded as `11`, `10` and `0`
d7fcdd86 15 Unary210,
5dd0d462 16 /// General limited unary code with defined run and terminating bit.
d7fcdd86 17 LimitedUnary(u32, u32),
5dd0d462
KS
18 /// Limited run of zeroes with terminating one (unless the code has maximum length).
19 ///
20 /// [`Unary012`] is essentially an alias for `LimitedZeroes(2)`.
21 ///
22 /// [`Unary012`]: #variant.Unary012
3b89a4d7 23 LimitedZeroes(u32),
5dd0d462 24 /// Limited run of one with terminating zero (unless the code has maximum length).
3b89a4d7 25 LimitedOnes(u32),
5dd0d462 26 /// Golomb code.
d7fcdd86 27 Golomb(u8),
5dd0d462 28 /// Rice code.
d7fcdd86 29 Rice(u8),
5dd0d462 30 /// Elias Gamma code (interleaved).
d7fcdd86 31 Gamma,
5dd0d462 32 /// Elias Gamma' code (sometimes incorrectly called exp-Golomb).
d7fcdd86
KS
33 GammaP,
34}
35
5dd0d462 36/// Signed integer code types.
d7fcdd86 37pub enum IntCodeType {
5dd0d462 38 /// Golomb code. Last bit represents the sign.
d7fcdd86 39 Golomb(u8),
5dd0d462 40 /// Golomb code. Last bit represents the sign.
d7fcdd86 41 Rice(u8),
5dd0d462 42 /// Elias Gamma code. Unsigned values are remapped as 0, 1, -1, 2, -2, ...
d7fcdd86 43 Gamma,
5dd0d462 44 /// Elias Gamma' code. Unsigned values are remapped as 0, 1, -1, 2, -2, ...
d7fcdd86
KS
45 GammaP,
46}
47
5dd0d462
KS
48/// Universal integer code reader trait for bitstream reader.
49///
50/// # Examples
51///
52/// Read an unsigned Golomb code:
53/// ````
54/// use nihav_core::io::bitreader::*;
55/// use nihav_core::io::intcode::{IntCodeReader,UintCodeType};
56///
57/// # fn foo() -> BitReaderResult<()> {
58/// let mem: [u8; 4] = [ 0, 1, 2, 3];
59/// let mut br = BitReader::new(&mem, BitReaderMode::BE);
60/// let val = br.read_code(UintCodeType::Golomb(3))?;
61/// # Ok(())
62/// # }
63/// ````
64///
65/// Read signed Elias code:
66/// ````
67/// use nihav_core::io::bitreader::*;
68/// use nihav_core::io::intcode::{IntCodeReader,IntCodeType};
69///
70/// # fn foo() -> BitReaderResult<()> {
71/// let mem: [u8; 4] = [ 0, 1, 2, 3];
72/// let mut br = BitReader::new(&mem, BitReaderMode::BE);
73/// let val = br.read_code_signed(IntCodeType::Gamma)?;
74/// # Ok(())
75/// # }
76/// ````
d7fcdd86 77pub trait IntCodeReader {
5dd0d462 78 /// Reads an unsigned integer code of requested type.
d7fcdd86 79 fn read_code(&mut self, t: UintCodeType) -> BitReaderResult<u32>;
5dd0d462 80 /// Reads signed integer code of requested type.
d7fcdd86
KS
81 fn read_code_signed(&mut self, t: IntCodeType) -> BitReaderResult<i32>;
82}
83
84fn read_unary(br: &mut BitReader, terminator: u32) -> BitReaderResult<u32> {
85 let mut res: u32 = 0;
86 loop {
87 if br.read(1)? == terminator { return Ok(res); }
e243ceb4 88 res += 1;
d7fcdd86
KS
89 }
90}
91
8f879477 92fn read_unary_lim(br: &mut BitReader, len: u32, terminator: u32) -> BitReaderResult<u32> {
d7fcdd86
KS
93 let mut res: u32 = 0;
94 loop {
95 if br.read(1)? == terminator { return Ok(res); }
e243ceb4 96 res += 1;
d7fcdd86
KS
97 if res == len { return Ok(res); }
98 }
99}
100
101fn read_unary210(br: &mut BitReader) -> BitReaderResult<u32> {
102 let val = read_unary_lim(br, 2, 0)?;
103 Ok(2 - val)
104}
105
106fn read_golomb(br: &mut BitReader, m: u8) -> BitReaderResult<u32> {
107 if m == 0 { return Err(BitReaderError::InvalidValue); }
108 let nbits = (8 - m.leading_zeros()) as u8;
109 if (m & (m - 1)) == 0 { return read_rice(br, nbits); }
e243ceb4 110 let cutoff = u32::from((1 << nbits) - m);
d7fcdd86
KS
111 let pfx = read_unary(br, 0)?;
112 let tail = br.read(nbits - 1)?;
113 if tail < cutoff {
e243ceb4 114 let res = pfx * u32::from(m) + tail;
d7fcdd86
KS
115 Ok (res)
116 } else {
117 let add = br.read(1)?;
e243ceb4 118 let res = pfx * u32::from(m) + (tail - cutoff) * 2 + add + cutoff;
d7fcdd86
KS
119 Ok (res)
120 }
121}
122
123fn read_rice(br: &mut BitReader, k: u8) -> BitReaderResult<u32> {
124 let pfx = read_unary(br, 1)?;
125 let ret = (pfx << k) + br.read(k)?;
126 Ok(ret)
127}
128
129fn read_gamma(br: &mut BitReader) -> BitReaderResult<u32> {
6036ce28 130 let mut ret = 1;
d7fcdd86
KS
131 while br.read(1)? != 1 {
132 ret = (ret << 1) | br.read(1)?;
133 }
6036ce28 134 Ok(ret - 1)
d7fcdd86
KS
135}
136
137fn read_gammap(br: &mut BitReader) -> BitReaderResult<u32> {
138 let pfx = read_unary(br, 1)?;
139 if pfx > 32 { return Err(BitReaderError::InvalidValue); }
140 let ret = (1 << pfx) + br.read(pfx as u8)?;
141 Ok(ret)
142}
143
144fn uval_to_sval0mp(uval: u32) -> i32 {
ffe6400e 145 if (uval & 1) != 0 { -(((uval + 1) >> 1) as i32) }
d7fcdd86
KS
146 else { (uval >> 1) as i32 }
147}
148
149fn uval_to_sval0pm(uval: u32) -> i32 {
150 if (uval & 1) != 0 { ((uval + 1) >> 1) as i32 }
151 else { -((uval >> 1) as i32) }
152}
153
154impl<'a> IntCodeReader for BitReader<'a> {
83b49341 155 #[inline(always)]
d7fcdd86
KS
156 fn read_code(&mut self, t: UintCodeType) -> BitReaderResult<u32> {
157 match t {
158 UintCodeType::UnaryOnes => read_unary(self, 0),
159 UintCodeType::UnaryZeroes => read_unary(self, 1),
3b89a4d7
KS
160 UintCodeType::LimitedZeroes(len) => read_unary_lim(self, len, 1),
161 UintCodeType::LimitedOnes(len) => read_unary_lim(self, len, 0),
8f879477 162 UintCodeType::LimitedUnary(len, term) => read_unary_lim(self, len, term),
d7fcdd86
KS
163 UintCodeType::Unary012 => read_unary_lim(self, 2, 0),
164 UintCodeType::Unary210 => read_unary210(self),
165 UintCodeType::Golomb(m) => read_golomb(self, m),
166 UintCodeType::Rice(k) => read_rice(self, k),
167 UintCodeType::Gamma => read_gamma(self),
168 UintCodeType::GammaP => read_gammap(self),
169 }
170 }
171 #[allow(unused_variables)]
172 fn read_code_signed(&mut self, t: IntCodeType) -> BitReaderResult<i32> {
173 let uval =
174 match t {
175 IntCodeType::Golomb(m) => read_golomb(self, m)?,
176 IntCodeType::Rice(k) => read_rice(self, k)?,
177 IntCodeType::Gamma => read_gamma(self)?,
178 IntCodeType::GammaP => read_gammap(self)?,
179 };
180 match t {
181 IntCodeType::Golomb(m) => Ok(uval_to_sval0mp(uval)),
182 IntCodeType::Rice(k) => Ok(uval_to_sval0mp(uval)),
183 IntCodeType::Gamma => Ok(uval_to_sval0pm(uval)),
184 IntCodeType::GammaP => Ok(uval_to_sval0pm(uval)),
185 }
186 }
187}
188
e450979b
KS
189/// Universal integer code writer trait for bitstream writer.
190///
191/// # Examples
192///
193/// Write an unsigned Golomb code:
194/// ````
195/// use nihav_core::io::bitwriter::*;
196/// use nihav_core::io::intcode::{IntCodeWriter,UintCodeType};
197///
198/// let mut bw = BitWriter::new(Vec::new(), BitWriterMode::BE);
199/// bw.write_code(UintCodeType::Golomb(3), 42);
200/// ````
201///
202/// Write signed Elias code:
203/// ````
204/// use nihav_core::io::bitwriter::*;
205/// use nihav_core::io::intcode::{IntCodeWriter,IntCodeType};
206///
207/// let mut bw = BitWriter::new(Vec::new(), BitWriterMode::BE);
712d6d9d 208/// bw.write_code_signed(IntCodeType::Gamma, 42);
e450979b
KS
209/// ````
210pub trait IntCodeWriter {
211 /// Writes an unsigned integer code of requested type.
212 fn write_code(&mut self, t: UintCodeType, val: u32);
213 /// Writes signed integer code of requested type.
214 fn write_code_signed(&mut self, t: IntCodeType, val: i32);
215}
216
217impl IntCodeWriter for BitWriter {
218 #[inline(always)]
219 fn write_code(&mut self, t: UintCodeType, val: u32) {
220 match t {
221 UintCodeType::UnaryOnes => write_unary(self, val, 0),
222 UintCodeType::UnaryZeroes => write_unary(self, val, 1),
223 UintCodeType::LimitedZeroes(len) => write_unary_lim(self, val, len, 1),
224 UintCodeType::LimitedOnes(len) => write_unary_lim(self, val, len, 0),
225 UintCodeType::LimitedUnary(len, term) => write_unary_lim(self, val, len, term),
226 UintCodeType::Unary012 => write_unary_lim(self, val, 2, 0),
227 UintCodeType::Unary210 => write_unary210(self, val),
228 UintCodeType::Golomb(m) => write_golomb(self, val, m),
229 UintCodeType::Rice(k) => write_rice(self, val, k),
230 UintCodeType::Gamma => write_gamma(self, val),
231 UintCodeType::GammaP => write_gammap(self, val),
232 };
233 }
234 fn write_code_signed(&mut self, t: IntCodeType, val: i32) {
235 match t {
236 IntCodeType::Golomb(m) => write_golomb(self, sval0mp_to_uval(val), m),
237 IntCodeType::Rice(k) => write_rice(self, sval0mp_to_uval(val), k),
238 IntCodeType::Gamma => write_gamma(self, sval0pm_to_uval(val)),
239 IntCodeType::GammaP => write_gammap(self, sval0pm_to_uval(val)),
240 };
241 }
242}
243
244fn sval0mp_to_uval(val: i32) -> u32 {
245 if val < 0 { (-val as u32) * 2 - 1 }
246 else { (val as u32) * 2 }
247}
248
249fn sval0pm_to_uval(val: i32) -> u32 {
250 if val >= 0 { (val as u32) * 2 + 1 }
251 else { (-val as u32) * 2 }
252}
253
254fn write_unary210(bw: &mut BitWriter, val: u32) {
255 bw.write_bit(val == 0);
256 if val != 0 {
257 bw.write_bit(val == 1);
258 }
259}
260
261fn write_unary(bw: &mut BitWriter, val: u32, term: u32) {
262 let term = term != 0;
263 for _ in 0..val {
264 bw.write_bit(!term);
265 }
266 bw.write_bit(term);
267}
268
269fn write_unary_lim(bw: &mut BitWriter, val: u32, maxval: u32, term: u32) {
270 let term = term != 0;
271 for _ in 0..val {
272 bw.write_bit(!term);
273 }
274 if val < maxval {
275 bw.write_bit(term);
276 }
277}
278
279fn write_rice(bw: &mut BitWriter, val: u32, k: u8) {
280 let mut exp = val >> k;
281 while exp >= 16 {
282 bw.write(0, 16);
283 exp -= 16
284 }
285 if exp > 0 {
286 bw.write(0, exp as u8);
287 }
288 bw.write1();
289 if k > 0 {
290 let mant = val & ((1 << k) - 1);
291 bw.write(mant, k);
292 }
293}
294
295fn write_golomb(bw: &mut BitWriter, val: u32, m: u8) {
296 if m == 0 { return; }
297 let nbits = (8 - m.leading_zeros()) as u8;
298 if (m & (m - 1)) == 0 { return write_rice(bw, val, nbits); }
299 let q = val / u32::from(m);
300 let r = val % u32::from(m);
301 let cutoff = u32::from((1 << nbits) - m);
302
303 write_unary(bw, q, 0);
304 if r < cutoff {
305 bw.write(r, nbits - 1);
306 } else {
307 bw.write(r + cutoff, nbits);
308 }
309}
310
311fn write_gamma(bw: &mut BitWriter, val: u32) {
312 let val = val + 1;
313 let bits = (32 - val.leading_zeros()) as u8;
314 let mut mask = 1 << bits >> 2;
315 while mask != 0 {
316 bw.write0();
317 bw.write_bit((val & mask) != 0);
318 mask >>= 1;
319 }
320 bw.write1();
321}
322
323fn write_gammap(bw: &mut BitWriter, val: u32) {
324 let bits = 31 - val.leading_zeros();
325 write_unary(bw, bits, 1);
326 bw.write(val - (1 << bits), bits as u8);
327}
328
d7fcdd86
KS
329#[cfg(test)]
330mod test {
331 use super::*;
aca89041 332 use crate::io::bitreader::*;
e450979b 333 use crate::io::bitwriter::*;
d7fcdd86
KS
334
335 #[test]
336 fn int_codes() {
337 const GDATA: [u8; 6] = [0b000_001_01, 0b0_0110_011, 0b1_1000_100, 0b1_1010_101, 0b10_10111_1, 0b1000_0000];
338 let src = &GDATA;
fa90ccfb 339 let mut br = BitReader::new(src, BitReaderMode::BE);
d7fcdd86
KS
340 for i in 0..11 {
341 assert_eq!(br.read_code(UintCodeType::Golomb(5)).unwrap(), i);
342 }
343 }
e450979b
KS
344 #[test]
345 fn rw_codes() {
346 let mut bw = BitWriter::new(Vec::new(), BitWriterMode::BE);
347 bw.write_code(UintCodeType::Golomb(5), 42);
348 bw.write_code(UintCodeType::Gamma, 42);
349 bw.write_code(UintCodeType::GammaP, 42);
350 let data = bw.end();
e450979b
KS
351
352 let mut br = BitReader::new(&data, BitReaderMode::BE);
353 assert_eq!(br.read_code(UintCodeType::Golomb(5)).unwrap(), 42);
354 assert_eq!(br.read_code(UintCodeType::Gamma).unwrap(), 42);
355 assert_eq!(br.read_code(UintCodeType::GammaP).unwrap(), 42);
356 }
d7fcdd86 357}