krabka_protocol/primitives/
array.rs1use bytes::{Buf, BufMut};
8
9use crate::{
10 ProtocolError,
11 primitives::{
12 fixed::{get_i32, put_i32},
13 varint::{get_uvarint, put_uvarint, uvarint_len},
14 },
15};
16
17pub fn put_array_len<B: BufMut>(buf: &mut B, n: usize, flexible: bool) {
21 if flexible {
22 put_uvarint(buf, u32::try_from(n + 1).expect("array too large"));
23 } else {
24 put_i32(buf, i32::try_from(n).expect("array too large"));
25 }
26}
27
28pub fn put_nullable_array_len<B: BufMut>(buf: &mut B, len: Option<usize>, flexible: bool) {
33 match (flexible, len) {
34 (false, None) => put_i32(buf, -1),
35 (false, Some(n)) => put_i32(buf, i32::try_from(n).expect("array too large")),
36 (true, None) => put_uvarint(buf, 0),
37 (true, Some(n)) => put_uvarint(buf, u32::try_from(n + 1).expect("array too large")),
38 }
39}
40
41#[must_use]
43pub fn array_len_prefix_len(n: usize, flexible: bool) -> usize {
46 if flexible {
47 uvarint_len(u32::try_from(n + 1).unwrap())
48 } else {
49 4
50 }
51}
52
53#[must_use]
55pub fn nullable_array_len_prefix_len(len: Option<usize>, flexible: bool) -> usize {
58 match (flexible, len) {
59 (false, _) => 4,
60 (true, None) => uvarint_len(0),
61 (true, Some(n)) => uvarint_len(u32::try_from(n + 1).unwrap()),
62 }
63}
64
65pub fn get_array_len<B: Buf>(buf: &mut B, flexible: bool) -> Result<usize, ProtocolError> {
72 let n = if flexible {
73 let raw = get_uvarint(buf)?;
74 if raw == 0 {
75 return Err(ProtocolError::InvalidValue(
76 "non-nullable array was null (compact encoding)",
77 ));
78 }
79 (raw - 1) as usize
80 } else {
81 let n = get_i32(buf)?;
82 if n < 0 {
83 return Err(ProtocolError::InvalidValue(
84 "non-nullable array had negative length",
85 ));
86 }
87 usize::try_from(n).expect("n is non-negative")
88 };
89 if n > buf.remaining() {
94 return Err(ProtocolError::InvalidValue(
95 "array length exceeds remaining buffer",
96 ));
97 }
98 Ok(n)
99}
100
101pub fn get_nullable_array_len<B: Buf>(
108 buf: &mut B,
109 flexible: bool,
110) -> Result<Option<usize>, ProtocolError> {
111 let n = if flexible {
112 let raw = get_uvarint(buf)?;
113 if raw == 0 {
114 return Ok(None);
115 }
116 (raw - 1) as usize
117 } else {
118 let n = get_i32(buf)?;
119 if n < 0 {
120 return Ok(None);
121 }
122 usize::try_from(n).expect("n is non-negative")
123 };
124 if n > buf.remaining() {
127 return Err(ProtocolError::InvalidValue(
128 "array length exceeds remaining buffer",
129 ));
130 }
131 Ok(Some(n))
132}
133
134#[cfg(test)]
135mod tests {
136
137 use bytes::BytesMut;
138
139 use super::*;
140
141 #[test]
142 fn array_roundtrip_cases() {
143 for (_case, len, flexible, expected_prefix) in [
144 ("non-flex empty", 0, false, &[0, 0, 0, 0][..]),
145 ("non-flex three", 3, false, &[0, 0, 0, 3][..]),
146 ("flex empty", 0, true, &[0x01][..]),
147 ("flex three", 3, true, &[0x04][..]),
148 ] {
149 let mut buf = BytesMut::new();
150 put_array_len(&mut buf, len, flexible);
151 assert2::assert!(&buf[..] == expected_prefix);
152 buf.extend_from_slice(&vec![0; len]);
153 let mut cur = &buf[..];
154 assert2::assert!((get_array_len(&mut cur, flexible).unwrap(), cur.len()) == (len, len));
155 }
156 }
157
158 #[test]
159 fn nullable_array_roundtrip_cases() {
160 for (_case, len, flexible, expected_prefix) in [
161 ("non-flex null", None, false, &[0xFF, 0xFF, 0xFF, 0xFF][..]),
162 ("non-flex some", Some(3), false, &[0, 0, 0, 3][..]),
163 ("flex null", None, true, &[0x00][..]),
164 ("flex some", Some(3), true, &[0x04][..]),
165 ] {
166 let mut buf = BytesMut::new();
167 put_nullable_array_len(&mut buf, len, flexible);
168 assert2::assert!(&buf[..] == expected_prefix);
169 let payload_len = len.unwrap_or(0);
170 buf.extend_from_slice(&vec![0; payload_len]);
171 let mut cur = &buf[..];
172 assert2::assert!(
173 (
174 get_nullable_array_len(&mut cur, flexible).unwrap(),
175 cur.len()
176 ) == (len, payload_len)
177 );
178 }
179 }
180
181 #[test]
184 fn array_len_prefix_len_non_flex() {
185 for (_case, len) in [("empty", 0), ("populated", 100)] {
186 assert2::assert!(array_len_prefix_len(len, false) == 4);
187 }
188 }
189
190 #[test]
191 fn array_len_prefix_len_flex() {
192 for (len, want) in [(0, 1), (126, 1), (127, 2)] {
195 assert2::assert!(array_len_prefix_len(len, true) == want);
196 }
197 }
198
199 #[test]
200 fn nullable_prefix_len_non_flex_always_4() {
201 for (_case, len) in [("null", None), ("some", Some(3))] {
202 assert2::assert!(nullable_array_len_prefix_len(len, false) == 4);
203 }
204 }
205
206 #[test]
207 fn nullable_prefix_len_flex_null_is_1() {
208 assert2::assert!(nullable_array_len_prefix_len(None, true) == 1);
210 }
211
212 #[test]
215 fn non_nullable_rejects_null_cases() {
216 for (_case, bytes, flexible) in [
217 ("non-flex", &(-1i32).to_be_bytes()[..], false),
218 ("flex", &[0x00][..], true),
219 ] {
220 let mut cur = bytes;
221 assert2::assert!(matches!(
222 get_array_len(&mut cur, flexible),
223 Err(ProtocolError::InvalidValue(_))
224 ));
225 }
226 }
227
228 #[test]
231 fn rejects_length_exceeding_remaining_cases() {
232 for (_case, nullable, flexible) in [
233 ("non-nullable non-flex", false, false),
234 ("non-nullable flex", false, true),
235 ("nullable non-flex", true, false),
236 ("nullable flex", true, true),
237 ] {
238 let mut buf = BytesMut::new();
239 if nullable {
240 put_nullable_array_len(&mut buf, Some(2_000_000_000), flexible);
241 } else {
242 put_array_len(&mut buf, 2_000_000_000, flexible);
243 }
244 let mut cur = &buf[..];
245 let result = if nullable {
246 get_nullable_array_len(&mut cur, flexible).map(|value| value.unwrap_or(0))
247 } else {
248 get_array_len(&mut cur, flexible)
249 };
250 assert2::assert!(matches!(
251 result,
252 Err(ProtocolError::InvalidValue(
253 "array length exceeds remaining buffer"
254 ))
255 ));
256 }
257 }
258
259 #[test]
260 fn length_equal_to_remaining_is_accepted() {
261 for (_case, flexible) in [("non-flex", false), ("flex", true)] {
262 let mut buf = BytesMut::new();
263 put_array_len(&mut buf, 5, flexible);
264 buf.extend_from_slice(&[0u8; 5]);
265 let mut cur = &buf[..];
266 assert2::assert!(get_array_len(&mut cur, flexible).unwrap() == 5);
267 }
268 }
269}