Skip to main content

oxihuman_core/
flatbuffer_stub.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! FlatBuffers builder stub.
6
7/// A FlatBuffers builder that accumulates bytes.
8#[derive(Debug, Default)]
9pub struct FlatBuilder {
10    buf: Vec<u8>,
11    vtables: Vec<usize>,
12    finished: bool,
13}
14
15/// FlatBuffers error.
16#[derive(Debug, Clone, PartialEq)]
17pub enum FlatError {
18    AlreadyFinished,
19    NotFinished,
20    InvalidOffset,
21}
22
23impl std::fmt::Display for FlatError {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        match self {
26            Self::AlreadyFinished => write!(f, "builder already finished"),
27            Self::NotFinished => write!(f, "builder not yet finished"),
28            Self::InvalidOffset => write!(f, "invalid FlatBuffers offset"),
29        }
30    }
31}
32
33impl FlatBuilder {
34    /// Create a new builder.
35    pub fn new() -> Self {
36        Self::default()
37    }
38
39    /// Create a builder with a pre-allocated capacity.
40    pub fn with_capacity(cap: usize) -> Self {
41        FlatBuilder {
42            buf: Vec::with_capacity(cap),
43            vtables: vec![],
44            finished: false,
45        }
46    }
47
48    /// Return the current byte length.
49    pub fn len(&self) -> usize {
50        self.buf.len()
51    }
52
53    /// Return `true` if the buffer is empty.
54    pub fn is_empty(&self) -> bool {
55        self.buf.is_empty()
56    }
57
58    /// Push a `u8` value.
59    pub fn push_u8(&mut self, v: u8) -> Result<usize, FlatError> {
60        if self.finished {
61            return Err(FlatError::AlreadyFinished);
62        }
63        let offset = self.buf.len();
64        self.buf.push(v);
65        Ok(offset)
66    }
67
68    /// Push a `u32` value in little-endian format.
69    pub fn push_u32(&mut self, v: u32) -> Result<usize, FlatError> {
70        if self.finished {
71            return Err(FlatError::AlreadyFinished);
72        }
73        let offset = self.buf.len();
74        self.buf.extend_from_slice(&v.to_le_bytes());
75        Ok(offset)
76    }
77
78    /// Push a byte slice.
79    pub fn push_bytes(&mut self, data: &[u8]) -> Result<usize, FlatError> {
80        if self.finished {
81            return Err(FlatError::AlreadyFinished);
82        }
83        let offset = self.buf.len();
84        self.buf.extend_from_slice(data);
85        Ok(offset)
86    }
87
88    /// Finish the buffer and return the bytes.
89    pub fn finish(mut self) -> Result<Vec<u8>, FlatError> {
90        self.finished = true;
91        Ok(self.buf)
92    }
93
94    /// Return the underlying bytes without consuming the builder.
95    pub fn bytes(&self) -> &[u8] {
96        &self.buf
97    }
98
99    /// Align the buffer to a given power-of-two size.
100    pub fn align(&mut self, alignment: usize) {
101        while !self.buf.len().is_multiple_of(alignment) {
102            self.buf.push(0);
103        }
104    }
105}
106
107/// Read a `u32` from a FlatBuffers byte slice at a given offset.
108pub fn read_u32(data: &[u8], offset: usize) -> Result<u32, FlatError> {
109    if offset + 4 > data.len() {
110        return Err(FlatError::InvalidOffset);
111    }
112    let bytes: [u8; 4] = data[offset..offset + 4].try_into().unwrap_or_default();
113    Ok(u32::from_le_bytes(bytes))
114}
115
116/// Compute the padded size for a given alignment.
117pub fn padded_size(size: usize, alignment: usize) -> usize {
118    let rem = size % alignment;
119    if rem == 0 {
120        size
121    } else {
122        size + alignment - rem
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn test_new_builder_empty() {
132        /* new builder starts empty */
133        let b = FlatBuilder::new();
134        assert!(b.is_empty());
135    }
136
137    #[test]
138    fn test_push_u8() {
139        /* push u8 grows buffer by 1 */
140        let mut b = FlatBuilder::new();
141        b.push_u8(0xAB).expect("should succeed");
142        assert_eq!(b.len(), 1);
143    }
144
145    #[test]
146    fn test_push_u32() {
147        /* push u32 grows buffer by 4 */
148        let mut b = FlatBuilder::new();
149        b.push_u32(0xDEAD_BEEF).expect("should succeed");
150        assert_eq!(b.len(), 4);
151    }
152
153    #[test]
154    fn test_push_after_finish_fails() {
155        /* push after finish returns error */
156        let b = FlatBuilder::new();
157        b.finish().expect("should succeed");
158        /* create new builder to test error */
159        let mut b2 = FlatBuilder::new();
160        b2.finished = true;
161        assert!(b2.push_u8(1).is_err());
162    }
163
164    #[test]
165    fn test_push_bytes() {
166        /* push_bytes copies data */
167        let mut b = FlatBuilder::new();
168        b.push_bytes(&[1, 2, 3]).expect("should succeed");
169        assert_eq!(b.len(), 3);
170    }
171
172    #[test]
173    fn test_finish_returns_bytes() {
174        /* finish produces the accumulated bytes */
175        let mut b = FlatBuilder::new();
176        b.push_u8(99).expect("should succeed");
177        let data = b.finish().expect("should succeed");
178        assert_eq!(data, &[99]);
179    }
180
181    #[test]
182    fn test_align_pads_to_boundary() {
183        /* align pads buffer to next boundary */
184        let mut b = FlatBuilder::new();
185        b.push_u8(1).expect("should succeed");
186        b.align(4);
187        assert_eq!(b.len() % 4, 0);
188    }
189
190    #[test]
191    fn test_read_u32_ok() {
192        /* read_u32 decodes little-endian correctly */
193        let data = [1u8, 0, 0, 0];
194        assert_eq!(read_u32(&data, 0).expect("should succeed"), 1);
195    }
196
197    #[test]
198    fn test_read_u32_overflow() {
199        /* read_u32 with bad offset returns error */
200        let data = [0u8; 3];
201        assert!(read_u32(&data, 0).is_err());
202    }
203
204    #[test]
205    fn test_padded_size() {
206        /* padded_size rounds up correctly */
207        assert_eq!(padded_size(5, 4), 8);
208        assert_eq!(padded_size(8, 4), 8);
209    }
210}