1use nodedb_types::read_instant;
6
7use super::scalar::read_str;
8use super::tags::*;
9
10pub fn read_value(buf: &[u8], offset: usize) -> Option<nodedb_types::Value> {
17 let tag = get(buf, offset)?;
18 match tag {
19 NIL => Some(nodedb_types::Value::Null),
20 TRUE => Some(nodedb_types::Value::Bool(true)),
21 FALSE => Some(nodedb_types::Value::Bool(false)),
22 0x00..=0x7f => Some(nodedb_types::Value::Integer(tag as i64)),
24 0xe0..=0xff => Some(nodedb_types::Value::Integer((tag as i8) as i64)),
25 UINT8 => Some(nodedb_types::Value::Integer(get(buf, offset + 1)? as i64)),
26 UINT16 => Some(nodedb_types::Value::Integer(
27 read_u16_be(buf, offset + 1)? as i64
28 )),
29 UINT32 => Some(nodedb_types::Value::Integer(
30 read_u32_be(buf, offset + 1)? as i64
31 )),
32 UINT64 => Some(nodedb_types::Value::from_u64(read_u64_be(buf, offset + 1)?)),
34 INT8 => Some(nodedb_types::Value::Integer(
35 get(buf, offset + 1)? as i8 as i64
36 )),
37 INT16 => Some(nodedb_types::Value::Integer(
38 read_u16_be(buf, offset + 1)? as i16 as i64,
39 )),
40 INT32 => Some(nodedb_types::Value::Integer(
41 read_u32_be(buf, offset + 1)? as i32 as i64,
42 )),
43 INT64 => Some(nodedb_types::Value::Integer(
44 read_u64_be(buf, offset + 1)? as i64
45 )),
46 FLOAT32 => {
48 let bits = read_u32_be(buf, offset + 1)?;
49 Some(nodedb_types::Value::Float(f32::from_bits(bits) as f64))
50 }
51 FLOAT64 => {
52 let bits = read_u64_be(buf, offset + 1)?;
53 Some(nodedb_types::Value::Float(f64::from_bits(bits)))
54 }
55 0xa0..=0xbf | STR8 | STR16 | STR32 => {
57 read_str(buf, offset).map(|s| nodedb_types::Value::String(s.to_string()))
58 }
59 FIXEXT8 => read_instant(buf, offset).map(|(kind, micros)| kind.from_micros(micros)),
61 _ => None,
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use crate::msgpack_scan::reader::{
69 array_header, map_header, read_bool, read_f64, read_i64, read_null, skip_value,
70 };
71 use nodedb_types::{InstantKind, NdbDateTime, Value, write_instant};
72
73 use serde_json::json;
74
75 fn encode(v: &serde_json::Value) -> Vec<u8> {
77 nodedb_types::json_msgpack::json_to_msgpack(v).expect("encode")
78 }
79
80 #[test]
81 fn read_value_instants() {
82 let mut buf = vec![0xc0];
83 write_instant(&mut buf, InstantKind::Utc, 1_710_498_600_000_000);
84 write_instant(&mut buf, InstantKind::Naive, -5);
85 assert_eq!(
86 read_value(&buf, 1),
87 Some(Value::DateTime(NdbDateTime::from_micros(
88 1_710_498_600_000_000
89 )))
90 );
91 assert_eq!(
92 read_value(&buf, 11),
93 Some(Value::NaiveDateTime(NdbDateTime::from_micros(-5)))
94 );
95 assert_eq!(skip_value(&buf, 1), Some(11));
96 }
97
98 #[test]
99 fn read_value_uint64_above_i64_max_is_decimal() {
100 let mut buf = vec![UINT64];
101 buf.extend_from_slice(&u64::MAX.to_be_bytes());
102 assert_eq!(
103 read_value(&buf, 0),
104 Some(Value::Decimal(rust_decimal::Decimal::from(u64::MAX)))
105 );
106 let mut edge = vec![UINT64];
107 edge.extend_from_slice(&(i64::MAX as u64).to_be_bytes());
108 assert_eq!(read_value(&edge, 0), Some(Value::Integer(i64::MAX)));
109 let buf = encode(&json!(9_223_372_036_854_775_808_u64));
110 assert_eq!(
111 read_value(&buf, 0),
112 Some(Value::Decimal(rust_decimal::Decimal::from(
113 9_223_372_036_854_775_808_u64
114 )))
115 );
116 }
117
118 #[test]
119 fn read_value_unknown_ext_is_none() {
120 let buf = [FIXEXT8, 0x09, 0, 0, 0, 0, 0, 0, 0, 1];
121 assert_eq!(read_value(&buf, 0), None);
122 let buf = [FIXEXT1, 0x01, 0xab];
123 assert_eq!(read_value(&buf, 0), None);
124 }
125
126 #[test]
127 fn read_value_truncated_instant_is_none() {
128 let mut buf = Vec::new();
129 write_instant(&mut buf, InstantKind::Utc, 42);
130 buf.truncate(9);
131 assert_eq!(read_value(&buf, 0), None);
132 }
133
134 #[test]
137 fn fuzz_all_single_byte_sequences() {
138 for byte in 0u8..=255 {
139 let buf = [byte];
140 let _ = skip_value(&buf, 0);
142 let _ = read_f64(&buf, 0);
143 let _ = read_i64(&buf, 0);
144 let _ = read_str(&buf, 0);
145 let _ = read_bool(&buf, 0);
146 let _ = read_null(&buf, 0);
147 let _ = map_header(&buf, 0);
148 let _ = array_header(&buf, 0);
149 let _ = read_value(&buf, 0);
150 }
151 }
152
153 #[test]
155 fn fuzz_two_byte_patterns() {
156 let tags_need_extra: &[u8] = &[
158 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xc4, 0xc5, 0xc6, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, ];
184 for &tag in tags_need_extra {
185 let buf = [tag];
187 let _ = skip_value(&buf, 0);
188 let _ = read_f64(&buf, 0);
189 let _ = read_i64(&buf, 0);
190 let _ = read_value(&buf, 0);
191
192 for second in [0x00u8, 0x01, 0x7f, 0x80, 0xff] {
194 let buf = [tag, second];
195 let _ = skip_value(&buf, 0);
196 let _ = read_f64(&buf, 0);
197 let _ = read_i64(&buf, 0);
198 let _ = read_value(&buf, 0);
199 }
200 }
201 }
202
203 #[test]
205 fn fuzz_deterministic_random_payloads() {
206 let mut state: u64 = 0xdeadbeef_cafebabe;
209 let next = |s: &mut u64| -> u8 {
210 *s = s
211 .wrapping_mul(6364136223846793005)
212 .wrapping_add(1442695040888963407);
213 (*s >> 33) as u8
214 };
215
216 let mut buf = vec![0u8; 256];
217 for _ in 0..2000 {
218 let len = (next(&mut state) as usize % 256) + 1;
220 for b in buf[..len].iter_mut() {
221 *b = next(&mut state);
222 }
223 let slice = &buf[..len];
224
225 for offset in [0, 1, len / 2, len.saturating_sub(1)] {
227 let _ = skip_value(slice, offset);
228 let _ = read_f64(slice, offset);
229 let _ = read_i64(slice, offset);
230 let _ = read_str(slice, offset);
231 let _ = read_bool(slice, offset);
232 let _ = read_null(slice, offset);
233 let _ = map_header(slice, offset);
234 let _ = array_header(slice, offset);
235 let _ = read_value(slice, offset);
236 }
237 }
238 }
239
240 #[test]
243 fn fuzz_truncated_valid_payloads() {
244 let docs = [
245 json!({"key": "value", "num": 42, "flag": true}),
246 json!({"nested": {"a": 1, "b": [1, 2, 3]}}),
247 json!([1, "two", 3.0, null, false]),
248 json!({"large": 9999999999_i64}),
249 json!({"float": 1.23456789}),
250 ];
251
252 for doc in &docs {
253 let full = encode(doc);
254 for truncate_at in 0..full.len() {
256 let slice = &full[..truncate_at];
257 let _ = skip_value(slice, 0);
259 let _ = read_f64(slice, 0);
260 let _ = read_i64(slice, 0);
261 let _ = read_str(slice, 0);
262 let _ = read_bool(slice, 0);
263 let _ = map_header(slice, 0);
264 let _ = array_header(slice, 0);
265 let _ = read_value(slice, 0);
266 }
267 }
268 }
269
270 #[test]
272 fn fuzz_never_used_tag_c1() {
273 let buf = [0xc1u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
275 assert_eq!(
276 skip_value(&buf, 0),
277 None,
278 "0xc1 must return None from skip_value"
279 );
280 assert_eq!(read_f64(&buf, 0), None);
281 assert_eq!(read_i64(&buf, 0), None);
282 assert_eq!(read_str(&buf, 0), None);
283 assert_eq!(read_bool(&buf, 0), None);
284 assert_eq!(map_header(&buf, 0), None);
285 assert_eq!(array_header(&buf, 0), None);
286 assert_eq!(read_value(&buf, 0), None);
287 }
288
289 #[test]
291 fn fuzz_out_of_bounds_offset() {
292 let buf = encode(&json!({"x": 1}));
293 let way_out = buf.len() + 1000;
294 assert_eq!(skip_value(&buf, way_out), None);
295 assert_eq!(read_f64(&buf, way_out), None);
296 assert_eq!(read_i64(&buf, way_out), None);
297 assert_eq!(read_str(&buf, way_out), None);
298 assert_eq!(read_bool(&buf, way_out), None);
299 assert_eq!(map_header(&buf, way_out), None);
300 assert_eq!(array_header(&buf, way_out), None);
301 assert_eq!(read_value(&buf, way_out), None);
302 }
303
304 #[test]
306 fn fuzz_empty_buffer() {
307 let buf: &[u8] = &[];
308 assert_eq!(skip_value(buf, 0), None);
309 assert_eq!(read_f64(buf, 0), None);
310 assert_eq!(read_i64(buf, 0), None);
311 assert_eq!(read_str(buf, 0), None);
312 assert_eq!(read_bool(buf, 0), None);
313 assert!(!read_null(buf, 0)); assert_eq!(map_header(buf, 0), None);
315 assert_eq!(array_header(buf, 0), None);
316 assert_eq!(read_value(buf, 0), None);
317 }
318}