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}