1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use std::io::{Error, ErrorKind, Result};
use crate::primitive::*;
use crate::{Readable, Writable};

macro_rules! variable_len_util {
    ($primitive: ident, $length: literal, $read_variable: ident, $write_variable: ident, $cause: literal,
    $read: ident, $write: ident, $inside_type: ident, $num_bits: literal, $next_bit: literal, $offset_position: literal) => {
        pub fn $read_variable(source: &mut impl Readable) -> Result<$primitive> {
            let mut value = 0;
            let mut position = 0;
            loop {
                let current = $read(source)?;
                value |= ((current & $num_bits) as $primitive) << position;
                if current & $next_bit == 0 {
                    break;
                }
                position += $offset_position;
                if position >= $length {
                    return Err(Error::new(ErrorKind::InvalidData, format!("Variable {} in stream is too long.", $cause)));
                }
            }
            Ok(value)
        }
        pub fn $write_variable(target: &mut impl Writable, message: $primitive) -> Result<usize> {
            let mut size = 0;
            let mut value = message;
            while value >> $offset_position > 0 {
                size += $write(target, ((value & $num_bits) as $inside_type) | $next_bit)?;
                value >>= $offset_position;
            }
            size += $write(target, (value & $num_bits) as $inside_type)?;
            Ok(size)
        }
    };
}
macro_rules! variable_len_utils {
    ($primitive: ident, $length: literal, $read_variable: ident, $write_variable: ident, $cause: literal, u8) => {
        variable_len_util!($primitive, $length, $read_variable, $write_variable, $cause, read_u8, write_u8, u8, 0x7f, 0x80, 7);
    };
    ($primitive: ident, $length: literal, $read_variable: ident, $write_variable: ident, $cause: literal, u16) => {
        variable_len_util!($primitive, $length, $read_variable, $write_variable, $cause, read_u16, write_u16, u16, 0x7fff, 0x8000, 15);
    };
    ($primitive: ident, $length: literal, $read_variable: ident, $write_variable: ident, $cause: literal, u16_be) => {
        variable_len_util!($primitive, $length, $read_variable, $write_variable, $cause, read_u16_be, write_u16_be, u16, 0x7fff, 0x8000, 15);
    };
    ($primitive: ident, $length: literal, $read_variable: ident, $write_variable: ident, $cause: literal, u32) => {
        variable_len_util!($primitive, $length, $read_variable, $write_variable, $cause, read_u32, write_u32, u32, 0x7fffffff, 0x80000000, 31);
    };
    ($primitive: ident, $length: literal, $read_variable: ident, $write_variable: ident, $cause: literal, u32_be) => {
        variable_len_util!($primitive, $length, $read_variable, $write_variable, $cause, read_u32_be, write_u32_be, u32, 0x7fffffff, 0x80000000, 31);
    };
    ($primitive: ident, $length: literal, $read_variable: ident, $write_variable: ident, $cause: literal, u64) => {
        variable_len_util!($primitive, $length, $read_variable, $write_variable, $cause, read_u64, write_u64, u64, 0x7fffffffffffffff, 0x8000000000000000, 63);
    };
    ($primitive: ident, $length: literal, $read_variable: ident, $write_variable: ident, $cause: literal, u64_be) => {
        variable_len_util!($primitive, $length, $read_variable, $write_variable, $cause, read_u64_be, write_u64_be, u64, 0x7fffffffffffffff, 0x8000000000000000, 63);
    };
}
variable_len_utils!(u16, 16, read_variable_u16, write_variable_u16, "u16", u8);
variable_len_utils!(u32, 32, read_variable_u32, write_variable_u32, "u32", u8);
variable_len_utils!(u32, 32, read_variable2_u32, write_variable2_u32, "u32(2)", u16);
variable_len_utils!(u32, 32, read_variable2_u32_be, write_variable2_u32_be, "u32(2be)", u16_be);
variable_len_utils!(u64, 64, read_variable_u64, write_variable_u64, "u64", u8);
variable_len_utils!(u64, 64, read_variable2_u64, write_variable2_u64, "u64(2)", u16);
variable_len_utils!(u64, 64, read_variable2_u64_be, write_variable2_u64_be, "u64(2be)", u16_be);
variable_len_utils!(u64, 64, read_variable4_u64, write_variable4_u64, "u64(4)", u32);
variable_len_utils!(u64, 64, read_variable4_u64_be, write_variable4_u64_be, "u64(4be)", u32_be);
variable_len_utils!(u128, 128, read_variable_u128, write_variable_u128, "u128", u8);
variable_len_utils!(u128, 128, read_variable2_u128, write_variable2_u128, "u128(2)", u16);
variable_len_utils!(u128, 128, read_variable2_u128_be, write_variable2_u128_be, "u128(2be)", u16_be);
variable_len_utils!(u128, 128, read_variable4_u128, write_variable4_u128, "u128(4)", u32);
variable_len_utils!(u128, 128, read_variable4_u128_be, write_variable4_u128_be, "u128(4be)", u32_be);
variable_len_utils!(u128, 128, read_variable8_u128, write_variable8_u128, "u128(8)", u64);
variable_len_utils!(u128, 128, read_variable8_u128_be, write_variable8_u128_be, "u128(8be)", u64_be);