sigma_ser/
zig_zag_encode.rs

1#[cfg(test)]
2use proptest::{num::i32, num::i64, prelude::*};
3
4/// Encode a 32-bit value with ZigZag. ZigZag encodes signed integers
5/// into values that can be efficiently encoded with VLQ. (Otherwise,
6/// negative values must be sign-extended to 64 bits to be varint encoded,
7/// thus always taking 10 bytes on the wire.)
8/// see <https://developers.google.com/protocol-buffers/docs/encoding#types>
9///
10/// Although result should be of u32 we need to use u64 due to the signed Int
11/// used for result in Scala version
12pub fn encode_i32(v: i32) -> u64 {
13    // Note:  the right-shift must be arithmetic
14    // source: http://github.com/google/protobuf/blob/a7252bf42df8f0841cf3a0c85fdbf1a5172adecb/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java#L934
15    ((v << 1) ^ (v >> 31)) as u64
16}
17
18/// Decode a signed value previously ZigZag-encoded with [`encode_i32`]
19/// see <https://developers.google.com/protocol-buffers/docs/encoding#types>
20pub fn decode_u32(v: u64) -> i32 {
21    // source: http://github.com/google/protobuf/blob/a7252bf42df8f0841cf3a0c85fdbf1a5172adecb/java/core/src/main/java/com/google/protobuf/CodedInputStream.java#L553
22    (v as u32 >> 1) as i32 ^ -(v as i32 & 1)
23}
24
25/// Encode a 64-bit value with ZigZag. ZigZag encodes signed integers
26/// into values that can be efficiently encoded with varint. (Otherwise,
27/// negative values must be sign-extended to 64 bits to be varint encoded,
28/// thus always taking 10 bytes on the wire.)
29/// see <https://developers.google.com/protocol-buffers/docs/encoding#types>
30pub fn encode_i64(v: i64) -> u64 {
31    // source: http://github.com/google/protobuf/blob/a7252bf42df8f0841cf3a0c85fdbf1a5172adecb/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java#L949
32    ((v << 1) ^ (v >> 63)) as u64
33}
34
35/// Decode a signed value previously ZigZag-encoded with [`encode_i64`]
36/// see <https://developers.google.com/protocol-buffers/docs/encoding#types>
37pub fn decode_u64(v: u64) -> i64 {
38    // source: http://github.com/google/protobuf/blob/a7252bf42df8f0841cf3a0c85fdbf1a5172adecb/java/core/src/main/java/com/google/protobuf/CodedInputStream.java#L566
39    ((v >> 1) ^ (-((v & 1) as i64)) as u64) as i64
40}
41#[cfg(test)]
42#[allow(clippy::panic)]
43mod tests {
44    use super::*;
45
46    #[allow(overflowing_literals)]
47    #[test]
48    fn test_expected_values() {
49        // source: http://github.com/google/protobuf/blob/a7252bf42df8f0841cf3a0c85fdbf1a5172adecb/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java#L281
50        assert_eq!(0, encode_i32(0));
51        assert_eq!(1, encode_i32(-1));
52        assert_eq!(2, encode_i32(1));
53        assert_eq!(3, encode_i32(-2));
54        assert_eq!(0x7FFF_FFFE, encode_i32(0x3FFF_FFFF));
55        assert_eq!(0x7FFF_FFFF, encode_i32(0xC000_0000));
56        assert_eq!(0xFFFF_FFFE, encode_i32(0x7FFF_FFFF) as i32);
57        assert_eq!(0xFFFF_FFFF, encode_i32(0x8000_0000) as i32);
58
59        assert_eq!(0, encode_i64(0));
60        assert_eq!(1, encode_i64(-1));
61        assert_eq!(2, encode_i64(1));
62        assert_eq!(3, encode_i64(-2));
63        assert_eq!(0x0000_0000_7FFF_FFFE, encode_i64(0x0000_0000_3FFF_FFFF));
64        assert_eq!(0x0000_0000_7FFF_FFFF, encode_i64(0xFFFF_FFFF_C000_0000));
65        assert_eq!(0x0000_0000_FFFF_FFFE, encode_i64(0x0000_0000_7FFF_FFFF));
66        assert_eq!(0x0000_0000_FFFF_FFFF, encode_i64(0xFFFF_FFFF_8000_0000));
67        assert_eq!(0xFFFF_FFFF_FFFF_FFFE, encode_i64(0x7FFF_FFFF_FFFF_FFFF));
68        assert_eq!(0xFFFF_FFFF_FFFF_FFFF, encode_i64(0x8000_0000_0000_0000));
69    }
70
71    proptest! {
72
73        #[test]
74        fn encode_i32_roundtrip(i in i32::ANY) {
75            let dec = decode_u32(encode_i32(i));
76            prop_assert_eq![i, dec];
77        }
78
79        #[test]
80        fn encode_i64_roundtrip(i in i64::ANY) {
81            let dec = decode_u64(encode_i64(i));
82            prop_assert_eq![i, dec];
83        }
84
85    }
86}