Skip to main content

dodecet_encoder/
string.rs

1//! # DodecetString: Variable-length strings of dodecets
2//!
3//! Provides heap-allocated growable arrays for encoding sequences.
4
5use crate::{Dodecet, DodecetError, Result};
6use std::ops::{Deref, DerefMut};
7
8/// A growable vector of dodecets
9///
10/// # Example
11///
12/// ```rust
13/// use dodecet_encoder::DodecetString;
14///
15/// let mut s = DodecetString::new();
16/// s.push(0x123);
17/// s.push(0x456);
18/// assert_eq!(s.len(), 2);
19/// ```
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct DodecetString {
22    data: Vec<Dodecet>,
23}
24
25impl DodecetString {
26    /// Create a new empty dodecet string
27    ///
28    /// # Example
29    ///
30    /// ```rust
31    /// use dodecet_encoder::DodecetString;
32    ///
33    /// let s = DodecetString::new();
34    /// assert!(s.is_empty());
35    /// ```
36    pub fn new() -> Self {
37        DodecetString { data: Vec::new() }
38    }
39
40    /// Create with capacity
41    ///
42    /// # Example
43    ///
44    /// ```rust
45    /// use dodecet_encoder::DodecetString;
46    ///
47    /// let s = DodecetString::with_capacity(10);
48    /// assert!(s.as_inner().capacity() >= 10);
49    /// ```
50    pub fn with_capacity(capacity: usize) -> Self {
51        DodecetString {
52            data: Vec::with_capacity(capacity),
53        }
54    }
55
56    /// Get capacity
57    pub fn capacity(&self) -> usize {
58        self.data.capacity()
59    }
60
61    /// Create from a slice of u16 values
62    ///
63    /// # Example
64    ///
65    /// ```rust
66    /// use dodecet_encoder::DodecetString;
67    ///
68    /// let s = DodecetString::from_slice(&[0x123, 0x456, 0x789]);
69    /// assert_eq!(s.len(), 3);
70    /// ```
71    pub fn from_slice(values: &[u16]) -> Self {
72        DodecetString {
73            data: values.iter().map(|&v| Dodecet::from_hex(v)).collect(),
74        }
75    }
76
77    /// Create from dodecets
78    ///
79    /// # Example
80    ///
81    /// ```rust
82    /// use dodecet_encoder::{Dodecet, DodecetString};
83    ///
84    /// let s = DodecetString::from_dodecets(vec![
85    ///     Dodecet::from_hex(0x123),
86    ///     Dodecet::from_hex(0x456),
87    /// ]);
88    /// ```
89    pub fn from_dodecets(dodecets: Vec<Dodecet>) -> Self {
90        DodecetString { data: dodecets }
91    }
92
93    /// Push a dodecet value
94    ///
95    /// # Example
96    ///
97    /// ```rust
98    /// use dodecet_encoder::DodecetString;
99    ///
100    /// let mut s = DodecetString::new();
101    /// s.push(0xABC);
102    /// assert_eq!(s[0].value(), 0xABC);
103    /// ```
104    pub fn push(&mut self, value: u16) {
105        self.data.push(Dodecet::from_hex(value));
106    }
107
108    /// Push a dodecet
109    pub fn push_dodecet(&mut self, dodecet: Dodecet) {
110        self.data.push(dodecet);
111    }
112
113    /// Pop a dodecet
114    ///
115    /// # Example
116    ///
117    /// ```rust
118    /// use dodecet_encoder::DodecetString;
119    ///
120    /// let mut s = DodecetString::from_slice(&[0x123, 0x456]);
121    /// let d = s.pop().unwrap();
122    /// assert_eq!(d.value(), 0x456);
123    /// assert_eq!(s.len(), 1);
124    /// ```
125    pub fn pop(&mut self) -> Option<Dodecet> {
126        self.data.pop()
127    }
128
129    /// Convert to hex string
130    ///
131    /// # Example
132    ///
133    /// ```rust
134    /// use dodecet_encoder::DodecetString;
135    ///
136    /// let s = DodecetString::from_slice(&[0x123, 0x456, 0x789]);
137    /// assert_eq!(s.to_hex_string(), "123456789");
138    /// ```
139    pub fn to_hex_string(&self) -> String {
140        self.data
141            .iter()
142            .map(|d| d.to_hex_string())
143            .collect::<Vec<_>>()
144            .join("")
145    }
146
147    /// Parse from hex string
148    ///
149    /// # Example
150    ///
151    /// ```rust
152    /// use dodecet_encoder::DodecetString;
153    ///
154    /// let s = DodecetString::from_hex_str("123456789").unwrap();
155    /// assert_eq!(s.len(), 3);
156    /// assert_eq!(s[0].value(), 0x123);
157    /// ```
158    pub fn from_hex_str(s: &str) -> Result<Self> {
159        if !s.len().is_multiple_of(3) {
160            return Err(DodecetError::InvalidHex);
161        }
162
163        let mut data = Vec::with_capacity(s.len() / 3);
164
165        for chunk in s.as_bytes().chunks(3) {
166            let chunk_str = std::str::from_utf8(chunk).unwrap();
167            data.push(Dodecet::from_hex_str(chunk_str)?);
168        }
169
170        Ok(DodecetString { data })
171    }
172
173    /// Convert to bytes (lossy compression)
174    ///
175    /// Since 12 bits don't fit evenly in 8-bit bytes, this packs 2 dodecets into 3 bytes
176    ///
177    /// # Example
178    ///
179    /// ```rust
180    /// use dodecet_encoder::DodecetString;
181    ///
182    /// let s = DodecetString::from_slice(&[0x123, 0x456]);
183    /// let bytes = s.to_bytes();
184    /// assert_eq!(bytes.len(), 3); // 2 dodecets = 3 bytes
185    /// ```
186    pub fn to_bytes(&self) -> Vec<u8> {
187        let mut bytes = Vec::with_capacity((self.data.len() * 3).div_ceil(2));
188
189        for chunk in self.data.chunks(2) {
190            if chunk.len() == 2 {
191                let d0 = chunk[0].value() as u32;
192                let d1 = chunk[1].value() as u32;
193
194                // Pack 2 dodecets (24 bits) into 3 bytes
195                bytes.push(((d0 >> 4) & 0xFF) as u8);
196                bytes.push((((d0 & 0x0F) << 4) | ((d1 >> 8) & 0x0F)) as u8);
197                bytes.push((d1 & 0xFF) as u8);
198            } else if chunk.len() == 1 {
199                // Last odd dodecet
200                let d0 = chunk[0].value() as u32;
201                bytes.push(((d0 >> 4) & 0xFF) as u8);
202                bytes.push(((d0 & 0x0F) << 4) as u8);
203            }
204        }
205
206        bytes
207    }
208
209    /// Parse from bytes (unpacking)
210    ///
211    /// # Example
212    ///
213    /// ```rust
214    /// use dodecet_encoder::DodecetString;
215    ///
216    /// let bytes = vec![0x12, 0x34, 0x56];
217    /// let s = DodecetString::from_bytes(&bytes).unwrap();
218    /// assert_eq!(s.len(), 2);
219    /// ```
220    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
221        let mut data = Vec::new();
222
223        let mut i = 0;
224        while i + 2 < bytes.len() {
225            let d0 = ((bytes[i] as u32) << 4) | ((bytes[i + 1] as u32) >> 4);
226            let d1 = (((bytes[i + 1] as u32) & 0x0F) << 8) | (bytes[i + 2] as u32);
227
228            data.push(Dodecet::from_hex(d0 as u16));
229            data.push(Dodecet::from_hex(d1 as u16));
230
231            i += 3;
232        }
233
234        // Handle remaining bytes
235        if i + 1 < bytes.len() {
236            let d0 = ((bytes[i] as u32) << 4) | ((bytes[i + 1] as u32) >> 4);
237            data.push(Dodecet::from_hex(d0 as u16));
238        } else if i < bytes.len() {
239            let d0 = (bytes[i] as u32) << 4;
240            data.push(Dodecet::from_hex(d0 as u16));
241        }
242
243        Ok(DodecetString { data })
244    }
245
246    /// Iterate over dodecets
247    pub fn iter(&self) -> impl Iterator<Item = &Dodecet> {
248        self.data.iter()
249    }
250
251    /// Get inner vector
252    pub fn as_inner(&self) -> &Vec<Dodecet> {
253        &self.data
254    }
255
256    /// Get mutable inner vector
257    pub fn as_inner_mut(&mut self) -> &mut Vec<Dodecet> {
258        &mut self.data
259    }
260}
261
262impl Default for DodecetString {
263    fn default() -> Self {
264        Self::new()
265    }
266}
267
268impl Deref for DodecetString {
269    type Target = [Dodecet];
270
271    fn deref(&self) -> &Self::Target {
272        &self.data
273    }
274}
275
276impl DerefMut for DodecetString {
277    fn deref_mut(&mut self) -> &mut Self::Target {
278        &mut self.data
279    }
280}
281
282impl From<Vec<u16>> for DodecetString {
283    fn from(values: Vec<u16>) -> Self {
284        DodecetString::from_slice(&values)
285    }
286}
287
288impl From<Vec<Dodecet>> for DodecetString {
289    fn from(data: Vec<Dodecet>) -> Self {
290        DodecetString { data }
291    }
292}
293
294impl std::fmt::Display for DodecetString {
295    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296        write!(f, "[")?;
297        for (i, d) in self.data.iter().enumerate() {
298            if i > 0 {
299                write!(f, ", ")?;
300            }
301            write!(f, "{}", d)?;
302        }
303        write!(f, "]")
304    }
305}
306
307#[cfg(test)]
308mod tests {
309    use super::*;
310
311    #[test]
312    fn test_creation() {
313        let s = DodecetString::new();
314        assert!(s.is_empty());
315
316        let s = DodecetString::with_capacity(10);
317        assert!(s.as_inner().capacity() >= 10);
318    }
319
320    #[test]
321    fn test_push_pop() {
322        let mut s = DodecetString::new();
323        s.push(0x123);
324        s.push(0x456);
325
326        assert_eq!(s.len(), 2);
327        assert_eq!(s[0].value(), 0x123);
328        assert_eq!(s[1].value(), 0x456);
329
330        let d = s.pop().unwrap();
331        assert_eq!(d.value(), 0x456);
332        assert_eq!(s.len(), 1);
333    }
334
335    #[test]
336    fn test_from_slice() {
337        let s = DodecetString::from_slice(&[0x123, 0x456, 0x789]);
338        assert_eq!(s.len(), 3);
339        assert_eq!(s[0].value(), 0x123);
340        assert_eq!(s[1].value(), 0x456);
341        assert_eq!(s[2].value(), 0x789);
342    }
343
344    #[test]
345    fn test_hex_string() {
346        let s = DodecetString::from_slice(&[0x123, 0x456, 0x789]);
347        assert_eq!(s.to_hex_string(), "123456789");
348
349        let s2 = DodecetString::from_hex_str("123456789").unwrap();
350        assert_eq!(s2.len(), 3);
351        assert_eq!(s2[0].value(), 0x123);
352    }
353
354    #[test]
355    fn test_bytes_conversion() {
356        let s = DodecetString::from_slice(&[0x123, 0x456]);
357        let bytes = s.to_bytes();
358
359        // 2 dodecets = 24 bits = 3 bytes
360        assert_eq!(bytes.len(), 3);
361
362        let s2 = DodecetString::from_bytes(&bytes).unwrap();
363        assert_eq!(s2.len(), 2);
364        assert_eq!(s2[0].value(), 0x123);
365        assert_eq!(s2[1].value(), 0x456);
366    }
367
368    #[test]
369    fn test_bytes_odd_length() {
370        let s = DodecetString::from_slice(&[0x123, 0x456, 0x789]);
371        let bytes = s.to_bytes();
372
373        // 3 dodecets = 36 bits = 5 bytes (with padding)
374        assert!(bytes.len() <= 5);
375
376        let s2 = DodecetString::from_bytes(&bytes).unwrap();
377        assert_eq!(s2.len(), 3);
378    }
379
380    #[test]
381    fn test_display() {
382        let s = DodecetString::from_slice(&[0x123, 0x456]);
383        assert_eq!(format!("{}", s), "[0x123, 0x456]");
384    }
385}