logicaffeine_base/
exact_codec.rs1use crate::describe::{read_uvarint, write_uvarint};
19use crate::numeric::{BigInt, Rational};
20
21pub const MAX_WIRE_DEGREE: usize = 32;
24pub const MAX_WIRE_LEN: usize = 1 << 16;
26pub const MAX_WIRE_MAGNITUDE: usize = 1 << 20;
28pub const MAX_WIRE_DEPTH: usize = 256;
30
31pub trait ExactCodec: Sized {
33 fn write(&self, out: &mut Vec<u8>);
34 fn read(buf: &[u8], pos: &mut usize) -> Option<Self>;
35
36 fn to_bytes(&self) -> Vec<u8> {
37 let mut out = Vec::new();
38 self.write(&mut out);
39 out
40 }
41
42 fn from_bytes(buf: &[u8]) -> Option<Self> {
44 let mut pos = 0;
45 let v = Self::read(buf, &mut pos)?;
46 (pos == buf.len() && v.to_bytes() == buf).then_some(v)
47 }
48}
49
50pub fn read_byte(buf: &[u8], pos: &mut usize) -> Option<u8> {
52 let b = *buf.get(*pos)?;
53 *pos += 1;
54 Some(b)
55}
56
57pub fn read_len(buf: &[u8], pos: &mut usize, bound: usize) -> Option<usize> {
59 let n = read_uvarint(buf, pos)?;
60 (n as usize <= bound).then_some(n as usize)
61}
62
63pub fn write_len(n: usize, out: &mut Vec<u8>) {
64 write_uvarint(n as u64, out);
65}
66
67pub fn write_list<T: ExactCodec>(items: &[T], out: &mut Vec<u8>) {
69 write_len(items.len(), out);
70 for item in items {
71 item.write(out);
72 }
73}
74
75pub fn read_list<T: ExactCodec>(buf: &[u8], pos: &mut usize) -> Option<Vec<T>> {
76 let n = read_len(buf, pos, MAX_WIRE_LEN)?;
77 let mut out = Vec::with_capacity(n.min(64));
78 for _ in 0..n {
79 out.push(T::read(buf, pos)?);
80 }
81 Some(out)
82}
83
84impl ExactCodec for u64 {
85 fn write(&self, out: &mut Vec<u8>) {
86 write_uvarint(*self, out);
87 }
88
89 fn read(buf: &[u8], pos: &mut usize) -> Option<Self> {
90 read_uvarint(buf, pos)
91 }
92}
93
94impl ExactCodec for i64 {
95 fn write(&self, out: &mut Vec<u8>) {
96 write_uvarint(crate::describe::zigzag(*self), out);
97 }
98
99 fn read(buf: &[u8], pos: &mut usize) -> Option<Self> {
100 read_uvarint(buf, pos).map(crate::describe::unzigzag)
101 }
102}
103
104impl ExactCodec for BigInt {
105 fn write(&self, out: &mut Vec<u8>) {
107 let (negative, magnitude) = self.to_le_bytes();
108 out.push(u8::from(negative));
109 write_len(magnitude.len(), out);
110 out.extend_from_slice(&magnitude);
111 }
112
113 fn read(buf: &[u8], pos: &mut usize) -> Option<Self> {
114 let negative = match read_byte(buf, pos)? {
115 0 => false,
116 1 => true,
117 _ => return None,
118 };
119 let n = read_len(buf, pos, MAX_WIRE_MAGNITUDE)?;
120 let bytes = buf.get(*pos..*pos + n)?;
121 *pos += n;
122 Some(BigInt::from_le_bytes(negative, bytes))
123 }
124}
125
126impl ExactCodec for Rational {
127 fn write(&self, out: &mut Vec<u8>) {
129 self.numerator().write(out);
130 self.denominator().write(out);
131 }
132
133 fn read(buf: &[u8], pos: &mut usize) -> Option<Self> {
134 let num = BigInt::read(buf, pos)?;
135 let den = BigInt::read(buf, pos)?;
136 if !den.is_positive() {
137 return None;
138 }
139 Rational::new(num, den)
140 }
141}
142
143#[cfg(test)]
144#[path = "../../logicaffeine_tests/unit/logicaffeine_base/exact_codec/tests.rs"]
145mod tests;