use super::{Decimal, NumError};
pub trait Encoder<const LEN: usize, TGT = u8>
where
Self: Sized,
{
type Err;
fn encode(&self) -> Result<[TGT; LEN], Self::Err>;
}
macro_rules! i8_encoder {
($len:expr) => {
impl Encoder<$len, i8> for Decimal {
type Err = NumError;
fn encode(&self) -> Result<[i8; $len], Self::Err> {
let encoded_u8 = <Self as Encoder<$len, u8>>::encode(self)?;
Ok(unsafe { std::mem::transmute(encoded_u8) })
}
}
};
}
macro_rules! u8_encoder_body {
($self:expr, $abs_to:ty) => {{
let is_negative = $self.is_sign_negative();
let num_part = <$abs_to>::try_from($self.mantissa().unsigned_abs()).expect("overflow!");
let scale_part = $self.scale() as u8;
let num_bytes = num_part.to_be_bytes();
let signed_scale = merge_to_signed_scale(scale_part, is_negative);
(num_bytes, signed_scale)
}};
}
i8_encoder!(5);
i8_encoder!(10);
impl Encoder<5, u8> for Decimal {
type Err = NumError;
fn encode(&self) -> Result<[u8; 5], Self::Err> {
let mut result = [0u8; 5];
let (num_bytes, signed_scale) = u8_encoder_body!(self, u32);
result[0..4].copy_from_slice(&num_bytes);
result[4] = signed_scale;
Ok(result)
}
}
impl Encoder<10, u8> for Decimal {
type Err = NumError;
fn encode(&self) -> Result<[u8; 10], Self::Err> {
let mut result = [0u8; 10];
let (num_bytes, signed_scale) = u8_encoder_body!(self, u64);
result[1..9].copy_from_slice(&num_bytes);
result[9] = signed_scale;
Ok(result)
}
}
fn merge_to_signed_scale(scale: u8, is_negative: bool) -> u8 {
if is_negative {
(scale & 0x7f) | 0x80
} else {
scale
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::identity_op)]
use crate::data::num::test_utils::{get_basic_tests_set, NOT_SIGN, SIGN};
#[test]
fn test_scale_merge() {
use super::merge_to_signed_scale;
assert_eq!(merge_to_signed_scale(0, false), 0 | NOT_SIGN);
assert_eq!(merge_to_signed_scale(0, true), 0 | SIGN);
assert_eq!(merge_to_signed_scale(1, false), 1 | NOT_SIGN);
assert_eq!(merge_to_signed_scale(2, true), 2 | SIGN);
assert_eq!(merge_to_signed_scale(3, false), 3 | NOT_SIGN);
}
#[test]
fn test_5b_encode() {
use crate::data::num::Encoder;
let tests_set = get_basic_tests_set::<5>();
for (raw, expected) in tests_set.iter() {
let actual: [u8; 5] = raw.encode().unwrap();
assert_eq!(actual.as_slice(), expected.as_slice());
}
}
#[test]
fn test_10b_encode() {
use crate::data::num::Encoder;
let tests_set = get_basic_tests_set::<10>();
for (raw, expected) in tests_set.iter() {
let actual: [u8; 10] = raw.encode().unwrap();
assert_eq!(actual.as_slice(), expected.as_slice());
}
}
}