1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use super::sign_extend;
use crate::parser::{InternalError, InternalResult};
use crate::utils::{as_i16, as_i8};
use crate::Reader;

const COUNT: usize = 4;

pub(crate) fn tagged_16(data: &mut Reader) -> InternalResult<[i16; COUNT]> {
    let tags = data.read_u8().ok_or(InternalError::Eof)?;

    if tags == 0 {
        return Ok([0; COUNT]);
    }

    let mut result = [0; COUNT];
    let mut aligned = true;
    let mut buffer = 0;

    for (i, result) in result.iter_mut().enumerate() {
        *result = match (tags >> (i * 2)) & 3 {
            0 => 0,
            1 => {
                let nibble = if aligned {
                    buffer = data.read_u8().ok_or(InternalError::Eof)?;
                    buffer >> 4
                } else {
                    buffer & 0xF
                };

                aligned = !aligned;
                i4_to_i16(nibble)
            }
            2 => {
                let byte = if aligned {
                    data.read_i8().ok_or(InternalError::Eof)?
                } else {
                    let upper = buffer << 4;
                    buffer = data.read_u8().ok_or(InternalError::Eof)?;
                    as_i8(upper | buffer >> 4)
                };

                byte.into()
            }
            3.. => {
                if aligned {
                    data.read_i16().ok_or(InternalError::Eof)?.swap_bytes()
                } else {
                    let upper = u16::from(buffer) << 12;
                    let [middle, lower] = data.read_u16().ok_or(InternalError::Eof)?.to_le_bytes();

                    buffer = lower;
                    as_i16(upper | (u16::from(middle) << 4) | u16::from(lower >> 4))
                }
            }
        }
    }

    Ok(result)
}

#[inline]
fn i4_to_i16(nibble: u8) -> i16 {
    sign_extend::<4>(nibble.into()) as i16
}

#[cfg(test)]
mod tests {
    use alloc::vec::Vec;
    use core::iter;

    use test_case::case;

    use super::*;

    fn bytes(first: u8, zeros: usize) -> Vec<u8> {
        iter::once(first)
            .chain(iter::repeat(0).take(zeros))
            .collect()
    }

    #[test]
    fn all_zeros() {
        let bytes = bytes(0x00, 0);
        let bytes = bytes.as_slice();

        assert_eq!([0; 4], tagged_16(&mut Reader::new(bytes)).unwrap());
    }

    #[test]
    fn all_nibbles() {
        let bytes = bytes(0x55, 2);
        let bytes = bytes.as_slice();

        assert_eq!([0; 4], tagged_16(&mut Reader::new(bytes)).unwrap());
    }

    #[test]
    fn all_bytes() {
        let bytes = bytes(0xAA, 4);
        let bytes = bytes.as_slice();

        assert_eq!([0; 4], tagged_16(&mut Reader::new(bytes)).unwrap());
    }

    #[test]
    fn all_16_bits() {
        let bytes: &[u8] = &[0xFF, 0, 1, 0, 2, 0, 3, 0, 4];
        let mut bits = Reader::new(bytes);

        let expected = [1, 2, 3, 4];
        assert_eq!(expected, tagged_16(&mut bits).unwrap());
    }

    #[test]
    fn tag_order_v2() {
        let bytes: &[u8] = &[0b1110_0100, 0x10, 0x20, 0x00, 0x30];
        let mut bits = Reader::new(bytes);

        assert_eq!([0, 1, 2, 3], tagged_16(&mut bits).unwrap());
    }

    #[case( &[0x30, 181, 61] => [0, 0, -19139, 0] ; "16 bit high byte first")]
    fn regressions(bytes: &[u8]) -> [i16; 4] {
        let mut bits = Reader::new(bytes);
        tagged_16(&mut bits).unwrap()
    }
}