Skip to main content

oxihuman_core/
cbor_codec.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! CBOR encode/decode stub.
6
7/// Major type codes used in CBOR.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum CborMajor {
10    Uint = 0,
11    Negint = 1,
12    Bstr = 2,
13    Tstr = 3,
14    Array = 4,
15    Map = 5,
16    Tag = 6,
17    Float = 7,
18}
19
20/// A CBOR value.
21#[derive(Debug, Clone, PartialEq)]
22pub enum CborValue {
23    Uint(u64),
24    Negint(i64),
25    Bstr(Vec<u8>),
26    Tstr(String),
27    Array(Vec<CborValue>),
28    Map(Vec<(CborValue, CborValue)>),
29    Bool(bool),
30    Null,
31    Float(f64),
32}
33
34/// CBOR codec error.
35#[derive(Debug, Clone, PartialEq)]
36pub enum CborError {
37    UnexpectedEnd,
38    InvalidMajor(u8),
39    InvalidUtf8,
40    NotImplemented(&'static str),
41}
42
43impl std::fmt::Display for CborError {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        match self {
46            Self::UnexpectedEnd => write!(f, "unexpected end of CBOR input"),
47            Self::InvalidMajor(b) => write!(f, "invalid major type: {b}"),
48            Self::InvalidUtf8 => write!(f, "invalid UTF-8 in tstr"),
49            Self::NotImplemented(s) => write!(f, "not implemented: {s}"),
50        }
51    }
52}
53
54/// Encode a `CborValue` to bytes (stub).
55pub fn encode_cbor(val: &CborValue, buf: &mut Vec<u8>) {
56    match val {
57        CborValue::Uint(n) => encode_uint(*n, 0, buf),
58        CborValue::Negint(n) => {
59            let encoded = (-1 - n) as u64;
60            encode_uint(encoded, 1, buf);
61        }
62        CborValue::Bstr(b) => {
63            encode_uint(b.len() as u64, 2, buf);
64            buf.extend_from_slice(b);
65        }
66        CborValue::Tstr(s) => {
67            let bytes = s.as_bytes();
68            encode_uint(bytes.len() as u64, 3, buf);
69            buf.extend_from_slice(bytes);
70        }
71        CborValue::Array(arr) => {
72            encode_uint(arr.len() as u64, 4, buf);
73            for item in arr {
74                encode_cbor(item, buf);
75            }
76        }
77        CborValue::Map(map) => {
78            encode_uint(map.len() as u64, 5, buf);
79            for (k, v) in map {
80                encode_cbor(k, buf);
81                encode_cbor(v, buf);
82            }
83        }
84        CborValue::Bool(true) => buf.push(0xf5),
85        CborValue::Bool(false) => buf.push(0xf4),
86        CborValue::Null => buf.push(0xf6),
87        CborValue::Float(f) => {
88            buf.push(0xfb);
89            buf.extend_from_slice(&f.to_bits().to_be_bytes());
90        }
91    }
92}
93
94fn encode_uint(n: u64, major: u8, buf: &mut Vec<u8>) {
95    let mt = major << 5;
96    if n <= 23 {
97        buf.push(mt | n as u8);
98    } else if n <= 0xFF {
99        buf.push(mt | 24);
100        buf.push(n as u8);
101    } else if n <= 0xFFFF {
102        buf.push(mt | 25);
103        buf.extend_from_slice(&(n as u16).to_be_bytes());
104    } else if n <= 0xFFFF_FFFF {
105        buf.push(mt | 26);
106        buf.extend_from_slice(&(n as u32).to_be_bytes());
107    } else {
108        buf.push(mt | 27);
109        buf.extend_from_slice(&n.to_be_bytes());
110    }
111}
112
113/// Return the byte length of the encoded form.
114pub fn cbor_encoded_len(val: &CborValue) -> usize {
115    let mut buf = vec![];
116    encode_cbor(val, &mut buf);
117    buf.len()
118}
119
120/// Return `true` if the value is null.
121pub fn cbor_is_null(val: &CborValue) -> bool {
122    matches!(val, CborValue::Null)
123}
124
125/// Return the major type of a CBOR value.
126pub fn major_of(val: &CborValue) -> CborMajor {
127    match val {
128        CborValue::Uint(_) => CborMajor::Uint,
129        CborValue::Negint(_) => CborMajor::Negint,
130        CborValue::Bstr(_) => CborMajor::Bstr,
131        CborValue::Tstr(_) => CborMajor::Tstr,
132        CborValue::Array(_) => CborMajor::Array,
133        CborValue::Map(_) => CborMajor::Map,
134        CborValue::Bool(_) | CborValue::Null | CborValue::Float(_) => CborMajor::Float,
135    }
136}
137
138/// Count items in a CBOR array value.
139pub fn cbor_array_len(val: &CborValue) -> usize {
140    if let CborValue::Array(a) = val {
141        a.len()
142    } else {
143        0
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150
151    #[test]
152    fn test_encode_null() {
153        /* null encodes to 0xf6 */
154        let mut buf = vec![];
155        encode_cbor(&CborValue::Null, &mut buf);
156        assert_eq!(buf, &[0xf6]);
157    }
158
159    #[test]
160    fn test_encode_bool_true() {
161        /* true encodes to 0xf5 */
162        let mut buf = vec![];
163        encode_cbor(&CborValue::Bool(true), &mut buf);
164        assert_eq!(buf, &[0xf5]);
165    }
166
167    #[test]
168    fn test_encode_uint_small() {
169        /* small uint uses single byte */
170        let mut buf = vec![];
171        encode_cbor(&CborValue::Uint(10), &mut buf);
172        assert_eq!(buf, &[10]);
173    }
174
175    #[test]
176    fn test_encode_tstr() {
177        /* text string encodes correctly */
178        let mut buf = vec![];
179        encode_cbor(&CborValue::Tstr("a".to_string()), &mut buf);
180        assert_eq!(buf[0], 0x61); /* major 3, length 1 */
181        assert_eq!(buf[1], b'a');
182    }
183
184    #[test]
185    fn test_cbor_is_null() {
186        /* null detection */
187        assert!(cbor_is_null(&CborValue::Null));
188        assert!(!cbor_is_null(&CborValue::Bool(false)));
189    }
190
191    #[test]
192    fn test_major_of_uint() {
193        /* uint major type */
194        assert_eq!(major_of(&CborValue::Uint(0)), CborMajor::Uint);
195    }
196
197    #[test]
198    fn test_array_len() {
199        /* cbor_array_len counts correctly */
200        let v = CborValue::Array(vec![CborValue::Null, CborValue::Null]);
201        assert_eq!(cbor_array_len(&v), 2);
202    }
203
204    #[test]
205    fn test_encoded_len_float() {
206        /* float is 9 bytes */
207        assert_eq!(cbor_encoded_len(&CborValue::Float(0.0)), 9);
208    }
209
210    #[test]
211    fn test_encode_negint() {
212        /* negative integer encodes with major type 1 */
213        let mut buf = vec![];
214        encode_cbor(&CborValue::Negint(-1), &mut buf);
215        assert_eq!(buf[0] >> 5, 1); /* major type 1 */
216    }
217
218    #[test]
219    fn test_encode_bstr() {
220        /* binary string encodes with major type 2 */
221        let mut buf = vec![];
222        encode_cbor(&CborValue::Bstr(vec![0xAB]), &mut buf);
223        assert_eq!(buf[0] >> 5, 2); /* major type 2 */
224    }
225}