beve/
fast.rs

1use crate::ext::Complex;
2use crate::header::*;
3use crate::size::write_size;
4use core::ptr;
5use half::{bf16, f16};
6
7/// Write a typed array header and length for numeric arrays.
8#[inline]
9fn write_typed_array_header_numeric(out: &mut Vec<u8>, class: u8, byte_code: u8, len: usize) {
10    let header = ((byte_code & 0b111) << 5) | ((class & 0b11) << 3) | (TYPE_TYPED_ARRAY & 0b111);
11    out.push(header);
12    write_size(len as u64, out);
13}
14
15/// Write a typed array header for boolean arrays.
16#[inline]
17fn write_typed_array_header_bool(out: &mut Vec<u8>, len: usize) {
18    let header = ((ARRAY_BOOL_OR_STRING & 0b11) << 3) | (TYPE_TYPED_ARRAY & 0b111);
19    out.push(header);
20    write_size(len as u64, out);
21}
22
23/// Write a typed array header for string arrays.
24#[inline]
25fn write_typed_array_header_string(out: &mut Vec<u8>, len: usize) {
26    // For boolean/string category, we use byte_code=1 to indicate string arrays
27    let header = ((1u8) << 5) | ((ARRAY_BOOL_OR_STRING & 0b11) << 3) | (TYPE_TYPED_ARRAY & 0b111);
28    out.push(header);
29    write_size(len as u64, out);
30}
31
32/// A trait implemented for scalar numeric types supported by BEVE typed arrays.
33pub trait BeveTypedSlice: Sized {
34    /// Typed array class (ARRAY_SIGNED/ARRAY_UNSIGNED/ARRAY_FLOAT)
35    const CLASS: u8;
36    /// Byte code (0:1B, 1:2B, 2:4B, 3:8B, 4:16B)
37    const BYTE_CODE: u8;
38    /// Number of bytes per element
39    const ELEM_SIZE: usize;
40    /// Encode one element to little-endian bytes, appending to `out`.
41    fn write_one_le(v: &Self, out: &mut Vec<u8>);
42}
43
44macro_rules! impl_beve_typed_int {
45    ($t:ty, $class:expr, $code:expr) => {
46        impl BeveTypedSlice for $t {
47            const CLASS: u8 = $class;
48            const BYTE_CODE: u8 = $code;
49            const ELEM_SIZE: usize = core::mem::size_of::<$t>();
50            #[inline]
51            fn write_one_le(v: &Self, out: &mut Vec<u8>) {
52                out.extend_from_slice(&v.to_le_bytes());
53            }
54        }
55    };
56}
57
58impl_beve_typed_int!(i8, ARRAY_SIGNED, 0);
59impl_beve_typed_int!(i16, ARRAY_SIGNED, 1);
60impl_beve_typed_int!(i32, ARRAY_SIGNED, 2);
61impl_beve_typed_int!(i64, ARRAY_SIGNED, 3);
62impl_beve_typed_int!(i128, ARRAY_SIGNED, 4);
63
64impl_beve_typed_int!(u8, ARRAY_UNSIGNED, 0);
65impl_beve_typed_int!(u16, ARRAY_UNSIGNED, 1);
66impl_beve_typed_int!(u32, ARRAY_UNSIGNED, 2);
67impl_beve_typed_int!(u64, ARRAY_UNSIGNED, 3);
68impl_beve_typed_int!(u128, ARRAY_UNSIGNED, 4);
69
70impl BeveTypedSlice for f32 {
71    const CLASS: u8 = ARRAY_FLOAT;
72    const BYTE_CODE: u8 = 2; // 4 bytes
73    const ELEM_SIZE: usize = core::mem::size_of::<f32>();
74    #[inline]
75    fn write_one_le(v: &Self, out: &mut Vec<u8>) {
76        out.extend_from_slice(&v.to_le_bytes());
77    }
78}
79impl BeveTypedSlice for f64 {
80    const CLASS: u8 = ARRAY_FLOAT;
81    const BYTE_CODE: u8 = 3; // 8 bytes
82    const ELEM_SIZE: usize = core::mem::size_of::<f64>();
83    #[inline]
84    fn write_one_le(v: &Self, out: &mut Vec<u8>) {
85        out.extend_from_slice(&v.to_le_bytes());
86    }
87}
88impl BeveTypedSlice for bf16 {
89    const CLASS: u8 = ARRAY_FLOAT;
90    const BYTE_CODE: u8 = 0; // Special-case brain float (2 bytes)
91    const ELEM_SIZE: usize = core::mem::size_of::<bf16>();
92    #[inline]
93    fn write_one_le(v: &Self, out: &mut Vec<u8>) {
94        out.extend_from_slice(&v.to_le_bytes());
95    }
96}
97impl BeveTypedSlice for f16 {
98    const CLASS: u8 = ARRAY_FLOAT;
99    const BYTE_CODE: u8 = 1; // 2 bytes
100    const ELEM_SIZE: usize = core::mem::size_of::<f16>();
101    #[inline]
102    fn write_one_le(v: &Self, out: &mut Vec<u8>) {
103        out.extend_from_slice(&v.to_le_bytes());
104    }
105}
106
107/// Write a typed numeric array directly to `out` without serde.
108pub fn write_typed_slice<T: BeveTypedSlice>(out: &mut Vec<u8>, slice: &[T]) {
109    let payload = core::mem::size_of_val(slice);
110    // Reserve for header byte, size prefix (<=8 bytes), and payload.
111    out.reserve(1 + 8 + payload);
112    write_typed_array_header_numeric(out, T::CLASS, T::BYTE_CODE, slice.len());
113    if slice.is_empty() {
114        return;
115    }
116    #[cfg(target_endian = "little")]
117    {
118        let start = out.len();
119        unsafe {
120            let dst = out.as_mut_ptr().add(start);
121            ptr::copy_nonoverlapping(slice.as_ptr() as *const u8, dst, payload);
122            out.set_len(start + payload);
123        }
124    }
125    #[cfg(not(target_endian = "little"))]
126    {
127        for v in slice {
128            T::write_one_le(v, out);
129        }
130    }
131}
132
133/// Encode a typed numeric slice to a new Vec<u8> (BEVE typed array).
134pub fn to_vec_typed_slice<T: BeveTypedSlice>(slice: &[T]) -> Vec<u8> {
135    let payload = slice.len() * T::ELEM_SIZE;
136    let mut out = Vec::with_capacity(1 + 8 + payload);
137    write_typed_slice(&mut out, slice);
138    out
139}
140
141/// Write a boolean typed array (bit-packed) to `out`.
142pub fn write_bool_slice(out: &mut Vec<u8>, slice: &[bool]) {
143    write_typed_array_header_bool(out, slice.len());
144    // Pack MSB-first per Glaze/BEVE interop: first element -> bit7
145    let mut acc: u8 = 0;
146    let mut idx: u8 = 0; // counts elements within the current byte
147    for &b in slice {
148        if b {
149            acc |= 1 << (7 - idx);
150        }
151        idx += 1;
152        if idx == 8 {
153            out.push(acc);
154            acc = 0;
155            idx = 0;
156        }
157    }
158    if idx != 0 {
159        out.push(acc);
160    }
161}
162
163/// Encode a boolean slice to a new Vec<u8> (BEVE typed boolean array).
164pub fn to_vec_bool_slice(slice: &[bool]) -> Vec<u8> {
165    let mut out = Vec::new();
166    write_bool_slice(&mut out, slice);
167    out
168}
169
170/// Write a typed string array to `out` (each element as SIZE | UTF-8 DATA, no per-element header).
171pub fn write_str_slice(out: &mut Vec<u8>, slice: &[&str]) {
172    write_typed_array_header_string(out, slice.len());
173    for s in slice {
174        write_size(s.len() as u64, out);
175        out.extend_from_slice(s.as_bytes());
176    }
177}
178
179/// Encode `&[&str]` to a new Vec<u8> (BEVE typed string array).
180pub fn to_vec_str_slice(slice: &[&str]) -> Vec<u8> {
181    let mut out = Vec::new();
182    write_str_slice(&mut out, slice);
183    out
184}
185
186/// Write a typed string array from `&[String]` to `out`.
187pub fn write_string_slice(out: &mut Vec<u8>, slice: &[String]) {
188    write_typed_array_header_string(out, slice.len());
189    for s in slice {
190        write_size(s.len() as u64, out);
191        out.extend_from_slice(s.as_bytes());
192    }
193}
194
195/// Encode `&[String]` to a new Vec<u8> (BEVE typed string array).
196pub fn to_vec_string_slice(slice: &[String]) -> Vec<u8> {
197    let mut out = Vec::new();
198    write_string_slice(&mut out, slice);
199    out
200}
201
202// -------- Complex numbers (extension) --------
203
204#[inline]
205fn write_complex_header(out: &mut Vec<u8>, is_array: bool, byte_code: u8) {
206    // Extension header + complex header
207    out.push(((EXT_COMPLEX & 0x1f) << 3) | (TYPE_EXTENSION & 0b111));
208    let h = ((byte_code & 0b111) << 5) | ((NUM_FLOAT & 0b11) << 3) | (if is_array { 1 } else { 0 });
209    out.push(h);
210}
211
212pub fn to_vec_complex64(re: f64, im: f64) -> Vec<u8> {
213    let mut out = Vec::new();
214    write_complex_header(&mut out, false, 3);
215    out.extend_from_slice(&re.to_le_bytes());
216    out.extend_from_slice(&im.to_le_bytes());
217    out
218}
219
220pub fn to_vec_complex32(re: f32, im: f32) -> Vec<u8> {
221    let mut out = Vec::new();
222    write_complex_header(&mut out, false, 2);
223    out.extend_from_slice(&re.to_le_bytes());
224    out.extend_from_slice(&im.to_le_bytes());
225    out
226}
227
228pub fn to_vec_complex64_slice(slice: &[Complex<f64>]) -> Vec<u8> {
229    let payload = core::mem::size_of_val(slice);
230    let mut out = Vec::with_capacity(2 + 8 + payload);
231    write_complex_header(&mut out, true, 3);
232    write_size(slice.len() as u64, &mut out);
233    if !slice.is_empty() {
234        #[cfg(target_endian = "little")]
235        {
236            let start = out.len();
237            unsafe {
238                let dst = out.as_mut_ptr().add(start);
239                ptr::copy_nonoverlapping(slice.as_ptr() as *const u8, dst, payload);
240                out.set_len(start + payload);
241            }
242        }
243        #[cfg(not(target_endian = "little"))]
244        {
245            out.reserve(payload);
246            for c in slice {
247                out.extend_from_slice(&c.re.to_le_bytes());
248                out.extend_from_slice(&c.im.to_le_bytes());
249            }
250        }
251    }
252    out
253}
254
255pub fn to_vec_complex32_slice(slice: &[Complex<f32>]) -> Vec<u8> {
256    let payload = core::mem::size_of_val(slice);
257    let mut out = Vec::with_capacity(2 + 8 + payload);
258    write_complex_header(&mut out, true, 2);
259    write_size(slice.len() as u64, &mut out);
260    if !slice.is_empty() {
261        #[cfg(target_endian = "little")]
262        {
263            let start = out.len();
264            unsafe {
265                let dst = out.as_mut_ptr().add(start);
266                ptr::copy_nonoverlapping(slice.as_ptr() as *const u8, dst, payload);
267                out.set_len(start + payload);
268            }
269        }
270        #[cfg(not(target_endian = "little"))]
271        {
272            out.reserve(payload);
273            for c in slice {
274                out.extend_from_slice(&c.re.to_le_bytes());
275                out.extend_from_slice(&c.im.to_le_bytes());
276            }
277        }
278    }
279    out
280}
281
282// -------- Matrices (extension) --------
283
284#[derive(Clone, Copy, Debug, PartialEq, Eq)]
285pub enum MatrixLayoutFast {
286    Right,
287    Left,
288}
289
290pub fn to_vec_matrix_f64(layout: MatrixLayoutFast, extents: &[u64], data: &[f64]) -> Vec<u8> {
291    let mut out = Vec::new();
292    // Extension: matrices
293    out.push(((EXT_MATRICES & 0x1f) << 3) | (TYPE_EXTENSION & 0b111));
294    // Matrix header: bit0 layout (0 row-major/right, 1 column-major/left per spec)
295    let mh = match layout {
296        MatrixLayoutFast::Right => 0u8,
297        MatrixLayoutFast::Left => 1u8,
298    };
299    out.push(mh);
300    // Extents as typed array of u64 (0x74)
301    write_typed_array_header_numeric(&mut out, ARRAY_UNSIGNED, 3, extents.len());
302    for &e in extents {
303        out.extend_from_slice(&e.to_le_bytes());
304    }
305    // Data as typed array of f64
306    write_typed_array_header_numeric(&mut out, ARRAY_FLOAT, 3, data.len());
307    for &v in data {
308        out.extend_from_slice(&v.to_le_bytes());
309    }
310    out
311}