use std::string::FromUtf8Error;
use thiserror::Error;
pub trait Value: Sized {
fn decode(data: &[u16]) -> Result<Self, DecodeError>;
fn encode(self) -> Box<[u16]>;
}
pub trait FixedSize: Value {
const SIZE: u16;
}
impl Value for u16 {
fn decode(data: &[u16]) -> Result<Self, DecodeError> {
let &[w0] = data else {
return Err(DecodeError::OutOfBounds);
};
Ok(w0)
}
fn encode(self) -> Box<[u16]> {
Box::new([self])
}
}
impl FixedSize for u16 {
const SIZE: u16 = 1;
}
impl Value for u32 {
fn decode(words: &[u16]) -> Result<Self, DecodeError> {
let &[w1, w0] = words else {
return Err(DecodeError::OutOfBounds);
};
Ok((w1 as u32) << 16 | w0 as u32)
}
fn encode(self) -> Box<[u16]> {
Box::new([(self >> 16) as u16, self as u16])
}
}
impl FixedSize for u32 {
const SIZE: u16 = 2;
}
impl Value for u64 {
fn decode(data: &[u16]) -> Result<Self, DecodeError> {
let &[w3, w2, w1, w0] = data else {
return Err(DecodeError::OutOfBounds);
};
Ok((w3 as u64) << 0x30 | (w2 as u64) << 0x20 | (w1 as u64) << 0x10 | w0 as u64)
}
fn encode(self) -> Box<[u16]> {
Box::new([
(self >> 0x30) as u16,
(self >> 0x20) as u16,
(self >> 0x10) as u16,
self as u16,
])
}
}
impl FixedSize for u64 {
const SIZE: u16 = 4;
}
impl Value for u128 {
fn decode(words: &[u16]) -> Result<Self, DecodeError> {
let &[w7, w6, w5, w4, w3, w2, w1, w0] = words else {
return Err(DecodeError::OutOfBounds);
};
Ok((w7 as u128) << 0x70
| (w6 as u128) << 0x60
| (w5 as u128) << 0x50
| (w4 as u128) << 0x40
| (w3 as u128) << 0x30
| (w2 as u128) << 0x20
| (w1 as u128) << 0x10
| (w0 as u128))
}
fn encode(self) -> Box<[u16]> {
Box::new([
(self >> 0x70) as u16,
(self >> 0x60) as u16,
(self >> 0x50) as u16,
(self >> 0x40) as u16,
(self >> 0x30) as u16,
(self >> 0x20) as u16,
(self >> 0x10) as u16,
self as u16,
])
}
}
impl FixedSize for u128 {
const SIZE: u16 = 8;
}
impl Value for i16 {
fn decode(data: &[u16]) -> Result<Self, DecodeError> {
Ok(u16::decode(data)? as Self)
}
fn encode(self) -> Box<[u16]> {
(self as u16).encode()
}
}
impl FixedSize for i16 {
const SIZE: u16 = 1;
}
impl Value for i32 {
fn decode(data: &[u16]) -> Result<Self, DecodeError> {
Ok(u32::decode(data)? as Self)
}
fn encode(self) -> Box<[u16]> {
(self as u32).encode()
}
}
impl FixedSize for i32 {
const SIZE: u16 = 2;
}
impl Value for i64 {
fn decode(data: &[u16]) -> Result<Self, DecodeError> {
Ok(u64::decode(data)? as Self)
}
fn encode(self) -> Box<[u16]> {
(self as u64).encode()
}
}
impl FixedSize for i64 {
const SIZE: u16 = 4;
}
impl Value for f32 {
fn decode(data: &[u16]) -> Result<Self, DecodeError> {
let &[w1, w0] = data else {
return Err(DecodeError::OutOfBounds);
};
Ok(f32::from_be_bytes([
(w1 >> 8) as u8,
w1 as u8,
(w0 >> 8) as u8,
w0 as u8,
]))
}
fn encode(self) -> Box<[u16]> {
let [b3, b2, b1, b0] = self.to_be_bytes();
Box::new([
(b3 as u16) << 8 | (b2 as u16),
(b1 as u16) << 8 | (b0 as u16),
])
}
}
impl FixedSize for f32 {
const SIZE: u16 = 2;
}
impl Value for f64 {
fn decode(data: &[u16]) -> Result<Self, DecodeError> {
let &[w3, w2, w1, w0] = data else {
return Err(DecodeError::OutOfBounds);
};
Ok(f64::from_be_bytes([
(w3 >> 8) as u8,
w3 as u8,
(w2 >> 8) as u8,
w2 as u8,
(w1 >> 8) as u8,
w1 as u8,
(w0 >> 8) as u8,
w0 as u8,
]))
}
fn encode(self) -> Box<[u16]> {
let [b7, b6, b5, b4, b3, b2, b1, b0] = self.to_be_bytes();
Box::new([
(b7 as u16) << 8 | (b6 as u16),
(b5 as u16) << 8 | (b4 as u16),
(b3 as u16) << 8 | (b2 as u16),
(b1 as u16) << 8 | (b0 as u16),
])
}
}
impl FixedSize for f64 {
const SIZE: u16 = 4;
}
impl Value for String {
fn decode(data: &[u16]) -> Result<Self, DecodeError> {
let bytes = data
.iter()
.flat_map(|word| word.to_be_bytes())
.take_while(|&b| b != 0)
.collect::<Vec<_>>();
Ok(String::from_utf8(bytes)?)
}
fn encode(self) -> Box<[u16]> {
self.as_bytes()
.chunks(2)
.map(|chunk| match *chunk {
[b1, b0] => (b1 as u16) << 8 | (b0 as u16),
[b1] => (b1 as u16) << 8,
_ => unreachable!(),
})
.collect()
}
}
#[derive(Debug, Error, Eq, PartialEq)]
pub enum DecodeError {
#[error("Out of bounds")]
OutOfBounds,
#[error("Invalid UTF-8 data")]
Utf8(#[from] FromUtf8Error),
}
#[test]
fn test_u16() {
assert_eq!(*0x0001i16.encode(), [0x1]);
assert_eq!(u16::decode(&[0x1]), Ok(0x0001u16));
}
#[test]
fn test_u32() {
assert_eq!(*0x00010002u32.encode(), [0x1, 0x2]);
assert_eq!(u32::decode(&[0x1, 0x2]), Ok(0x00010002u32));
}
#[test]
fn test_u64() {
assert_eq!(*0x0001000200030004u64.encode(), [0x1, 0x2, 0x3, 0x4]);
assert_eq!(
u64::decode(&[0x1, 0x2, 0x3, 0x4]),
Ok(0x0001000200030004u64)
);
}
#[test]
fn test_u128() {
assert_eq!(
*0x00010002000300040005000600070008u128.encode(),
[0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]
);
assert_eq!(
u128::decode(&[0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]),
Ok(0x00010002000300040005000600070008u128)
);
}
#[test]
fn test_i16() {
assert_eq!(*(-1i16).encode(), [0xffff]);
assert_eq!(i16::decode(&[0xffff]), Ok(-1i16));
}
#[test]
fn test_i32() {
assert_eq!(*(-1i32).encode(), [0xffff, 0xffff]);
assert_eq!(i32::decode(&[0xffff, 0xffff]), Ok(-1i32));
}
#[test]
fn test_i64() {
assert_eq!(*(-1i64).encode(), [0xffff, 0xffff, 0xffff, 0xffff]);
assert_eq!(i64::decode(&[0xffff, 0xffff, 0xffff, 0xffff]), Ok(-1i64));
}
#[test]
fn test_f32() {
assert_eq!(*(0.5f32).encode(), [0x3f00, 0x0000]);
assert_eq!(f32::decode(&[0x3f00, 0x0000]), Ok(0.5f32));
}
#[test]
fn test_f64() {
assert_eq!(*(0.5f64).encode(), [0x3fe0, 0x0000, 0x0000, 0x0000]);
assert_eq!(f64::decode(&[0x3fe0, 0x0000, 0x0000, 0x0000]), Ok(0.5f64));
}
#[test]
fn test_string() {
assert_eq!(*String::from("SunS").encode(), [0x5375, 0x6e53]);
assert_eq!(String::decode(&[0x5375, 0x6e53]), Ok(String::from("SunS")));
assert_eq!(*String::from("pad").encode(), [0x7061, 0x6400]);
assert_eq!(String::decode(&[0x7061, 0x6400]), Ok(String::from("pad")));
assert_eq!(
String::decode(&[0x7061, 0x6400, 0x0000]),
Ok(String::from("pad"))
);
}