use crate::{Dodecet, DodecetError, Result};
use std::ops::{Deref, DerefMut};
const HEX_CHARS: [u8; 16] = *b"0123456789ABCDEF";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DodecetStringOptimized {
data: Vec<Dodecet>,
}
impl DodecetStringOptimized {
pub fn new() -> Self {
DodecetStringOptimized { data: Vec::new() }
}
pub fn with_capacity(capacity: usize) -> Self {
DodecetStringOptimized {
data: Vec::with_capacity(capacity),
}
}
pub fn capacity(&self) -> usize {
self.data.capacity()
}
pub fn from_slice(values: &[u16]) -> Self {
DodecetStringOptimized {
data: values.iter().map(|&v| Dodecet::from_hex(v)).collect(),
}
}
pub fn from_dodecets(dodecets: Vec<Dodecet>) -> Self {
DodecetStringOptimized { data: dodecets }
}
pub fn push(&mut self, value: u16) {
self.data.push(Dodecet::from_hex(value));
}
pub fn push_dodecet(&mut self, dodecet: Dodecet) {
self.data.push(dodecet);
}
pub fn pop(&mut self) -> Option<Dodecet> {
self.data.pop()
}
pub fn to_hex_string(&self) -> String {
let mut result = String::with_capacity(self.data.len() * 3);
for d in &self.data {
let value = d.value();
result.push(HEX_CHARS[((value >> 8) & 0xF) as usize] as char);
result.push(HEX_CHARS[((value >> 4) & 0xF) as usize] as char);
result.push(HEX_CHARS[(value & 0xF) as usize] as char);
}
result
}
pub fn to_hex_bytes(&self) -> Vec<u8> {
let mut result = Vec::with_capacity(self.data.len() * 3);
for d in &self.data {
let value = d.value();
result.push(HEX_CHARS[((value >> 8) & 0xF) as usize]);
result.push(HEX_CHARS[((value >> 4) & 0xF) as usize]);
result.push(HEX_CHARS[(value & 0xF) as usize]);
}
result
}
pub fn from_hex_str(s: &str) -> Result<Self> {
if !s.len().is_multiple_of(3) {
return Err(DodecetError::InvalidHex);
}
let mut data = Vec::with_capacity(s.len() / 3);
for chunk in s.as_bytes().chunks(3) {
let chunk_str = std::str::from_utf8(chunk).unwrap();
data.push(Dodecet::from_hex_str(chunk_str)?);
}
Ok(DodecetStringOptimized { data })
}
pub fn to_bytes(&self) -> Vec<u8> {
let len = self.data.len();
let mut bytes = Vec::with_capacity((len * 3 + 1) / 2);
let mut i = 0;
while i + 1 < len {
let d0 = self.data[i].value();
let d1 = self.data[i + 1].value();
bytes.extend_from_slice(&[
(d0 >> 4) as u8,
(((d0 & 0x0F) << 4) | (d1 >> 8)) as u8,
(d1 & 0xFF) as u8,
]);
i += 2;
}
if i < len {
let d0 = self.data[i].value();
bytes.extend_from_slice(&[
(d0 >> 4) as u8,
((d0 & 0x0F) << 4) as u8,
]);
}
bytes
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
let mut data = Vec::new();
let mut i = 0;
while i + 2 < bytes.len() {
let d0 = ((bytes[i] as u32) << 4) | ((bytes[i + 1] as u32) >> 4);
let d1 = (((bytes[i + 1] as u32) & 0x0F) << 8) | (bytes[i + 2] as u32);
data.push(Dodecet::from_hex(d0 as u16));
data.push(Dodecet::from_hex(d1 as u16));
i += 3;
}
if i + 1 < bytes.len() {
let d0 = ((bytes[i] as u32) << 4) | ((bytes[i + 1] as u32) >> 4);
data.push(Dodecet::from_hex(d0 as u16));
} else if i < bytes.len() {
let d0 = (bytes[i] as u32) << 4;
data.push(Dodecet::from_hex(d0 as u16));
}
Ok(DodecetStringOptimized { data })
}
pub fn from_u16_batch(values: &[u16]) -> Self {
let mut data = Vec::with_capacity(values.len());
for &value in values {
data.push(Dodecet::from_hex(value));
}
DodecetStringOptimized { data }
}
pub fn memory_usage(&self) -> usize {
self.data.len() * 2 + 24 }
pub fn iter(&self) -> impl Iterator<Item = &Dodecet> {
self.data.iter()
}
pub fn as_inner(&self) -> &Vec<Dodecet> {
&self.data
}
pub fn as_inner_mut(&mut self) -> &mut Vec<Dodecet> {
&mut self.data
}
}
impl Default for DodecetStringOptimized {
fn default() -> Self {
Self::new()
}
}
impl Deref for DodecetStringOptimized {
type Target = [Dodecet];
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl DerefMut for DodecetStringOptimized {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
impl From<Vec<u16>> for DodecetStringOptimized {
fn from(values: Vec<u16>) -> Self {
DodecetStringOptimized::from_u16_batch(&values)
}
}
impl From<Vec<Dodecet>> for DodecetStringOptimized {
fn from(data: Vec<Dodecet>) -> Self {
DodecetStringOptimized { data }
}
}
impl std::fmt::Display for DodecetStringOptimized {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[")?;
for (i, d) in self.data.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", d)?;
}
write!(f, "]")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_optimized_to_hex_string() {
let s = DodecetStringOptimized::from_slice(&[0x123, 0x456, 0x789]);
assert_eq!(s.to_hex_string(), "123456789");
let s = DodecetStringOptimized::from_slice(&[0xABC; 1000]);
let hex = s.to_hex_string();
assert_eq!(hex.len(), 3000); assert_eq!(hex.capacity(), 3000); }
#[test]
fn test_optimized_to_hex_bytes() {
let s = DodecetStringOptimized::from_slice(&[0x123, 0x456, 0x789]);
assert_eq!(s.to_hex_bytes(), b"123456789");
}
#[test]
fn test_optimized_memory_usage() {
let s = DodecetStringOptimized::from_u16_batch(&[0; 50_000]);
let memory = s.memory_usage();
assert!(memory < 150_000); assert_eq!(memory, 50_000 * 2 + 24);
}
#[test]
fn test_optimized_bytes_conversion() {
let s = DodecetStringOptimized::from_slice(&[0x123, 0x456]);
let bytes = s.to_bytes();
assert_eq!(bytes.len(), 3);
let s2 = DodecetStringOptimized::from_bytes(&bytes).unwrap();
assert_eq!(s2.len(), 2);
assert_eq!(s2[0].value(), 0x123);
assert_eq!(s2[1].value(), 0x456);
}
#[test]
fn test_optimized_batch_conversion() {
let values: Vec<u16> = (0..1000).collect();
let s = DodecetStringOptimized::from_u16_batch(&values);
assert_eq!(s.len(), 1000);
assert_eq!(s[0].value(), 0);
assert_eq!(s[999].value(), 999);
}
#[test]
fn test_optimized_capacity() {
let s = DodecetStringOptimized::with_capacity(100);
assert!(s.capacity() >= 100);
assert_eq!(s.len(), 0);
}
#[test]
fn test_optimized_push_pop() {
let mut s = DodecetStringOptimized::new();
s.push(0x123);
s.push(0x456);
assert_eq!(s.len(), 2);
assert_eq!(s[0].value(), 0x123);
let d = s.pop().unwrap();
assert_eq!(d.value(), 0x456);
assert_eq!(s.len(), 1);
}
#[test]
fn test_optimized_display() {
let s = DodecetStringOptimized::from_slice(&[0x123, 0x456]);
assert_eq!(format!("{}", s), "[0x123, 0x456]");
}
#[bench]
fn bench_to_hex_string_50k(b: &mut test::Bencher) {
let s = DodecetStringOptimized::from_u16_batch(&[0; 50_000]);
b.iter(|| {
s.to_hex_string()
});
}
#[bench]
fn bench_to_hex_bytes_50k(b: &mut test::Bencher) {
let s = DodecetStringOptimized::from_u16_batch(&[0; 50_000]);
b.iter(|| {
s.to_hex_bytes()
});
}
}