Skip to main content

cynos_binary/
lib.rs

1//! Binary Protocol for high-performance row serialization.
2//!
3//! Provides a compact binary encoding format for transferring row data.
4//! When the `wasm` feature is enabled, includes zero-copy WASM bindings
5//! for direct JS access to encoded buffers.
6//!
7//! ## Binary Format (Row-Major)
8//!
9//! ```text
10//! Header: 16 bytes
11//! +----------+----------+------------+-------+
12//! | row_count| row_stride| var_offset | flags |
13//! | u32      | u32       | u32        | u32   |
14//! +----------+----------+------------+-------+
15//!
16//! Fixed Section (row-major):
17//! Row 0: [null_mask: ceil(cols/8) bytes][col0][col1][col2]
18//! Row 1: [null_mask][col0][col1][col2]
19//! ...
20//!
21//! Variable Section:
22//! [string bytes][bytes data][jsonb data]
23//! ```
24
25#![no_std]
26
27extern crate alloc;
28
29mod encoder;
30mod layout_cache;
31mod schema_layout;
32
33pub use encoder::BinaryEncoder;
34pub use layout_cache::SchemaLayoutCache;
35pub use schema_layout::{ColumnLayout, SchemaLayout};
36
37use alloc::vec::Vec;
38
39/// Header size in bytes
40pub const HEADER_SIZE: usize = 16;
41
42/// Header flags
43pub mod flags {
44    pub const HAS_NULLS: u32 = 1 << 0;
45}
46
47/// Data type IDs for binary encoding
48#[repr(u8)]
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum BinaryDataType {
51    Boolean = 0,
52    Int32 = 1,
53    Int64 = 2,
54    Float64 = 3,
55    String = 4,
56    DateTime = 5,
57    Bytes = 6,
58    Jsonb = 7,
59}
60
61impl BinaryDataType {
62    /// Get the fixed size in bytes for this data type
63    pub fn fixed_size(self) -> usize {
64        match self {
65            BinaryDataType::Boolean => 1,
66            BinaryDataType::Int32 => 4,
67            BinaryDataType::Int64 => 8,   // stored as f64 for JS compatibility
68            BinaryDataType::Float64 => 8,
69            BinaryDataType::String => 8,  // (offset: u32, len: u32)
70            BinaryDataType::DateTime => 8,
71            BinaryDataType::Bytes => 8,   // (offset: u32, len: u32)
72            BinaryDataType::Jsonb => 8,   // (offset: u32, len: u32)
73        }
74    }
75
76    /// Check if this type uses variable-length storage
77    pub fn is_variable_length(self) -> bool {
78        matches!(
79            self,
80            BinaryDataType::String | BinaryDataType::Bytes | BinaryDataType::Jsonb
81        )
82    }
83}
84
85impl From<cynos_core::DataType> for BinaryDataType {
86    fn from(dt: cynos_core::DataType) -> Self {
87        match dt {
88            cynos_core::DataType::Boolean => BinaryDataType::Boolean,
89            cynos_core::DataType::Int32 => BinaryDataType::Int32,
90            cynos_core::DataType::Int64 => BinaryDataType::Int64,
91            cynos_core::DataType::Float64 => BinaryDataType::Float64,
92            cynos_core::DataType::String => BinaryDataType::String,
93            cynos_core::DataType::DateTime => BinaryDataType::DateTime,
94            cynos_core::DataType::Bytes => BinaryDataType::Bytes,
95            cynos_core::DataType::Jsonb => BinaryDataType::Jsonb,
96        }
97    }
98}
99
100/// Binary result buffer returned from execBinary()
101#[cfg_attr(feature = "wasm", wasm_bindgen::prelude::wasm_bindgen)]
102pub struct BinaryResult {
103    buffer: Vec<u8>,
104}
105
106#[cfg(feature = "wasm")]
107use wasm_bindgen::JsCast;
108
109#[cfg(feature = "wasm")]
110#[wasm_bindgen::prelude::wasm_bindgen]
111impl BinaryResult {
112    /// Get pointer to the buffer data (as usize for JS)
113    pub fn ptr(&self) -> usize {
114        self.buffer.as_ptr() as usize
115    }
116
117    /// Get buffer length
118    pub fn len(&self) -> usize {
119        self.buffer.len()
120    }
121
122    /// Check if buffer is empty
123    #[wasm_bindgen(js_name = isEmpty)]
124    pub fn is_empty(&self) -> bool {
125        self.buffer.is_empty()
126    }
127
128    /// Get buffer as Uint8Array (copies data to JS)
129    /// Use asView() for zero-copy access instead.
130    #[wasm_bindgen(js_name = toUint8Array)]
131    pub fn to_uint8_array(&self) -> js_sys::Uint8Array {
132        js_sys::Uint8Array::from(&self.buffer[..])
133    }
134
135    /// Get a zero-copy Uint8Array view into WASM memory.
136    /// WARNING: This view becomes invalid if WASM memory grows or if this BinaryResult is freed.
137    /// The caller must ensure the BinaryResult outlives any use of the returned view.
138    #[wasm_bindgen(js_name = asView)]
139    pub fn as_view(&self) -> js_sys::Uint8Array {
140        let memory = wasm_bindgen::memory();
141        let buffer = memory.dyn_ref::<js_sys::WebAssembly::Memory>()
142            .expect("wasm_bindgen::memory() should return WebAssembly.Memory")
143            .buffer();
144
145        js_sys::Uint8Array::new_with_byte_offset_and_length(
146            &buffer,
147            self.buffer.as_ptr() as u32,
148            self.buffer.len() as u32,
149        )
150    }
151
152    /// Free the buffer memory
153    pub fn free(self) {
154        drop(self);
155    }
156}
157
158#[cfg(not(feature = "wasm"))]
159impl BinaryResult {
160    /// Get pointer to the buffer data
161    pub fn ptr(&self) -> usize {
162        self.buffer.as_ptr() as usize
163    }
164
165    /// Get buffer length
166    pub fn len(&self) -> usize {
167        self.buffer.len()
168    }
169
170    /// Check if buffer is empty
171    pub fn is_empty(&self) -> bool {
172        self.buffer.is_empty()
173    }
174}
175
176impl BinaryResult {
177    /// Create a new BinaryResult from a buffer
178    pub fn new(buffer: Vec<u8>) -> Self {
179        Self { buffer }
180    }
181}