feophantlib/engine/io/page_formats/
uint12.rs

1//We are only going to support 4kb pages to match most common underlying I/O subsystems
2use bytes::{Buf, BufMut};
3use std::convert::TryFrom;
4use std::fmt;
5use std::mem::size_of;
6use std::ops::{Add, AddAssign, Sub, SubAssign};
7use thiserror::Error;
8
9use crate::constants::PAGE_SIZE;
10use crate::engine::io::ConstEncodedSize;
11
12#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
13pub struct UInt12(u16);
14
15impl UInt12 {
16    fn is_in_range(val: u16) -> bool {
17        val < PAGE_SIZE
18    }
19
20    fn clamp(val: u16) -> u16 {
21        if val > PAGE_SIZE - 1 {
22            return PAGE_SIZE - 1;
23        }
24        // Otherwise return val itself
25        val
26    }
27
28    pub fn new(val: u16) -> Result<UInt12, UInt12Error> {
29        if UInt12::is_in_range(val) {
30            Ok(UInt12(val))
31        } else {
32            Err(UInt12Error::ValueTooLargeU16(val))
33        }
34    }
35
36    pub fn to_u16(self) -> u16 {
37        self.0
38    }
39
40    pub fn to_usize(self) -> usize {
41        usize::try_from(self.0).unwrap()
42    }
43
44    pub fn max() -> UInt12 {
45        UInt12(PAGE_SIZE - 1)
46    }
47
48    pub fn serialize_packed(buffer: &mut impl BufMut, args: &[UInt12]) {
49        let mut left = true;
50        let mut combined = None;
51
52        for a in args {
53            if left {
54                buffer.put_u8((a.to_u16() & 0x00FF) as u8);
55                combined = Some(((a.to_u16() & 0xFF00) >> 4) as u8);
56                left = false;
57            } else {
58                buffer.put_u8(combined.unwrap() | ((a.to_u16() & 0xFF00) >> 8) as u8);
59                buffer.put_u8((a.to_u16() & 0x00FF) as u8);
60                combined = None;
61                left = true;
62            }
63        }
64
65        if let Some(s) = combined {
66            buffer.put_u8(s)
67        }
68    }
69
70    pub fn parse_packed(
71        buffer: &mut impl Buf,
72        expected_count: usize,
73    ) -> Result<Vec<UInt12>, UInt12Error> {
74        let mut items = Vec::with_capacity(expected_count);
75        let mut count = 0;
76
77        let mut left: u16 = 0;
78        let mut middle: u16 = 0;
79
80        while items.len() < expected_count {
81            if !buffer.has_remaining() {
82                return Err(UInt12Error::InsufficentData(buffer.remaining()));
83            }
84
85            match count % 3 {
86                0 => {
87                    left = buffer.get_u8() as u16;
88                }
89                1 => {
90                    middle = buffer.get_u8() as u16;
91                    let item = UInt12::new(left | (middle & 0x00F0) << 4)?;
92                    items.push(item);
93                }
94                2 => {
95                    let right = buffer.get_u8() as u16;
96                    let item = UInt12::new(right | (middle & 0x000F) << 8)?;
97                    items.push(item);
98                }
99                _ => panic!("Modular math is broken."),
100            }
101
102            count += 1;
103        }
104
105        Ok(items)
106    }
107}
108
109impl Add for UInt12 {
110    type Output = Self;
111
112    fn add(self, other: Self) -> Self::Output {
113        UInt12(UInt12::clamp(self.0.saturating_add(other.0)))
114    }
115}
116
117impl AddAssign for UInt12 {
118    fn add_assign(&mut self, other: Self) {
119        *self = UInt12(UInt12::clamp(self.0.saturating_add(other.0)))
120    }
121}
122
123impl Sub for UInt12 {
124    type Output = Self;
125    fn sub(self, other: Self) -> Self::Output {
126        UInt12(UInt12::clamp(self.0.saturating_sub(other.0)))
127    }
128}
129
130impl SubAssign for UInt12 {
131    fn sub_assign(&mut self, other: Self) {
132        *self = UInt12(UInt12::clamp(self.0.saturating_sub(other.0)))
133    }
134}
135
136impl TryFrom<usize> for UInt12 {
137    type Error = UInt12Error;
138
139    fn try_from(value: usize) -> Result<Self, Self::Error> {
140        let max = PAGE_SIZE as usize;
141        if value >= max {
142            return Err(UInt12Error::ValueTooLargeUSize(value));
143        }
144
145        Ok(UInt12(value as u16))
146    }
147}
148
149impl fmt::Display for UInt12 {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        write!(f, "{}", self.0)
152    }
153}
154
155impl ConstEncodedSize for UInt12 {
156    fn encoded_size() -> usize {
157        size_of::<u16>()
158    }
159}
160
161#[derive(Debug, Error, PartialEq)]
162pub enum UInt12Error {
163    #[error("Not enough data to parse, got {0}")]
164    InsufficentData(usize),
165    #[error("usize too large for UInt12 got {0}")]
166    ValueTooLargeUSize(usize),
167    #[error("u16 too large for UInt12 got {0}")]
168    ValueTooLargeU16(u16),
169}
170
171#[cfg(test)]
172mod tests {
173    use bytes::BytesMut;
174
175    use super::*;
176
177    #[test]
178    fn test_normal() -> Result<(), Box<dyn std::error::Error>> {
179        let test = UInt12::new(1)?;
180
181        assert_eq!(test.to_u16(), 1);
182
183        Ok(())
184    }
185
186    #[test]
187    fn test_math() -> Result<(), Box<dyn std::error::Error>> {
188        let mut test = UInt12::new(1)?;
189
190        test += UInt12::new(1)?;
191        test -= UInt12::new(1)?;
192
193        assert_eq!(test.to_u16(), 1);
194
195        Ok(())
196    }
197
198    #[test]
199    fn test_subtraction() -> Result<(), Box<dyn std::error::Error>> {
200        let left = UInt12::new(10)?;
201        let right = UInt12::new(5)?;
202
203        let result = left - right;
204
205        assert_eq!(result, right);
206
207        Ok(())
208    }
209
210    #[test]
211    fn test_usize() -> Result<(), Box<dyn std::error::Error>> {
212        let large: usize = 400;
213        let test = UInt12::try_from(large)?;
214
215        assert_eq!(test.to_u16(), 400);
216
217        Ok(())
218    }
219
220    #[test]
221    fn test_fail_usize() {
222        let large: usize = 40000;
223        let test = UInt12::try_from(large);
224
225        assert!(test.is_err());
226    }
227
228    fn roundtrip(input: Vec<UInt12>, serial_len: usize) -> Result<(), Box<dyn std::error::Error>> {
229        let mut buffer = BytesMut::new();
230        UInt12::serialize_packed(&mut buffer, &input);
231        let mut buffer = buffer.freeze();
232        assert_eq!(buffer.len(), serial_len);
233        let test_rt = UInt12::parse_packed(&mut buffer, input.len())?;
234        assert_eq!(test_rt, input);
235
236        Ok(())
237    }
238
239    #[test]
240    fn test_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
241        //Value that gave me a lovely bug
242        roundtrip(vec![UInt12::new(0)?], 2)?;
243
244        //Test numbers were picked to give a distingishable binary pattern for troubleshooting
245        roundtrip(vec![UInt12::new(2730)?], 2)?;
246
247        roundtrip(vec![UInt12::new(2730)?, UInt12::new(1365)?], 3)?;
248
249        roundtrip(
250            vec![UInt12::new(2730)?, UInt12::new(1365)?, UInt12::new(2730)?],
251            5,
252        )?;
253
254        Ok(())
255    }
256}