From 7a9a7bb59cb0a3106fdf9e08839c7fdfed1d0d23 Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Mon, 14 Oct 2024 17:06:17 +0200 Subject: [PATCH] fix!: #63 sane ordering for signed map keys --- packages/storey/src/containers/map/key.rs | 57 ++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/packages/storey/src/containers/map/key.rs b/packages/storey/src/containers/map/key.rs index 3ecb5cb..c9166ba 100644 --- a/packages/storey/src/containers/map/key.rs +++ b/packages/storey/src/containers/map/key.rs @@ -175,7 +175,9 @@ mod sealed { } /// An error type for decoding numeric keys. +#[derive(Debug, PartialEq, Eq, Clone, Copy, thiserror::Error)] pub enum NumericKeyDecodeError { + #[error("invalid length")] InvalidLength, } @@ -210,4 +212,57 @@ macro_rules! impl_key_for_numeric { }; } -impl_key_for_numeric!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); +impl_key_for_numeric!(u8, u16, u32, u64, u128); + +macro_rules! impl_key_for_signed { + ($($t:ty : $ut:ty),*) => { + $( + impl Key for $t { + type Kind = FixedSizeKey<{(Self::BITS / 8) as usize}>; + + fn encode(&self) -> Vec { + (*self as $ut ^ <$t>::MIN as $ut).to_be_bytes().to_vec() + } + } + + impl OwnedKey for $t { + type Error = NumericKeyDecodeError; + + fn from_bytes(bytes: &[u8]) -> Result + where + Self: Sized, + { + if bytes.len() != std::mem::size_of::() { + return Err(NumericKeyDecodeError::InvalidLength); + } + + let mut buf = [0; std::mem::size_of::()]; + buf.copy_from_slice(bytes); + Ok((Self::from_be_bytes(buf) as $ut ^ <$t>::MIN as $ut) as _) + } + } + )* + }; +} + +impl_key_for_signed!(i8 : u8, i16 : u16, i32 : u32, i64 : u64, i128 : u128); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn signed_int_ordering() { + let data = [-555555555, -3333, -1, 0, 1, 3333, 55555555]; + + let mut encoded = data.iter().map(|&x| x.encode()).collect::>(); + encoded.sort(); + + let decoded = encoded + .iter() + .map(|x| i32::from_bytes(x).unwrap()) + .collect::>(); + + assert_eq!(&data[..], &decoded); + } +}