1
use std::io::Read;
2
use std::io::Write;
3

            
4
use bitstream_io::BitRead;
5
use bitstream_io::BitReader;
6
use bitstream_io::BitWrite;
7
use bitstream_io::BitWriter;
8
use bitstream_io::Endianness;
9

            
10
use merc_utilities::MercError;
11

            
12
/// The number of bits needed to represent a value of type T in most significant bit encoding.
13
662364
pub const fn encoding_size<T>() -> usize {
14
662364
    ((std::mem::size_of::<T>() + 1) * 8) / 7
15
662364
}
16

            
17
/// Encodes a given unsigned variable-length integer using the most significant bit (MSB) algorithm.
18
///
19
/// # Details
20
///
21
/// Implementation taken from <https://techoverflow.net/2013/01/25/efficiently-encoding-variable-length-integers-in-cc/>
22
651802
pub fn write_u64_variablelength<W: Write, E: Endianness>(
23
651802
    stream: &mut BitWriter<W, E>,
24
651802
    mut value: u64,
25
651802
) -> Result<(), MercError> {
26
    // While more than 7 bits of data are left, occupy the last output byte
27
    // and set the next byte flag.
28
702560
    while value > 0b01111111 {
29
50758
        stream.write::<8, u8>((value as u8 & 0b01111111) | 0b10000000)?;
30

            
31
        // Remove the seven bits we just wrote from value.
32
50758
        value >>= 7;
33
    }
34

            
35
651802
    stream.write::<8, u8>(value as u8)?;
36
651802
    Ok(())
37
651802
}
38

            
39
/// Decodes an unsigned variable-length integer using the MSB algorithm.
40
662364
pub fn read_u64_variablelength<R: Read, E: Endianness>(stream: &mut BitReader<R, E>) -> Result<u64, MercError> {
41
662364
    let mut value: u64 = 0;
42
713198
    for i in 0..encoding_size::<u64>() {
43
713198
        let byte = stream.read::<8, u8>()?;
44

            
45
        // Take 7 bits (mask 0x01111111) from byte and shift it before the bits already written to value.
46
713198
        value |= ((byte & 0b01111111) as u64) << (7 * i);
47

            
48
713198
        if byte & 0b10000000 == 0 {
49
            // If the next-byte flag is not set then we are finished.
50
662364
            break;
51
50834
        }
52
    }
53

            
54
662364
    Ok(value)
55
662364
}
56

            
57
#[cfg(test)]
58
mod tests {
59
    use super::*;
60

            
61
    use bitstream_io::BigEndian;
62
    use rand::Rng;
63

            
64
    use merc_utilities::random_test;
65

            
66
    #[test]
67
1
    fn test_random_integer_encoding() {
68
1000
        random_test(1000, |rng| {
69
1000
            let value = rng.random();
70

            
71
1000
            let mut stream: [u8; encoding_size::<u64>()] = [0; encoding_size::<u64>()];
72
1000
            let mut writer = BitWriter::<_, BigEndian>::new(&mut stream[0..]);
73
1000
            write_u64_variablelength(&mut writer, value).unwrap();
74

            
75
1000
            let mut reader = BitReader::<_, BigEndian>::new(&stream[0..]);
76
1000
            let result = read_u64_variablelength(&mut reader).unwrap();
77

            
78
1000
            assert_eq!(result, value);
79
1000
        });
80
1
    }
81
}