wire_codec/zigzag.rs
1//! Zigzag encoding for signed integers.
2//!
3//! Zigzag maps signed integers to unsigned ones so that small-magnitude negative
4//! values produce small-magnitude varints. Pair these helpers with the
5//! [`varint`][`crate::varint`] functions to encode signed values compactly.
6//!
7//! The mapping mirrors the one used by Protocol Buffers:
8//!
9//! | signed | unsigned |
10//! | ------ | -------- |
11//! | 0 | 0 |
12//! | -1 | 1 |
13//! | 1 | 2 |
14//! | -2 | 3 |
15//! | 2 | 4 |
16
17/// Map a signed `i32` to its zigzag-encoded `u32`.
18///
19/// # Example
20///
21/// ```
22/// use wire_codec::zigzag;
23/// assert_eq!(zigzag::encode_i32(0), 0);
24/// assert_eq!(zigzag::encode_i32(-1), 1);
25/// assert_eq!(zigzag::encode_i32(1), 2);
26/// assert_eq!(zigzag::encode_i32(i32::MIN), u32::MAX);
27/// ```
28#[inline]
29pub const fn encode_i32(value: i32) -> u32 {
30 ((value << 1) ^ (value >> 31)) as u32
31}
32
33/// Recover the signed `i32` from a zigzag-encoded `u32`.
34///
35/// # Example
36///
37/// ```
38/// use wire_codec::zigzag;
39/// assert_eq!(zigzag::decode_i32(0), 0);
40/// assert_eq!(zigzag::decode_i32(1), -1);
41/// assert_eq!(zigzag::decode_i32(u32::MAX), i32::MIN);
42/// ```
43#[inline]
44pub const fn decode_i32(value: u32) -> i32 {
45 ((value >> 1) as i32) ^ -((value & 1) as i32)
46}
47
48/// Map a signed `i64` to its zigzag-encoded `u64`.
49///
50/// # Example
51///
52/// ```
53/// use wire_codec::zigzag;
54/// assert_eq!(zigzag::encode_i64(i64::MIN), u64::MAX);
55/// ```
56#[inline]
57pub const fn encode_i64(value: i64) -> u64 {
58 ((value << 1) ^ (value >> 63)) as u64
59}
60
61/// Recover the signed `i64` from a zigzag-encoded `u64`.
62#[inline]
63pub const fn decode_i64(value: u64) -> i64 {
64 ((value >> 1) as i64) ^ -((value & 1) as i64)
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn i32_round_trip() {
73 for v in [0i32, 1, -1, 2, -2, 100, -100, i32::MAX, i32::MIN] {
74 assert_eq!(decode_i32(encode_i32(v)), v);
75 }
76 }
77
78 #[test]
79 fn i64_round_trip() {
80 for v in [0i64, 1, -1, i64::MAX, i64::MIN, 1 << 40, -(1 << 40)] {
81 assert_eq!(decode_i64(encode_i64(v)), v);
82 }
83 }
84}