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
14//! - Efficient implementation
15//!
16//! ## Usage
17//!
18//! Add the dependency to your `Cargo.toml`:
19//!
20//! ```toml
21//! [dependencies]
22//! zigzag-rs = "0.1.0"
23//! ```
24//!
25//! Example code:
26//!
27//! ```rust
28//! use zigzag_rs::ZigZag;
29//!
30//! fn main() {
31//!     // Encoding
32//!     let encoded = i32::zigzag_encode(-1);
33//!     assert_eq!(encoded, 1u32);
34//!     
35//!     // Decoding
36//!     let decoded = i32::zigzag_decode(1u32);
37//!     assert_eq!(decoded, -1i32);
38//! }
39//! ```
40//!
41//! ## ZigZag Encoding Principle
42//!
43//! ZigZag encoding maps signed integers to unsigned integers as follows:
44//! - 0 -> 0
45//! - -1 -> 1
46//! - 1 -> 2
47//! - -2 -> 3
48//! - 2 -> 4
49//! ...
50//!
51//! This encoding method ensures that small absolute values (whether positive or negative)
52//! are mapped to small unsigned integers, which is ideal for subsequent variable-length encoding.
53/// Trait for ZigZag encoding, used to convert signed integers to unsigned integers
54pub trait ZigZag {
55    /// The corresponding unsigned type
56    type UInt;
57    
58    /// Encode a signed integer to an unsigned integer
59    fn zigzag_encode(value: Self) -> Self::UInt;
60    
61    /// Decode an unsigned integer back to a signed integer
62    fn zigzag_decode(value: Self::UInt) -> Self;
63}
64
65macro_rules! impl_zigzag {
66    ($signed:ty, $unsigned:ty, $bits:expr) => {
67        impl ZigZag for $signed {
68            type UInt = $unsigned;
69            
70            #[inline]
71            fn zigzag_encode(value: Self) -> Self::UInt {
72                // Left shift by one bit, then XOR with arithmetic right shift result
73                ((value << 1) ^ (value >> ($bits - 1))) as $unsigned
74            }
75            
76            #[inline]
77            fn zigzag_decode(value: Self::UInt) -> Self {
78                // Right shift by one bit
79                let shr1 = (value >> 1) as $signed;
80                // Lowest bit is 1 for negative, 0 for positive
81                let neg_mask = -((value & 1) as $signed);
82                // XOR operation to restore the original value
83                shr1 ^ neg_mask
84            }
85        }
86    };
87}
88
89// Implement ZigZag trait for various integer types
90impl_zigzag!(i8, u8, 8);
91impl_zigzag!(i16, u16, 16);
92impl_zigzag!(i32, u32, 32);
93impl_zigzag!(i64, u64, 64);
94impl_zigzag!(i128, u128, 128);
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99    
100    #[test]
101    fn test_encode_decode_i32() {
102        // Test specific values
103        assert_eq!(i32::zigzag_encode(0), 0u32);
104        assert_eq!(i32::zigzag_encode(-1), 1u32);
105        assert_eq!(i32::zigzag_encode(1), 2u32);
106        assert_eq!(i32::zigzag_encode(-2), 3u32);
107        
108        // Test boundary values
109        assert_eq!(i32::zigzag_encode(i32::MAX), 4294967294u32);
110        assert_eq!(i32::zigzag_encode(i32::MIN), 4294967295u32);
111        
112        // Test round-trip conversion
113        for i in [-100, -10, -1, 0, 1, 10, 100].iter() {
114            let encoded = i32::zigzag_encode(*i);
115            let decoded = i32::zigzag_decode(encoded);
116            assert_eq!(*i, decoded);
117        }
118    }
119    
120    #[test]
121    fn test_encode_decode_i8() {
122        // Test round-trip conversion for i8 type
123        for i in i8::MIN..=i8::MAX {
124            let encoded = i8::zigzag_encode(i);
125            let decoded = i8::zigzag_decode(encoded);
126            assert_eq!(i, decoded);
127        }
128    }
129    
130    #[test]
131    fn test_encode_decode_i16() {
132        // Test some i16 values
133        for i in [-1000, -100, -1, 0, 1, 100, 1000].iter() {
134            let encoded = i16::zigzag_encode(*i);
135            let decoded = i16::zigzag_decode(encoded);
136            assert_eq!(*i, decoded);
137        }
138    }
139}