Skip to main content

oxihuman_core/
message_pack_codec.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! MessagePack encode/decode stub.
6
7/// A MessagePack value.
8#[derive(Debug, Clone, PartialEq)]
9pub enum MsgValue {
10    Nil,
11    Bool(bool),
12    Int(i64),
13    Float(f64),
14    Str(String),
15    Bin(Vec<u8>),
16    Array(Vec<MsgValue>),
17    Map(Vec<(MsgValue, MsgValue)>),
18}
19
20/// MessagePack codec error.
21#[derive(Debug, Clone, PartialEq)]
22pub enum MsgError {
23    UnexpectedEnd,
24    UnknownFormat(u8),
25    InvalidUtf8,
26    LengthOverflow,
27}
28
29impl std::fmt::Display for MsgError {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        match self {
32            Self::UnexpectedEnd => write!(f, "unexpected end of buffer"),
33            Self::UnknownFormat(b) => write!(f, "unknown format byte: 0x{b:02x}"),
34            Self::InvalidUtf8 => write!(f, "invalid UTF-8 in string"),
35            Self::LengthOverflow => write!(f, "length field overflows usize"),
36        }
37    }
38}
39
40/// Encode a `MsgValue` into a byte buffer (stub: minimal subset).
41pub fn encode(val: &MsgValue, buf: &mut Vec<u8>) {
42    match val {
43        MsgValue::Nil => buf.push(0xc0),
44        MsgValue::Bool(true) => buf.push(0xc3),
45        MsgValue::Bool(false) => buf.push(0xc2),
46        MsgValue::Int(i) => {
47            if (0..=127).contains(i) {
48                buf.push(*i as u8);
49            } else {
50                buf.push(0xd3);
51                buf.extend_from_slice(&i.to_be_bytes());
52            }
53        }
54        MsgValue::Float(f) => {
55            buf.push(0xcb);
56            buf.extend_from_slice(&f.to_bits().to_be_bytes());
57        }
58        MsgValue::Str(s) => {
59            let bytes = s.as_bytes();
60            if bytes.len() <= 31 {
61                buf.push(0xa0 | bytes.len() as u8);
62            } else {
63                buf.push(0xd9);
64                buf.push(bytes.len() as u8);
65            }
66            buf.extend_from_slice(bytes);
67        }
68        MsgValue::Bin(b) => {
69            buf.push(0xc4);
70            buf.push(b.len() as u8);
71            buf.extend_from_slice(b);
72        }
73        MsgValue::Array(arr) => {
74            buf.push(0x90 | arr.len().min(15) as u8);
75            for item in arr {
76                encode(item, buf);
77            }
78        }
79        MsgValue::Map(map) => {
80            buf.push(0x80 | map.len().min(15) as u8);
81            for (k, v) in map {
82                encode(k, buf);
83                encode(v, buf);
84            }
85        }
86    }
87}
88
89/// Return the encoded size of a value (stub).
90pub fn encoded_size(val: &MsgValue) -> usize {
91    match val {
92        MsgValue::Nil | MsgValue::Bool(_) => 1,
93        MsgValue::Int(i) => {
94            if (0..=127).contains(i) {
95                1
96            } else {
97                9
98            }
99        }
100        MsgValue::Float(_) => 9,
101        MsgValue::Str(s) => 1 + s.len(),
102        MsgValue::Bin(b) => 2 + b.len(),
103        MsgValue::Array(a) => 1 + a.iter().map(encoded_size).sum::<usize>(),
104        MsgValue::Map(m) => {
105            1 + m
106                .iter()
107                .map(|(k, v)| encoded_size(k) + encoded_size(v))
108                .sum::<usize>()
109        }
110    }
111}
112
113/// Return `true` if two encoded buffers represent equal values.
114pub fn buffers_equal(a: &[u8], b: &[u8]) -> bool {
115    a == b
116}
117
118/// Count the number of top-level items in an array value.
119pub fn array_len(val: &MsgValue) -> usize {
120    if let MsgValue::Array(arr) = val {
121        arr.len()
122    } else {
123        0
124    }
125}
126
127/// Return `true` if the value is `Nil`.
128pub fn is_nil(val: &MsgValue) -> bool {
129    matches!(val, MsgValue::Nil)
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    #[test]
137    fn test_encode_nil() {
138        /* nil encodes to 0xc0 */
139        let mut buf = vec![];
140        encode(&MsgValue::Nil, &mut buf);
141        assert_eq!(buf, &[0xc0]);
142    }
143
144    #[test]
145    fn test_encode_bool_true() {
146        /* true encodes to 0xc3 */
147        let mut buf = vec![];
148        encode(&MsgValue::Bool(true), &mut buf);
149        assert_eq!(buf, &[0xc3]);
150    }
151
152    #[test]
153    fn test_encode_bool_false() {
154        /* false encodes to 0xc2 */
155        let mut buf = vec![];
156        encode(&MsgValue::Bool(false), &mut buf);
157        assert_eq!(buf, &[0xc2]);
158    }
159
160    #[test]
161    fn test_encode_positive_fixint() {
162        /* small int uses positive fixint */
163        let mut buf = vec![];
164        encode(&MsgValue::Int(42), &mut buf);
165        assert_eq!(buf[0], 42);
166    }
167
168    #[test]
169    fn test_encode_str() {
170        /* short string uses fixstr format */
171        let mut buf = vec![];
172        encode(&MsgValue::Str("hi".to_string()), &mut buf);
173        assert_eq!(buf[0], 0xa0 | 2);
174    }
175
176    #[test]
177    fn test_is_nil() {
178        /* nil detection */
179        assert!(is_nil(&MsgValue::Nil));
180        assert!(!is_nil(&MsgValue::Bool(false)));
181    }
182
183    #[test]
184    fn test_array_len() {
185        /* array_len counts items */
186        let v = MsgValue::Array(vec![MsgValue::Nil, MsgValue::Bool(true)]);
187        assert_eq!(array_len(&v), 2);
188    }
189
190    #[test]
191    fn test_encoded_size_nil() {
192        /* nil size is 1 */
193        assert_eq!(encoded_size(&MsgValue::Nil), 1);
194    }
195
196    #[test]
197    fn test_buffers_equal() {
198        /* identical buffers are equal */
199        assert!(buffers_equal(&[1, 2, 3], &[1, 2, 3]));
200        assert!(!buffers_equal(&[1], &[2]));
201    }
202
203    #[test]
204    fn test_encode_bin() {
205        /* binary value includes header + data */
206        let mut buf = vec![];
207        encode(&MsgValue::Bin(vec![0xDE, 0xAD]), &mut buf);
208        assert_eq!(buf[0], 0xc4);
209        assert_eq!(buf[1], 2);
210    }
211}