zigzag_rs/
lib.rs

1#![no_std]
2
3//! # zigzag-rs
4//!
5//! A dependency-free (including no std) ZigZag encoding/decoding Rust library.
6//! ZigZag encoding is a method for mapping signed integers to unsigned integers,
7//! commonly used in variable-length encoding and data compression.
8//!
9//! ## Features
10//!
11//! - Completely dependency-free, usable in `#![no_std]` environments
12//! - Supports all Rust native signed integer types (i8, i16, i32, i64, i128)
13//! - Simple and easy-to-use API with both single value and batch processing
14//! - Iterator-based API for memory-constrained environments
15//! - Efficient implementation optimized for embedded systems
16//! - Error handling with Result types for robust application development
17//!
18//! ## Usage
19//!
20//! Add the dependency to your `Cargo.toml`:
21//!
22//! ```toml
23//! [dependencies]
24//! zigzag-rs = "0.2.0"
25//! ```
26//!
27//! Example code:
28//!
29//! ```rust
30//! use zigzag_rs::ZigZag;
31//!
32//! fn main() {
33//!     // Single value encoding/decoding
34//!     let encoded = i32::zigzag_encode(-1);
35//!     assert_eq!(encoded, 1u32);
36//!     
37//!     let decoded = i32::zigzag_decode(1u32);
38//!     assert_eq!(decoded, -1i32);
39//!     
40//!     // Batch processing
41//!     let values = [-10, -1, 0, 1, 10];
42//!     let mut encoded = [0u32; 5];
43//!     i32::zigzag_encode_slice(&values, &mut encoded);
44//!     
45//!     let mut decoded = [0i32; 5];
46//!     i32::zigzag_decode_slice(&encoded, &mut decoded);
47//!     
48//!     assert_eq!(values, decoded);
49//!     
50//!     // Using Result-based error handling
51//!     let values = [-10, -1, 0, 1, 10];
52//!     let mut encoded = [0u32; 5];
53//!     let result = i32::try_zigzag_encode_slice(&values, &mut encoded);
54//!     assert!(result.is_ok());
55//!     
56//!     // Using iterator-based API
57//!     let values = [-10, -1, 0, 1, 10];
58//!     // Encode each value on the fly without allocating a buffer
59//!     let encoded_iter = zigzag_rs::zigzag_encode_iter::<i32, _>(values.iter());
60//!     
61//!     // The values are encoded only when the iterator is consumed
62//!     for (original, encoded) in values.iter().zip(encoded_iter) {
63//!         assert_eq!(encoded, i32::zigzag_encode(*original));
64//!     }
65//! }
66//! ```
67//!
68//! ## ZigZag Encoding Principle
69//!
70//! ZigZag encoding maps signed integers to unsigned integers as follows:
71//! - 0 -> 0
72//! - -1 -> 1
73//! - 1 -> 2
74//! - -2 -> 3
75//! - 2 -> 4
76//! ...
77//!
78//! This encoding method ensures that small absolute values (whether positive or negative)
79//! are mapped to small unsigned integers, which is ideal for subsequent variable-length encoding.
80
81/// Error type for ZigZag operations
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum ZigZagError {
84    /// Output buffer is too small to hold all converted values
85    BufferTooSmall {
86        /// Number of elements needed
87        needed: usize,
88        /// Actual buffer size
89        actual: usize,
90    },
91}
92
93/// Trait for ZigZag encoding, used to convert signed integers to unsigned integers
94pub trait ZigZag {
95    /// The corresponding unsigned type
96    type UInt;
97    
98    /// Encode a signed integer to an unsigned integer
99    fn zigzag_encode(value: Self) -> Self::UInt;
100    
101    /// Decode an unsigned integer back to a signed integer
102    fn zigzag_decode(value: Self::UInt) -> Self;
103    
104    /// Encode a slice of signed integers to unsigned integers
105    /// 
106    /// # Arguments
107    /// * `values` - Slice of signed integers to encode
108    /// * `out` - Output slice to store encoded unsigned integers
109    /// 
110    /// # Panics
111    /// Panics if `out` is smaller than `values` 
112    fn zigzag_encode_slice(values: &[Self], out: &mut [Self::UInt]) 
113    where 
114        Self: Sized + Copy
115    {
116        assert!(out.len() >= values.len(), "Output slice must be at least as large as input slice");
117        for (i, &value) in values.iter().enumerate() {
118            out[i] = Self::zigzag_encode(value);
119        }
120    }
121    
122    /// Decode a slice of unsigned integers back to signed integers
123    /// 
124    /// # Arguments
125    /// * `values` - Slice of unsigned integers to decode
126    /// * `out` - Output slice to store decoded signed integers
127    /// 
128    /// # Panics
129    /// Panics if `out` is smaller than `values`
130    fn zigzag_decode_slice(values: &[Self::UInt], out: &mut [Self]) 
131    where 
132        Self: Sized + Copy,
133        Self::UInt: Copy
134    {
135        assert!(out.len() >= values.len(), "Output slice must be at least as large as input slice");
136        for (i, &value) in values.iter().enumerate() {
137            out[i] = Self::zigzag_decode(value);
138        }
139    }
140    
141    /// Try to encode a slice of signed integers to unsigned integers, returning
142    /// a Result instead of panicking if the output buffer is too small
143    /// 
144    /// # Arguments
145    /// * `values` - Slice of signed integers to encode
146    /// * `out` - Output slice to store encoded unsigned integers
147    /// 
148    /// # Returns
149    /// * `Ok(())` if all values were encoded successfully
150    /// * `Err(ZigZagError::BufferTooSmall)` if output buffer is too small
151    fn try_zigzag_encode_slice(values: &[Self], out: &mut [Self::UInt]) -> Result<(), ZigZagError> 
152    where 
153        Self: Sized + Copy
154    {
155        if out.len() < values.len() {
156            return Err(ZigZagError::BufferTooSmall { 
157                needed: values.len(), 
158                actual: out.len(),
159            });
160        }
161        
162        for (i, &value) in values.iter().enumerate() {
163            out[i] = Self::zigzag_encode(value);
164        }
165        
166        Ok(())
167    }
168    
169    /// Try to decode a slice of unsigned integers back to signed integers, returning
170    /// a Result instead of panicking if the output buffer is too small
171    /// 
172    /// # Arguments
173    /// * `values` - Slice of unsigned integers to decode
174    /// * `out` - Output slice to store decoded signed integers
175    /// 
176    /// # Returns
177    /// * `Ok(())` if all values were decoded successfully
178    /// * `Err(ZigZagError::BufferTooSmall)` if output buffer is too small
179    fn try_zigzag_decode_slice(values: &[Self::UInt], out: &mut [Self]) -> Result<(), ZigZagError> 
180    where 
181        Self: Sized + Copy,
182        Self::UInt: Copy
183    {
184        if out.len() < values.len() {
185            return Err(ZigZagError::BufferTooSmall { 
186                needed: values.len(), 
187                actual: out.len(),
188            });
189        }
190        
191        for (i, &value) in values.iter().enumerate() {
192            out[i] = Self::zigzag_decode(value);
193        }
194        
195        Ok(())
196    }
197}
198
199/// Creates an iterator that encodes each signed integer from the source iterator.
200///
201/// This function provides an iterator-based API for ZigZag encoding. The values are encoded
202/// on-the-fly as the iterator is consumed, without requiring an intermediate buffer.
203///
204/// # Arguments
205/// * `iter` - An iterator that yields references to signed integers
206///
207/// # Returns
208/// An iterator that yields encoded unsigned integers
209///
210/// # Example
211/// ```
212/// use zigzag_rs::{ZigZag, zigzag_encode_iter};
213///
214/// let values = [-10, -1, 0, 1, 10];
215/// let encoded_iter = zigzag_encode_iter::<i32, _>(values.iter());
216///
217/// for (original, encoded) in values.iter().zip(encoded_iter) {
218///     assert_eq!(encoded, i32::zigzag_encode(*original));
219/// }
220/// ```
221///
222/// # Advanced Example
223/// ```
224/// use zigzag_rs::{ZigZag, zigzag_encode_iter};
225///
226/// // Filtering and encoding in one pass
227/// let values = [-100, -10, -1, 0, 1, 10, 100];
228/// 
229/// // Process only positive numbers
230/// let positive_encoded: Vec<u32> = values.iter()
231///     .filter(|&&v| v > 0)
232///     .map(|&v| i32::zigzag_encode(v))
233///     .collect();
234///     
235/// assert_eq!(positive_encoded, vec![2, 20, 200]);
236///
237/// // Alternative approach using zigzag_encode_iter
238/// let positive_encoded2: Vec<u32> = zigzag_encode_iter::<i32, _>(
239///     values.iter().filter(|&&v| v > 0)
240/// ).collect();
241///
242/// assert_eq!(positive_encoded2, vec![2, 20, 200]);
243/// ```
244pub fn zigzag_encode_iter<'a, T, I>(iter: I) -> impl Iterator<Item = T::UInt> + 'a
245where
246    T: ZigZag + Copy + 'a,
247    I: Iterator<Item = &'a T> + 'a,
248{
249    iter.map(|&value| T::zigzag_encode(value))
250}
251
252/// Creates an iterator that decodes each unsigned integer from the source iterator.
253///
254/// This function provides an iterator-based API for ZigZag decoding. The values are decoded
255/// on-the-fly as the iterator is consumed, without requiring an intermediate buffer.
256///
257/// # Arguments
258/// * `iter` - An iterator that yields references to unsigned integers
259///
260/// # Returns
261/// An iterator that yields decoded signed integers
262///
263/// # Example
264/// ```
265/// use zigzag_rs::{ZigZag, zigzag_decode_iter};
266///
267/// let encoded = [1u32, 0, 2, 3, 20];
268/// let decoded_iter = zigzag_decode_iter::<i32, _>(encoded.iter());
269///
270/// let expected = [-1, 0, 1, -2, 10];
271/// for (expected, decoded) in expected.iter().zip(decoded_iter) {
272///     assert_eq!(*expected, decoded);
273/// }
274/// ```
275///
276/// # Chaining Example
277/// ```
278/// use zigzag_rs::{ZigZag, zigzag_encode_iter, zigzag_decode_iter};
279///
280/// // Encode, then immediately decode without creating intermediate storage
281/// let values = [-10, -1, 0, 1, 10];
282/// let encoded: Vec<u32> = zigzag_encode_iter::<i32, _>(values.iter()).collect();
283/// 
284/// // We can decode directly from the encoded values
285/// let decoded: Vec<i32> = zigzag_decode_iter::<i32, _>(encoded.iter()).collect();
286/// 
287/// // Verify values are preserved
288/// assert_eq!(values.to_vec(), decoded);
289/// ```
290pub fn zigzag_decode_iter<'a, T, I>(iter: I) -> impl Iterator<Item = T> + 'a
291where
292    T: ZigZag + Copy + 'a,
293    I: Iterator<Item = &'a T::UInt> + 'a,
294    T::UInt: Copy + 'a,
295{
296    iter.map(|&value| T::zigzag_decode(value))
297}
298
299macro_rules! impl_zigzag {
300    ($signed:ty, $unsigned:ty, $bits:expr) => {
301        impl ZigZag for $signed {
302            type UInt = $unsigned;
303            
304            #[inline]
305            fn zigzag_encode(value: Self) -> Self::UInt {
306                // Left shift by one bit, then XOR with arithmetic right shift result
307                ((value << 1) ^ (value >> ($bits - 1))) as $unsigned
308            }
309            
310            #[inline]
311            fn zigzag_decode(value: Self::UInt) -> Self {
312                // Optimized version: combine right shift, negation and XOR in one expression
313                ((value >> 1) as Self) ^ (-((value & 1) as Self))
314            }
315        }
316    };
317}
318
319// Implement ZigZag trait for various integer types
320impl_zigzag!(i8, u8, 8);
321impl_zigzag!(i16, u16, 16);
322impl_zigzag!(i32, u32, 32);
323impl_zigzag!(i64, u64, 64);
324impl_zigzag!(i128, u128, 128);
325
326#[cfg(test)]
327extern crate std;
328
329#[cfg(test)]
330mod tests {
331    use super::*;
332    
333    #[cfg(test)]
334    use std::vec::Vec;
335    
336    #[test]
337    fn test_encode_decode_i32() {
338        // Test specific values
339        assert_eq!(i32::zigzag_encode(0), 0u32);
340        assert_eq!(i32::zigzag_encode(-1), 1u32);
341        assert_eq!(i32::zigzag_encode(1), 2u32);
342        assert_eq!(i32::zigzag_encode(-2), 3u32);
343        
344        // Test boundary values
345        assert_eq!(i32::zigzag_encode(i32::MAX), 4294967294u32);
346        assert_eq!(i32::zigzag_encode(i32::MIN), 4294967295u32);
347        
348        // Test round-trip conversion
349        for i in [-100, -10, -1, 0, 1, 10, 100].iter() {
350            let encoded = i32::zigzag_encode(*i);
351            let decoded = i32::zigzag_decode(encoded);
352            assert_eq!(*i, decoded);
353        }
354    }
355    
356    #[test]
357    fn test_encode_decode_slice_i32() {
358        let values = [-100i32, -10, -1, 0, 1, 10, 100];
359        let mut encoded = [0u32; 7];
360        let mut decoded = [0i32; 7];
361        
362        // Test encoding slice
363        i32::zigzag_encode_slice(&values, &mut encoded);
364        assert_eq!(encoded[0], i32::zigzag_encode(-100));
365        assert_eq!(encoded[3], i32::zigzag_encode(0));
366        assert_eq!(encoded[6], i32::zigzag_encode(100));
367        
368        // Test decoding slice
369        i32::zigzag_decode_slice(&encoded, &mut decoded);
370        assert_eq!(values, decoded);
371    }
372    
373    #[test]
374    fn test_try_encode_decode_slice() {
375        let values = [-100i32, -10, -1, 0, 1, 10, 100];
376        
377        // Test with correct buffer size
378        let mut encoded = [0u32; 7];
379        let result = i32::try_zigzag_encode_slice(&values, &mut encoded);
380        assert!(result.is_ok());
381        
382        let mut decoded = [0i32; 7];
383        let result = i32::try_zigzag_decode_slice(&encoded, &mut decoded);
384        assert!(result.is_ok());
385        assert_eq!(values, decoded);
386        
387        // Test with too small buffer
388        let mut small_encoded = [0u32; 3];
389        let result = i32::try_zigzag_encode_slice(&values, &mut small_encoded);
390        assert!(result.is_err());
391        if let Err(ZigZagError::BufferTooSmall { needed, actual }) = result {
392            assert_eq!(needed, 7);
393            assert_eq!(actual, 3);
394        } else {
395            panic!("Expected BufferTooSmall error");
396        }
397        
398        let mut small_decoded = [0i32; 3];
399        let result = i32::try_zigzag_decode_slice(&encoded, &mut small_decoded);
400        assert!(result.is_err());
401    }
402    
403    #[test]
404    fn test_encode_decode_i8() {
405        // Test round-trip conversion for i8 type
406        for i in i8::MIN..=i8::MAX {
407            let encoded = i8::zigzag_encode(i);
408            let decoded = i8::zigzag_decode(encoded);
409            assert_eq!(i, decoded);
410        }
411    }
412    
413    #[test]
414    fn test_encode_decode_i16() {
415        // Test some i16 values
416        for i in [-1000, -100, -1, 0, 1, 100, 1000].iter() {
417            let encoded = i16::zigzag_encode(*i);
418            let decoded = i16::zigzag_decode(encoded);
419            assert_eq!(*i, decoded);
420        }
421    }
422    
423    #[test]
424    fn test_encode_decode_slice_all_types() {
425        // Test i8
426        let i8_values = [-100i8, -10, -1, 0, 1, 10, 100];
427        let mut i8_encoded = [0u8; 7];
428        let mut i8_decoded = [0i8; 7];
429        i8::zigzag_encode_slice(&i8_values, &mut i8_encoded);
430        i8::zigzag_decode_slice(&i8_encoded, &mut i8_decoded);
431        assert_eq!(i8_values, i8_decoded);
432        
433        // Test i16
434        let i16_values = [-1000i16, -100, -10, 0, 10, 100, 1000];
435        let mut i16_encoded = [0u16; 7];
436        let mut i16_decoded = [0i16; 7];
437        i16::zigzag_encode_slice(&i16_values, &mut i16_encoded);
438        i16::zigzag_decode_slice(&i16_encoded, &mut i16_decoded);
439        assert_eq!(i16_values, i16_decoded);
440        
441        // Test i64
442        let i64_values = [-1000000i64, -10000, -100, 0, 100, 10000, 1000000];
443        let mut i64_encoded = [0u64; 7];
444        let mut i64_decoded = [0i64; 7];
445        i64::zigzag_encode_slice(&i64_values, &mut i64_encoded);
446        i64::zigzag_decode_slice(&i64_encoded, &mut i64_decoded);
447        assert_eq!(i64_values, i64_decoded);
448    }
449    
450    #[test]
451    fn test_zigzag_error() {
452        // Just test that we can create the error type and access its fields
453        let error = ZigZagError::BufferTooSmall { needed: 10, actual: 5 };
454        assert_eq!(error.needed(), 10);
455        assert_eq!(error.actual(), 5);
456    }
457    
458    #[test]
459    fn test_zigzag_encode_iter() {
460        let values = [-100i32, -10, -1, 0, 1, 10, 100];
461        
462        // Convert to a Vec to compare
463        let expected: Vec<u32> = values.iter()
464            .map(|&v| i32::zigzag_encode(v))
465            .collect();
466        
467        // Use the iterator-based method
468        let encoded: Vec<u32> = zigzag_encode_iter::<i32, _>(values.iter()).collect();
469        
470        assert_eq!(encoded, expected);
471        
472        // Test with different integer types
473        let i8_values = [-100i8, -10, -1, 0, 1, 10, 100];
474        let i8_encoded: Vec<u8> = zigzag_encode_iter::<i8, _>(i8_values.iter()).collect();
475        
476        for (i, &val) in i8_values.iter().enumerate() {
477            assert_eq!(i8_encoded[i], i8::zigzag_encode(val));
478        }
479    }
480    
481    #[test]
482    fn test_zigzag_decode_iter() {
483        let encoded = [199u32, 19, 1, 0, 2, 20, 200];
484        let expected = [-100i32, -10, -1, 0, 1, 10, 100];
485        
486        // Use the iterator-based method
487        let decoded: Vec<i32> = zigzag_decode_iter::<i32, _>(encoded.iter()).collect();
488        
489        assert_eq!(decoded, expected);
490        
491        // Test with different integer types
492        let i16_encoded = [1999u16, 199, 19, 1, 0, 2, 20, 200, 2000];
493        let i16_expected = [-1000i16, -100, -10, -1, 0, 1, 10, 100, 1000];
494        
495        let i16_decoded: Vec<i16> = zigzag_decode_iter::<i16, _>(i16_encoded.iter()).collect();
496        assert_eq!(i16_decoded, i16_expected);
497    }
498    
499    #[test]
500    fn test_iterator_based_round_trip() {
501        let original = [-1000i16, -100, -10, -1, 0, 1, 10, 100, 1000];
502        
503        // Encode using iterator
504        let encoded: Vec<u16> = zigzag_encode_iter::<i16, _>(original.iter()).collect();
505        
506        // Decode using iterator
507        let decoded: Vec<i16> = zigzag_decode_iter::<i16, _>(encoded.iter()).collect();
508        
509        // Verify round-trip
510        assert_eq!(original.to_vec(), decoded);
511    }
512}
513
514// Add methods to ZigZagError to access fields without requiring std
515impl ZigZagError {
516    /// Get the needed buffer size
517    pub fn needed(&self) -> usize {
518        match self {
519            ZigZagError::BufferTooSmall { needed, .. } => *needed,
520        }
521    }
522    
523    /// Get the actual buffer size
524    pub fn actual(&self) -> usize {
525        match self {
526            ZigZagError::BufferTooSmall { actual, .. } => *actual,
527        }
528    }
529}