crabka_protocol/primitives/
array.rs1use bytes::{Buf, BufMut};
8
9use crate::ProtocolError;
10use crate::primitives::fixed::{get_i32, put_i32};
11use crate::primitives::varint::{get_uvarint, put_uvarint, uvarint_len};
12
13pub fn put_array_len<B: BufMut>(buf: &mut B, n: usize, flexible: bool) {
15 if flexible {
16 put_uvarint(buf, u32::try_from(n + 1).expect("array too large"));
17 } else {
18 put_i32(buf, i32::try_from(n).expect("array too large"));
19 }
20}
21
22pub fn put_nullable_array_len<B: BufMut>(buf: &mut B, len: Option<usize>, flexible: bool) {
25 match (flexible, len) {
26 (false, None) => put_i32(buf, -1),
27 (false, Some(n)) => put_i32(buf, i32::try_from(n).expect("array too large")),
28 (true, None) => put_uvarint(buf, 0),
29 (true, Some(n)) => put_uvarint(buf, u32::try_from(n + 1).expect("array too large")),
30 }
31}
32
33#[must_use]
35pub fn array_len_prefix_len(n: usize, flexible: bool) -> usize {
36 if flexible {
37 uvarint_len(u32::try_from(n + 1).unwrap())
38 } else {
39 4
40 }
41}
42
43#[must_use]
45pub fn nullable_array_len_prefix_len(len: Option<usize>, flexible: bool) -> usize {
46 match (flexible, len) {
47 (false, _) => 4,
48 (true, None) => uvarint_len(0),
49 (true, Some(n)) => uvarint_len(u32::try_from(n + 1).unwrap()),
50 }
51}
52
53pub fn get_array_len<B: Buf>(buf: &mut B, flexible: bool) -> Result<usize, ProtocolError> {
56 if flexible {
57 let raw = get_uvarint(buf)?;
58 if raw == 0 {
59 return Err(ProtocolError::InvalidValue(
60 "non-nullable array was null (compact encoding)",
61 ));
62 }
63 Ok((raw - 1) as usize)
64 } else {
65 let n = get_i32(buf)?;
66 if n < 0 {
67 return Err(ProtocolError::InvalidValue(
68 "non-nullable array had negative length",
69 ));
70 }
71 Ok(usize::try_from(n).expect("n is non-negative"))
72 }
73}
74
75pub fn get_nullable_array_len<B: Buf>(
78 buf: &mut B,
79 flexible: bool,
80) -> Result<Option<usize>, ProtocolError> {
81 if flexible {
82 let raw = get_uvarint(buf)?;
83 if raw == 0 {
84 Ok(None)
85 } else {
86 Ok(Some((raw - 1) as usize))
87 }
88 } else {
89 let n = get_i32(buf)?;
90 if n < 0 {
91 Ok(None)
92 } else {
93 Ok(Some(usize::try_from(n).expect("n is non-negative")))
94 }
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use bytes::BytesMut;
102
103 #[test]
106 fn non_flex_empty_array_roundtrip() {
107 let mut buf = BytesMut::new();
108 put_array_len(&mut buf, 0, false);
109 assert_eq!(buf.len(), 4, "prefix must be 4 bytes");
110 let mut cur = &buf[..];
111 assert_eq!(get_array_len(&mut cur, false).unwrap(), 0);
112 assert!(cur.is_empty());
113 }
114
115 #[test]
116 fn non_flex_three_element_array_roundtrip() {
117 let mut buf = BytesMut::new();
118 put_array_len(&mut buf, 3, false);
119 assert_eq!(buf.len(), 4);
120 let mut cur = &buf[..];
121 assert_eq!(get_array_len(&mut cur, false).unwrap(), 3);
122 assert!(cur.is_empty());
123 }
124
125 #[test]
128 fn flex_empty_array_roundtrip() {
129 let mut buf = BytesMut::new();
130 put_array_len(&mut buf, 0, true);
131 assert_eq!(&buf[..], &[0x01]);
133 let mut cur = &buf[..];
134 assert_eq!(get_array_len(&mut cur, true).unwrap(), 0);
135 assert!(cur.is_empty());
136 }
137
138 #[test]
139 fn flex_three_element_array_roundtrip() {
140 let mut buf = BytesMut::new();
141 put_array_len(&mut buf, 3, true);
142 assert_eq!(&buf[..], &[0x04]);
144 let mut cur = &buf[..];
145 assert_eq!(get_array_len(&mut cur, true).unwrap(), 3);
146 assert!(cur.is_empty());
147 }
148
149 #[test]
152 fn non_flex_nullable_null_roundtrip() {
153 let mut buf = BytesMut::new();
154 put_nullable_array_len(&mut buf, None, false);
155 assert_eq!(&buf[..], &[0xFF, 0xFF, 0xFF, 0xFF]); let mut cur = &buf[..];
157 assert_eq!(get_nullable_array_len(&mut cur, false).unwrap(), None);
158 assert!(cur.is_empty());
159 }
160
161 #[test]
162 fn non_flex_nullable_some_roundtrip() {
163 let mut buf = BytesMut::new();
164 put_nullable_array_len(&mut buf, Some(3), false);
165 let mut cur = &buf[..];
166 assert_eq!(get_nullable_array_len(&mut cur, false).unwrap(), Some(3));
167 assert!(cur.is_empty());
168 }
169
170 #[test]
173 fn flex_nullable_null_roundtrip() {
174 let mut buf = BytesMut::new();
175 put_nullable_array_len(&mut buf, None, true);
176 assert_eq!(&buf[..], &[0x00]); let mut cur = &buf[..];
178 assert_eq!(get_nullable_array_len(&mut cur, true).unwrap(), None);
179 assert!(cur.is_empty());
180 }
181
182 #[test]
183 fn flex_nullable_some_roundtrip() {
184 let mut buf = BytesMut::new();
185 put_nullable_array_len(&mut buf, Some(3), true);
186 assert_eq!(&buf[..], &[0x04]);
188 let mut cur = &buf[..];
189 assert_eq!(get_nullable_array_len(&mut cur, true).unwrap(), Some(3));
190 assert!(cur.is_empty());
191 }
192
193 #[test]
196 fn array_len_prefix_len_non_flex() {
197 assert_eq!(array_len_prefix_len(0, false), 4);
198 assert_eq!(array_len_prefix_len(100, false), 4);
199 }
200
201 #[test]
202 fn array_len_prefix_len_flex() {
203 assert_eq!(array_len_prefix_len(0, true), 1);
206 assert_eq!(array_len_prefix_len(126, true), 1);
207 assert_eq!(array_len_prefix_len(127, true), 2);
208 }
209
210 #[test]
211 fn nullable_prefix_len_non_flex_always_4() {
212 assert_eq!(nullable_array_len_prefix_len(None, false), 4);
213 assert_eq!(nullable_array_len_prefix_len(Some(3), false), 4);
214 }
215
216 #[test]
217 fn nullable_prefix_len_flex_null_is_1() {
218 assert_eq!(nullable_array_len_prefix_len(None, true), 1);
220 }
221
222 #[test]
225 fn non_nullable_rejects_null_non_flex() {
226 let bytes = (-1i32).to_be_bytes();
227 let mut cur = &bytes[..];
228 assert!(matches!(
229 get_array_len(&mut cur, false),
230 Err(ProtocolError::InvalidValue(_))
231 ));
232 }
233
234 #[test]
235 fn non_nullable_rejects_null_flex() {
236 let bytes = [0x00u8];
238 let mut cur = &bytes[..];
239 assert!(matches!(
240 get_array_len(&mut cur, true),
241 Err(ProtocolError::InvalidValue(_))
242 ));
243 }
244}