feophantlib/engine/io/page_formats/
uint12.rs1use 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 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 roundtrip(vec![UInt12::new(0)?], 2)?;
243
244 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}