brain_db_sdk/wire/
cbor.rs1use serde::de::DeserializeOwned;
15use serde::Serialize;
16
17#[derive(Debug, thiserror::Error)]
19pub enum CborError {
20 #[error("CBOR decode failed: {0}")]
22 Decode(String),
23 #[error("trailing bytes after CBOR payload")]
26 TrailingBytes,
27 #[error("vector section is {0} bytes, not a multiple of 4")]
30 RaggedVector(usize),
31}
32
33pub fn to_cbor_bytes<T: Serialize>(value: &T) -> Vec<u8> {
42 let mut buf = Vec::new();
43 ciborium::into_writer(value, &mut buf)
44 .expect("invariant: CBOR encode of an owned value is infallible");
45 buf
46}
47
48pub fn from_cbor_bytes<T: DeserializeOwned>(bytes: &[u8]) -> Result<T, CborError> {
51 let mut cursor = std::io::Cursor::new(bytes);
52 let value: T =
53 ciborium::from_reader(&mut cursor).map_err(|e| CborError::Decode(e.to_string()))?;
54 let consumed = usize::try_from(cursor.position()).map_err(|_| CborError::TrailingBytes)?;
58 if consumed != bytes.len() {
59 return Err(CborError::TrailingBytes);
60 }
61 Ok(value)
62}
63
64pub fn from_cbor_prefix<T: DeserializeOwned>(bytes: &[u8]) -> Result<(T, usize), CborError> {
68 let mut cursor = std::io::Cursor::new(bytes);
69 let value: T =
70 ciborium::from_reader(&mut cursor).map_err(|e| CborError::Decode(e.to_string()))?;
71 let consumed = usize::try_from(cursor.position()).map_err(|_| CborError::TrailingBytes)?;
74 Ok((value, consumed))
75}
76
77#[must_use]
80pub fn f32_slice_to_le_bytes(v: &[f32]) -> Vec<u8> {
81 let mut out = Vec::with_capacity(v.len() * 4);
82 for f in v {
83 out.extend_from_slice(&f.to_le_bytes());
84 }
85 out
86}
87
88pub fn le_bytes_to_f32_vec(bytes: &[u8]) -> Result<Vec<f32>, CborError> {
93 if bytes.len() % 4 != 0 {
94 return Err(CborError::RaggedVector(bytes.len()));
95 }
96 Ok(bytes
97 .chunks_exact(4)
98 .map(|c| f32::from_le_bytes([c[0], c[1], c[2], c[3]]))
99 .collect())
100}
101
102pub mod vec_byte_array16 {
109 use serde::de::{Deserializer, SeqAccess, Visitor};
110 use serde::ser::SerializeSeq;
111 use std::fmt;
112
113 pub fn serialize<S>(v: &[[u8; 16]], serializer: S) -> Result<S::Ok, S::Error>
115 where
116 S: serde::ser::Serializer,
117 {
118 let mut seq = serializer.serialize_seq(Some(v.len()))?;
119 for id in v {
120 seq.serialize_element(serde_bytes::Bytes::new(id))?;
121 }
122 seq.end()
123 }
124
125 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<[u8; 16]>, D::Error>
127 where
128 D: Deserializer<'de>,
129 {
130 struct V;
131 impl<'de> Visitor<'de> for V {
132 type Value = Vec<[u8; 16]>;
133 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 f.write_str("a sequence of 16-byte byte strings")
135 }
136 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
137 where
138 A: SeqAccess<'de>,
139 {
140 const PREALLOC_FLOOR: usize = 64;
146 let hint = seq.size_hint().unwrap_or(0).min(PREALLOC_FLOOR);
147 let mut out = Vec::with_capacity(hint);
148 while let Some(b) = seq.next_element::<serde_bytes::ByteBuf>()? {
149 let arr: [u8; 16] = b.as_ref().try_into().map_err(|_| {
150 serde::de::Error::invalid_length(b.len(), &"exactly 16 bytes")
151 })?;
152 out.push(arr);
153 }
154 Ok(out)
155 }
156 }
157 deserializer.deserialize_seq(V)
158 }
159}
160
161pub mod opt_vec_byte_array16 {
165 use serde::de::Deserializer;
166
167 pub fn serialize<S>(v: &Option<Vec<[u8; 16]>>, serializer: S) -> Result<S::Ok, S::Error>
169 where
170 S: serde::ser::Serializer,
171 {
172 match v {
173 None => serializer.serialize_none(),
174 Some(inner) => {
175 #[derive(serde::Serialize)]
176 struct W<'a>(#[serde(with = "super::vec_byte_array16")] &'a [[u8; 16]]);
177 serializer.serialize_some(&W(inner))
178 }
179 }
180 }
181
182 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<[u8; 16]>>, D::Error>
185 where
186 D: Deserializer<'de>,
187 {
188 use serde::Deserialize as _;
189 #[derive(serde::Deserialize)]
190 struct W(#[serde(with = "super::vec_byte_array16")] Vec<[u8; 16]>);
191 let opt: Option<W> = Option::deserialize(deserializer)?;
192 Ok(opt.map(|w| w.0))
193 }
194}
195
196pub(crate) mod opt_byte_array16 {
206 use serde::de::Deserializer;
207
208 pub(crate) fn serialize<S>(v: &Option<[u8; 16]>, serializer: S) -> Result<S::Ok, S::Error>
209 where
210 S: serde::ser::Serializer,
211 {
212 match v {
213 None => serializer.serialize_none(),
214 Some(id) => serializer.serialize_some(serde_bytes::Bytes::new(id)),
215 }
216 }
217
218 pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Option<[u8; 16]>, D::Error>
219 where
220 D: Deserializer<'de>,
221 {
222 use serde::Deserialize as _;
223 let opt: Option<serde_bytes::ByteBuf> = Option::deserialize(deserializer)?;
224 match opt {
225 None => Ok(None),
226 Some(b) => {
227 let arr: [u8; 16] = b
228 .as_ref()
229 .try_into()
230 .map_err(|_| serde::de::Error::invalid_length(b.len(), &"exactly 16 bytes"))?;
231 Ok(Some(arr))
232 }
233 }
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)]
242 struct Sample {
243 a: u32,
244 b: String,
245 }
246
247 #[test]
248 fn round_trip_whole_buffer() {
249 let s = Sample {
250 a: 7,
251 b: "hi".into(),
252 };
253 let bytes = to_cbor_bytes(&s);
254 let back: Sample = from_cbor_bytes(&bytes).expect("decode");
255 assert_eq!(s, back);
256 }
257
258 #[test]
259 fn trailing_bytes_rejected() {
260 let s = Sample {
261 a: 1,
262 b: "x".into(),
263 };
264 let mut bytes = to_cbor_bytes(&s);
265 bytes.push(0xFF);
266 assert!(matches!(
267 from_cbor_bytes::<Sample>(&bytes),
268 Err(CborError::TrailingBytes)
269 ));
270 }
271
272 #[test]
273 fn prefix_reports_consumed_and_vector_round_trips() {
274 let s = Sample {
275 a: 3,
276 b: "v".into(),
277 };
278 let vec = vec![1.0f32, -2.5, 3.25];
279 let mut payload = to_cbor_bytes(&s);
280 let cbor_len = payload.len();
281 payload.extend_from_slice(&f32_slice_to_le_bytes(&vec));
282
283 let (back, consumed): (Sample, usize) = from_cbor_prefix(&payload).expect("prefix");
284 assert_eq!(back, s);
285 assert_eq!(consumed, cbor_len);
286 let got = le_bytes_to_f32_vec(&payload[consumed..]).expect("vec");
287 assert_eq!(got, vec);
288 }
289
290 #[test]
291 fn ragged_vector_section_rejected() {
292 assert!(matches!(
293 le_bytes_to_f32_vec(&[0u8, 1, 2]),
294 Err(CborError::RaggedVector(3))
295 ));
296 }
297}