Skip to main content

reifydb_codec/frame/encode/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4pub(crate) mod any;
5mod fixed;
6mod varlen;
7
8use arrow_buffer::BooleanBuffer;
9use reifydb_value::value::{
10	column_view::{ColumnView, ViewData},
11	diff_type::DiffType,
12	frame::frame::Frame,
13};
14use tracing::{Span, instrument};
15
16use crate::{
17	error::EncodeError,
18	frame::{
19		encoding::plain::{encode_bitvec, encode_plain},
20		format::{
21			COL_FLAG_HAS_NONES, Encoding, FRAME_HEADER_SIZE, MESSAGE_HEADER_SIZE, RBCF_MAGIC, RBCF_VERSION,
22		},
23		heuristics::choose_encoding,
24		options::EncodeOptions,
25	},
26	tag::TypeTag,
27	typeinfo::encode_digest_params,
28	unscaled::params,
29};
30
31pub(crate) struct EncodedColumn {
32	pub(crate) type_code: u8,
33	pub(crate) encoding: Encoding,
34	pub(crate) flags: u8,
35	pub(crate) nones: Vec<u8>,
36	pub(crate) data: Vec<u8>,
37	pub(crate) offsets: Vec<u8>,
38	pub(crate) extra: Vec<u8>,
39	pub(crate) row_count: u32,
40}
41
42#[instrument(
43	name = "wire::encode_frames",
44	level = "trace",
45	skip_all,
46	fields(
47		frame_count = frames.len(),
48		total_rows = frames.iter().map(|f| f.batch.num_rows()).sum::<usize>(),
49		bytes,
50	),
51)]
52pub fn encode_frames(frames: &[Frame], options: &EncodeOptions) -> Result<Vec<u8>, EncodeError> {
53	let mut buf = Vec::with_capacity(4096);
54	reserve_message_header(&mut buf);
55	for frame in frames {
56		encode_frame(frame, &mut buf, options)?;
57	}
58	write_message_header(&mut buf, frames.len() as u32);
59	Span::current().record("bytes", buf.len());
60	Ok(buf)
61}
62
63#[inline]
64fn reserve_message_header(buf: &mut Vec<u8>) {
65	buf.extend_from_slice(&[0u8; MESSAGE_HEADER_SIZE]);
66}
67
68#[inline]
69fn write_message_header(buf: &mut [u8], frame_count: u32) {
70	let total_size = buf.len() as u32;
71	buf[0..4].copy_from_slice(&RBCF_MAGIC.to_le_bytes());
72	buf[4..6].copy_from_slice(&RBCF_VERSION.to_le_bytes());
73	buf[6..8].copy_from_slice(&0u16.to_le_bytes());
74	buf[8..12].copy_from_slice(&frame_count.to_le_bytes());
75	buf[12..16].copy_from_slice(&total_size.to_le_bytes());
76}
77
78fn encode_frame(frame: &Frame, buf: &mut Vec<u8>, options: &EncodeOptions) -> Result<(), EncodeError> {
79	let frame_start = buf.len();
80	let row_count = frame.batch.num_rows() as u32;
81	let column_count = frame.batch.num_columns() as u16;
82
83	reserve_frame_header(buf);
84	encode_frame_columns(frame, buf, options)?;
85
86	let frame_size = (buf.len() - frame_start) as u32;
87	let op = frame.op.map_or(0, DiffType::as_u8);
88	write_frame_header(buf, frame_start, row_count, column_count, op, frame_size);
89	Ok(())
90}
91
92#[inline]
93fn reserve_frame_header(buf: &mut Vec<u8>) {
94	buf.extend_from_slice(&[0u8; FRAME_HEADER_SIZE]);
95}
96
97#[inline]
98fn encode_frame_columns(frame: &Frame, buf: &mut Vec<u8>, options: &EncodeOptions) -> Result<(), EncodeError> {
99	let schema = frame.batch.schema_ref();
100	for (field, array) in schema.fields().iter().zip(frame.batch.columns()) {
101		let view = ColumnView::try_from((array, field.as_ref()))
102			.map_err(|error| EncodeError::UnsupportedType(error.to_string()))?;
103		encode_column(field.name(), &view, buf, options)?;
104	}
105	Ok(())
106}
107
108#[inline]
109fn write_frame_header(buf: &mut [u8], frame_start: usize, row_count: u32, column_count: u16, op: u8, frame_size: u32) {
110	let h = frame_start;
111	buf[h..h + 4].copy_from_slice(&row_count.to_le_bytes());
112	buf[h + 4..h + 6].copy_from_slice(&column_count.to_le_bytes());
113	buf[h + 6] = 0;
114	buf[h + 7] = op;
115	buf[h + 8..h + 12].copy_from_slice(&frame_size.to_le_bytes());
116}
117
118fn encode_column(
119	name: &str,
120	view: &ColumnView<'_>,
121	buf: &mut Vec<u8>,
122	options: &EncodeOptions,
123) -> Result<(), EncodeError> {
124	let desired = options.force_encoding.unwrap_or_else(|| choose_encoding(view, options.compression));
125	let enc = try_encode_with(view, desired)?;
126	write_column(name, &enc, buf);
127	Ok(())
128}
129
130fn try_encode_with(view: &ColumnView<'_>, desired: Encoding) -> Result<EncodedColumn, EncodeError> {
131	let row_count = view.len();
132	let has_nones = view.is_nullable() || view.is_none();
133	let nones = match (has_nones, view.logical_nulls()) {
134		(false, _) => Vec::new(),
135		(true, Some(nulls)) => encode_bitvec(nulls.inner()),
136		(true, None) => encode_bitvec(&BooleanBuffer::new_set(row_count)),
137	};
138
139	let row_count = row_count as u32;
140
141	let result = match desired {
142		Encoding::Dict => varlen::try_dict_varlen(view),
143		Encoding::Rle => fixed::try_rle_fixed(view),
144		Encoding::Delta => fixed::try_delta_fixed(view),
145		Encoding::DeltaRle => fixed::try_delta_rle_fixed(view),
146		_ => None,
147	};
148
149	let mut enc = match result {
150		Some(enc) => enc,
151		None => {
152			let plain = encode_plain(view)?;
153			let mut extra = Vec::new();
154			if let ViewData::Digest {
155				inner: digest_inner,
156				accuracy,
157				..
158			} = &view.data
159			{
160				encode_digest_params(digest_inner, *accuracy, &mut extra)?;
161			}
162			EncodedColumn {
163				type_code: plain.type_code,
164				encoding: Encoding::Plain,
165				flags: 0,
166				nones: vec![],
167				data: plain.data,
168				offsets: plain.offsets,
169				extra,
170				row_count,
171			}
172		}
173	};
174
175	if let Some((precision, scale)) = params(&view.base_type()) {
176		enc.extra = vec![precision.value(), scale.value()];
177	}
178
179	if has_nones {
180		enc.type_code = TypeTag::of_type(&view.get_type())?.byte();
181		enc.nones = nones;
182		enc.flags |= COL_FLAG_HAS_NONES;
183	}
184	enc.row_count = row_count;
185	Ok(enc)
186}
187
188fn write_column(name: &str, enc: &EncodedColumn, buf: &mut Vec<u8>) {
189	let name_bytes = name.as_bytes();
190	let name_len = name_bytes.len() as u16;
191	let name_pad = (4 - (name_bytes.len() % 4)) % 4;
192
193	buf.push(enc.type_code);
194	buf.push(enc.encoding as u8);
195	buf.push(enc.flags);
196	buf.push(0);
197	buf.extend_from_slice(&name_len.to_le_bytes());
198	buf.extend_from_slice(&0u16.to_le_bytes());
199	buf.extend_from_slice(&enc.row_count.to_le_bytes());
200	buf.extend_from_slice(&(enc.nones.len() as u32).to_le_bytes());
201	buf.extend_from_slice(&(enc.data.len() as u32).to_le_bytes());
202	buf.extend_from_slice(&(enc.offsets.len() as u32).to_le_bytes());
203	buf.extend_from_slice(&(enc.extra.len() as u32).to_le_bytes());
204
205	buf.extend_from_slice(name_bytes);
206	for _ in 0..name_pad {
207		buf.push(0);
208	}
209
210	buf.extend_from_slice(&enc.nones);
211
212	buf.extend_from_slice(&enc.data);
213
214	buf.extend_from_slice(&enc.offsets);
215
216	buf.extend_from_slice(&enc.extra);
217}