use enum_dispatch::enum_dispatch;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EID([u8; 8]);
impl EID {
#[inline]
pub fn new(value: u64) -> Self {
EID::from(value)
}
#[inline]
pub fn empty() -> Self {
EID([0; 8])
}
#[inline]
pub fn is_valid(&self) -> bool {
const ZERO_ARR: [u8; 8] = [0; 8];
ZERO_ARR != self.0
}
}
impl From<u64> for EID {
#[inline]
fn from(value: u64) -> Self {
EID(value.to_le_bytes())
}
}
impl Into<u64> for EID {
#[inline]
fn into(self) -> u64 {
u64::from_le_bytes(self.0)
}
}
impl From<[u8; 8]> for EID {
#[inline]
fn from(value: [u8; 8]) -> Self {
EID(value)
}
}
impl AsRef<[u8]> for EID {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[enum_dispatch]
pub trait Valuable {}
#[allow(missing_docs)]
#[enum_dispatch(Valuable)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd)]
pub enum Value {
Bool(bool), EID(EID), UInt(u64), Int(i64), Float(f64), UInt3([u64; 3]), Int3([i64; 3]), Float3([f64; 3]), UInt2([u64; 2]), Int2([i64; 2]), Float2([f64; 2]), Mark(Marker), }
pub type Delta = Value;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct Marker([u8; 31]);
impl Marker {
pub fn new(hint: &str) -> Self {
Marker::from(hint)
}
}
impl From<&str> for Marker {
fn from(value: &str) -> Self {
let v = write_str_into::<31>(value);
Marker(v)
}
}
impl AsRef<str> for Marker {
fn as_ref(&self) -> &str {
read_str_from(&self.0)
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct PropTag([u8; 8]);
impl PropTag {
pub fn new(hint: &str) -> Self {
PropTag::from(hint)
}
}
impl From<&str> for PropTag {
fn from(value: &str) -> Self {
let v = write_str_into::<8>(value);
PropTag(v)
}
}
impl AsRef<str> for PropTag {
fn as_ref(&self) -> &str {
read_str_from(&self.0)
}
}
#[inline]
fn write_str_into<const N: usize>(raw: &str) -> [u8; N] {
let mut buf = [0u8; N];
let len = raw.len();
let limit = N;
if len == limit {
buf.copy_from_slice(&raw.as_bytes());
} else if len < limit {
buf[..len].copy_from_slice(&raw.as_bytes()[..len]);
} else {
let (len,trunc_str) = truncate_utf8(raw, limit);
buf[..len].copy_from_slice(trunc_str);
}
buf
}
fn read_str_from<'t, T: AsRef<[u8]>>(buf: &'t T) -> &'t str {
let buf = buf.as_ref();
let rtn = match std::str::from_utf8(buf) {
Ok(s) => s,
Err(e) => unsafe { std::str::from_utf8_unchecked(&buf[..e.valid_up_to()]) },
};
rtn.trim_end_matches('\0')
}
#[inline]
fn truncate_utf8(s: &str, max_length: usize) -> (usize, &[u8]) {
let mut cut_index = 0;
for (i, _) in s.char_indices().rev() {
if i <= max_length {
cut_index = i;
break;
}
}
(cut_index, &s.as_bytes()[..cut_index])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_truncate_utf8() {
let s1 = "Hello, World!";
let max_length1 = 1;
assert_eq!(truncate_utf8(s1, max_length1), (1,"H".as_bytes()));
let s2 = "Hello, World!";
let max_length2 = 3;
assert_eq!(truncate_utf8(s2, max_length2), (3, "Hel".as_bytes()));
let s3 = "Hello, World!";
let max_length3 = 5;
let expected_result3 = "Hello".as_bytes();
assert_eq!(truncate_utf8(s3, max_length3), (5, expected_result3));
let s4 = "你好,世界!";
let max_length4 = 6;
let expected_result4 = "你好".as_bytes();
assert_eq!(truncate_utf8(s4, max_length4), (6, expected_result4));
}
#[test]
fn test_write_str_into() {
let buffer = write_str_into::<5>("Hello");
assert_eq!(&buffer, b"Hello");
let buffer = write_str_into::<6>("世界你好");
assert_eq!(&buffer, "世界".as_bytes());
let buffer = write_str_into::<6>("こんにちは");
assert_eq!(&buffer, "こん".as_bytes());
let buffer = write_str_into::<10>("This string is longer than the buffer length");
assert_eq!(&buffer, "This strin".as_bytes());
let buffer = write_str_into::<0>("");
assert_eq!(&buffer[..0], "".as_bytes());
}
#[test]
fn test_marker_new() {
let marker = Marker::new("Hello, World!");
assert_eq!(marker.0, "Hello, World!\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0".as_bytes());
let marker = Marker::new("你好世界你好世界你好世界你好世界你好世界你好世界你好世界你好世界你好世界");
assert_eq!(marker.0, "你好世界你好世界你好\0".as_bytes());
let marker = Marker::new("你好世界");
assert_eq!(marker.0, "你好世界\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0".as_bytes());
}
#[test]
fn test_marker_eq(){
let marker1 = Marker::new("Hello, World!");
let marker2 = Marker::new("Hello, World!\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
let marker3 = Marker::new("Hello, World!\0\0\0");
assert_eq!(marker1, marker2);
assert_eq!(marker1, marker3);
assert_eq!(marker2, marker3);
let marker4 = Marker::new("Hello,\0 World!");
let marker5 = Marker::new("Hello,\0 World!\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0你好");
assert_eq!(marker4, marker5);
}
#[test]
fn test_marker_from() {
let marker = Marker::from("Hello, World!");
assert_eq!(marker, Marker::new("Hello, World!"));
let long_string =
"This is a very long string that exceeds the maximum length of 31 characters.";
let marker = Marker::from(long_string);
assert_eq!(marker.as_ref(), "This is a very long string that");
}
#[test]
fn test_marker_as_ref() {
let marker = Marker::new("Hello, World!");
assert_eq!(marker.as_ref(), "Hello, World!");
let marker = Marker::new("a你好世界你好世界你好世界你好世界你好世界你好世界你好世界你好世界你好世界");
assert_eq!(marker.as_ref(), "a你好世界你好世界你好");
let marker = Marker::new("aa你好世界你好世界你好世界你好世界你好世界你好世界你好世界你好世界你好世界");
assert_eq!(marker.as_ref(), "aa你好世界你好世界你");
let marker = Marker::new("你好世界");
assert_eq!(marker.as_ref(), "你好世界");
}
}