use std::str;
use super::tags::*;
pub fn read_f64(buf: &[u8], offset: usize) -> Option<f64> {
let tag = get(buf, offset)?;
match tag {
0x00..=0x7f => Some(tag as f64),
0xe0..=0xff => Some((tag as i8) as f64),
FLOAT64 => {
let bits = read_u64_be(buf, offset + 1)?;
Some(f64::from_bits(bits))
}
FLOAT32 => {
let bits = read_u32_be(buf, offset + 1)?;
Some(f32::from_bits(bits) as f64)
}
UINT8 => Some(get(buf, offset + 1)? as f64),
UINT16 => Some(read_u16_be(buf, offset + 1)? as f64),
UINT32 => Some(read_u32_be(buf, offset + 1)? as f64),
UINT64 => Some(read_u64_be(buf, offset + 1)? as f64),
INT8 => Some(get(buf, offset + 1)? as i8 as f64),
INT16 => Some(read_u16_be(buf, offset + 1)? as i16 as f64),
INT32 => Some(read_u32_be(buf, offset + 1)? as i32 as f64),
INT64 => Some(read_u64_be(buf, offset + 1)? as i64 as f64),
_ => None,
}
}
pub fn read_i64(buf: &[u8], offset: usize) -> Option<i64> {
i64::try_from(read_integer(buf, offset)?).ok()
}
pub fn read_integer(buf: &[u8], offset: usize) -> Option<i128> {
let tag = get(buf, offset)?;
match tag {
0x00..=0x7f => Some(i128::from(tag)),
0xe0..=0xff => Some(i128::from(tag as i8)),
UINT8 => Some(i128::from(get(buf, offset + 1)?)),
UINT16 => Some(i128::from(read_u16_be(buf, offset + 1)?)),
UINT32 => Some(i128::from(read_u32_be(buf, offset + 1)?)),
UINT64 => Some(i128::from(read_u64_be(buf, offset + 1)?)),
INT8 => Some(i128::from(get(buf, offset + 1)? as i8)),
INT16 => Some(i128::from(read_u16_be(buf, offset + 1)? as i16)),
INT32 => Some(i128::from(read_u32_be(buf, offset + 1)? as i32)),
INT64 => Some(i128::from(read_u64_be(buf, offset + 1)? as i64)),
_ => None,
}
}
pub(crate) fn read_numeric(buf: &[u8], offset: usize) -> Option<crate::numeric_cmp::Numeric> {
use crate::numeric_cmp::Numeric;
match read_integer(buf, offset) {
Some(i) => Some(Numeric::Int(i)),
None => match get(buf, offset)? {
FLOAT32 | FLOAT64 => read_f64(buf, offset).map(Numeric::Float),
_ => None,
},
}
}
pub fn read_str(buf: &[u8], offset: usize) -> Option<&str> {
let (start, len) = str_bounds(buf, offset)?;
let bytes = buf.get(start..start + len)?;
str::from_utf8(bytes).ok()
}
pub fn read_str_advance<'a>(buf: &'a [u8], off: &mut usize) -> Option<&'a str> {
let (start, len) = str_bounds(buf, *off)?;
let bytes = buf.get(start..start + len)?;
let s = str::from_utf8(bytes).ok()?;
*off = start + len;
Some(s)
}
pub fn read_bin_advance<'a>(buf: &'a [u8], off: &mut usize) -> Option<&'a [u8]> {
let tag = get(buf, *off)?;
let (len, header) = match tag {
BIN8 => (get(buf, *off + 1)? as usize, 2),
BIN16 => (read_u16_be(buf, *off + 1)? as usize, 3),
BIN32 => (read_u32_be(buf, *off + 1)? as usize, 5),
_ => return None,
};
let start = *off + header;
let end = start + len;
let data = buf.get(start..end)?;
*off = end;
Some(data)
}
pub fn read_u32_advance(buf: &[u8], off: &mut usize) -> Option<u32> {
let tag = get(buf, *off)?;
match tag {
0x00..=0x7f => {
*off += 1;
Some(tag as u32)
}
UINT8 => {
let v = get(buf, *off + 1)? as u32;
*off += 2;
Some(v)
}
UINT16 => {
let v = read_u16_be(buf, *off + 1)? as u32;
*off += 3;
Some(v)
}
UINT32 => {
let v = read_u32_be(buf, *off + 1)?;
*off += 5;
Some(v)
}
_ => None,
}
}
pub(crate) fn str_bounds(buf: &[u8], offset: usize) -> Option<(usize, usize)> {
let tag = get(buf, offset)?;
match tag {
0xa0..=0xbf => {
let len = (tag & 0x1f) as usize;
Some((offset + 1, len))
}
STR8 => {
let len = get(buf, offset + 1)? as usize;
Some((offset + 2, len))
}
STR16 => {
let len = read_u16_be(buf, offset + 1)? as usize;
Some((offset + 3, len))
}
STR32 => {
let len = read_u32_be(buf, offset + 1)? as usize;
Some((offset + 5, len))
}
_ => None,
}
}
pub fn read_bool(buf: &[u8], offset: usize) -> Option<bool> {
match get(buf, offset)? {
TRUE => Some(true),
FALSE => Some(false),
_ => None,
}
}
pub fn read_null(buf: &[u8], offset: usize) -> bool {
get(buf, offset) == Some(NIL)
}
pub fn map_header(buf: &[u8], offset: usize) -> Option<(usize, usize)> {
let tag = get(buf, offset)?;
match tag {
0x80..=0x8f => Some(((tag & 0x0f) as usize, offset + 1)),
MAP16 => Some((read_u16_be(buf, offset + 1)? as usize, offset + 3)),
MAP32 => Some((read_u32_be(buf, offset + 1)? as usize, offset + 5)),
_ => None,
}
}
pub fn array_header(buf: &[u8], offset: usize) -> Option<(usize, usize)> {
let tag = get(buf, offset)?;
match tag {
0x90..=0x9f => Some(((tag & 0x0f) as usize, offset + 1)),
ARRAY16 => Some((read_u16_be(buf, offset + 1)? as usize, offset + 3)),
ARRAY32 => Some((read_u32_be(buf, offset + 1)? as usize, offset + 5)),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::msgpack_scan::reader::skip_value;
use serde_json::json;
fn encode(v: &serde_json::Value) -> Vec<u8> {
nodedb_types::json_msgpack::json_to_msgpack(v).expect("encode")
}
#[test]
fn read_f64_fixint() {
assert_eq!(read_f64(&[42u8], 0), Some(42.0));
}
#[test]
fn read_f64_negative_fixint() {
assert_eq!(read_f64(&[0xffu8], 0), Some(-1.0));
}
#[test]
fn read_f64_float64() {
let buf = encode(&json!(std::f64::consts::PI));
assert_eq!(read_f64(&buf, 0), Some(std::f64::consts::PI));
}
#[test]
fn read_f64_uint16() {
let buf = encode(&json!(1000));
assert_eq!(read_f64(&buf, 0), Some(1000.0));
}
#[test]
fn read_f64_float32() {
let buf = [0xca, 0x3f, 0xc0, 0x00, 0x00];
let val = read_f64(&buf, 0).unwrap();
assert!((val - 1.5).abs() < 1e-6);
}
#[test]
fn read_i64_values() {
assert_eq!(read_i64(&[42u8], 0), Some(42));
assert_eq!(read_i64(&[0xffu8], 0), Some(-1));
let buf = encode(&json!(300));
assert_eq!(read_i64(&buf, 0), Some(300));
let buf = encode(&json!(-500));
assert_eq!(read_i64(&buf, 0), Some(-500));
}
#[test]
fn uint64_above_i64_max_never_wraps() {
let mut buf = vec![UINT64];
buf.extend_from_slice(&u64::MAX.to_be_bytes());
assert_eq!(read_i64(&buf, 0), None);
assert_eq!(read_integer(&buf, 0), Some(i128::from(u64::MAX)));
let mut edge = vec![UINT64];
edge.extend_from_slice(&(i64::MAX as u64).to_be_bytes());
assert_eq!(read_i64(&edge, 0), Some(i64::MAX));
let mut neg = vec![INT64];
neg.extend_from_slice(&i64::MIN.to_be_bytes());
assert_eq!(read_i64(&neg, 0), Some(i64::MIN));
assert_eq!(read_integer(&neg, 0), Some(i128::from(i64::MIN)));
}
#[test]
fn read_numeric_keeps_integers_exact() {
use crate::numeric_cmp::Numeric;
let buf = encode(&json!(9_007_199_254_740_993_i64));
assert!(matches!(
read_numeric(&buf, 0),
Some(Numeric::Int(9_007_199_254_740_993))
));
let buf = encode(&json!(1.5));
assert!(matches!(read_numeric(&buf, 0), Some(Numeric::Float(f)) if f == 1.5));
let buf = encode(&json!("1"));
assert!(read_numeric(&buf, 0).is_none());
assert!(read_integer(&buf, 0).is_none());
}
#[test]
fn read_str_fixstr() {
let buf = encode(&json!("hi"));
assert_eq!(read_str(&buf, 0), Some("hi"));
}
#[test]
fn read_str_str8() {
let long = "a".repeat(40);
let buf = encode(&json!(long));
assert_eq!(read_str(&buf, 0), Some(long.as_str()));
}
#[test]
fn read_bool_values() {
assert_eq!(read_bool(&[TRUE], 0), Some(true));
assert_eq!(read_bool(&[FALSE], 0), Some(false));
assert_eq!(read_bool(&[NIL], 0), None);
}
#[test]
fn read_null_check() {
assert!(read_null(&[NIL], 0));
assert!(!read_null(&[TRUE], 0));
}
#[test]
fn map_header_fixmap() {
let buf = encode(&json!({"x": 1}));
let (count, _data_offset) = map_header(&buf, 0).unwrap();
assert_eq!(count, 1);
}
#[test]
fn array_header_fixarray() {
let buf = encode(&json!([10, 20, 30]));
let (count, data_offset) = array_header(&buf, 0).unwrap();
assert_eq!(count, 3);
assert_eq!(read_i64(&buf, data_offset), Some(10));
}
#[test]
fn canonical_integer_smallest_representation() {
let buf = encode(&json!(42));
assert_eq!(buf.len(), 1);
assert_eq!(buf[0], 42);
let buf = encode(&json!(0));
assert_eq!(buf.len(), 1);
assert_eq!(buf[0], 0);
let buf = encode(&json!(127));
assert_eq!(buf.len(), 1);
assert_eq!(buf[0], 127);
let buf = encode(&json!(128));
assert_eq!(buf[0], 0xd1); assert_eq!(buf.len(), 3);
let buf = encode(&json!(-1));
assert_eq!(buf.len(), 1);
assert_eq!(buf[0], 0xff);
let buf = encode(&json!(-32));
assert_eq!(buf.len(), 1);
assert_eq!(buf[0], 0xe0); }
#[test]
fn canonical_map_keys_sorted() {
let buf = encode(&json!({"z": 1, "a": 2, "m": 3}));
let (count, mut pos) = map_header(&buf, 0).unwrap();
assert_eq!(count, 3);
let mut keys = Vec::new();
for _ in 0..count {
let key = read_str(&buf, pos).unwrap();
keys.push(key.to_string());
pos = skip_value(&buf, pos).unwrap(); pos = skip_value(&buf, pos).unwrap(); }
assert_eq!(keys, vec!["a", "m", "z"]);
}
#[test]
fn canonical_deterministic_bytes() {
let doc1 = encode(&json!({"name": "alice", "age": 30, "active": true}));
let doc2 = encode(&json!({"age": 30, "active": true, "name": "alice"}));
assert_eq!(
doc1, doc2,
"same logical doc must produce identical msgpack bytes"
);
}
#[test]
fn canonical_nested_map_keys_sorted() {
let buf = encode(&json!({"outer": {"z": 1, "a": 2}}));
let (start, _end) = crate::msgpack_scan::field::extract_field(&buf, 0, "outer").unwrap();
let (count, mut pos) = map_header(&buf, start).unwrap();
assert_eq!(count, 2);
let key1 = read_str(&buf, pos).unwrap();
pos = skip_value(&buf, pos).unwrap();
pos = skip_value(&buf, pos).unwrap();
let key2 = read_str(&buf, pos).unwrap();
assert_eq!(key1, "a");
assert_eq!(key2, "z");
}
#[test]
fn read_bin_advance_all_widths() {
let mut off = 0;
let buf = [BIN8, 3, 0xde, 0xad, 0xbe, 0xff];
assert_eq!(
read_bin_advance(&buf, &mut off),
Some(&[0xde, 0xad, 0xbe][..])
);
assert_eq!(off, 5);
let mut off = 0;
let buf = [BIN16, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04];
assert_eq!(
read_bin_advance(&buf, &mut off),
Some(&[0x01, 0x02, 0x03, 0x04][..])
);
assert_eq!(off, 7);
let mut off = 0;
let buf = [BIN32, 0x00, 0x00, 0x00, 0x02, 0xaa, 0xbb];
assert_eq!(read_bin_advance(&buf, &mut off), Some(&[0xaa, 0xbb][..]));
assert_eq!(off, 7);
let mut off = 0;
let buf = [0xc0u8]; assert_eq!(read_bin_advance(&buf, &mut off), None);
assert_eq!(off, 0);
let mut off = 0;
let buf = [BIN8, 5, 0x01]; assert_eq!(read_bin_advance(&buf, &mut off), None);
}
#[test]
fn read_u32_advance_all_widths() {
let mut off = 0;
assert_eq!(read_u32_advance(&[42u8], &mut off), Some(42));
assert_eq!(off, 1);
let mut off = 0;
assert_eq!(read_u32_advance(&[UINT8, 200], &mut off), Some(200));
assert_eq!(off, 2);
let mut off = 0;
let buf = [UINT16, 0x12, 0x34];
assert_eq!(read_u32_advance(&buf, &mut off), Some(0x1234));
assert_eq!(off, 3);
let mut off = 0;
let buf = [UINT32, 0xde, 0xad, 0xbe, 0xef];
assert_eq!(read_u32_advance(&buf, &mut off), Some(0xdeadbeef));
assert_eq!(off, 5);
let mut off = 0;
assert_eq!(read_u32_advance(&[0xffu8], &mut off), None); assert_eq!(off, 0);
let mut off = 0;
assert_eq!(read_u32_advance(&[INT8, 5], &mut off), None);
let mut off = 0;
assert_eq!(
read_u32_advance(&[UINT64, 0, 0, 0, 0, 0, 0, 0, 1], &mut off),
None
);
let mut off = 0;
assert_eq!(read_u32_advance(&[UINT16, 0x12], &mut off), None);
}
#[test]
fn read_str_advance_basic() {
let mut off = 0;
let buf = encode(&json!("hi"));
assert_eq!(read_str_advance(&buf, &mut off), Some("hi"));
assert_eq!(off, buf.len());
let buf = encode(&json!(["one", "two"]));
let (count, mut off) = array_header(&buf, 0).unwrap();
assert_eq!(count, 2);
assert_eq!(read_str_advance(&buf, &mut off), Some("one"));
assert_eq!(read_str_advance(&buf, &mut off), Some("two"));
assert_eq!(off, buf.len());
let mut off = 0;
assert_eq!(read_str_advance(&[NIL], &mut off), None);
assert_eq!(off, 0);
}
}