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