1mod any;
5mod fixed;
6mod varlen;
7
8use std::{str, sync::Arc};
9
10use arrow_array::{ArrayRef, LargeStringArray, RecordBatch, RecordBatchOptions, make_array};
11use arrow_buffer::{BooleanBuffer, Buffer, NullBuffer};
12use arrow_schema::{FieldRef, Schema};
13use reifydb_value::{
14 encoding::LeBytes,
15 reifydb_assertions,
16 value::{
17 container::varlen_array::blob_array,
18 diff_type::DiffType,
19 frame::frame::Frame,
20 value_type::{
21 ValueType,
22 field::{FieldType, to_field},
23 },
24 },
25};
26
27use crate::{
28 error::DecodeError,
29 frame::{
30 encoding::dict::{decode_dict_blob, decode_dict_utf8},
31 format::{
32 COL_FLAG_HAS_NONES, COLUMN_DESCRIPTOR_SIZE, Encoding, FRAME_HEADER_SIZE, MESSAGE_HEADER_SIZE,
33 RBCF_MAGIC, RBCF_VERSION, dict_index_width_from_flags,
34 },
35 },
36 tag::{TypeTag, ValueKind},
37};
38
39pub fn decode_frames(data: &[u8]) -> Result<Vec<Frame>, DecodeError> {
40 let mut pos = 0;
41
42 check_len(data, pos, MESSAGE_HEADER_SIZE)?;
43 let magic = read_u32(data, pos);
44 pos += 4;
45 if magic != RBCF_MAGIC {
46 return Err(DecodeError::InvalidMagic(magic));
47 }
48 let version = read_u16(data, pos);
49 pos += 2;
50 if version != RBCF_VERSION {
51 return Err(DecodeError::UnsupportedVersion(version));
52 }
53 let _flags = read_u16(data, pos);
54 pos += 2;
55 let frame_count = read_u32(data, pos) as usize;
56 pos += 4;
57 let _total_size = read_u32(data, pos) as usize;
58 pos += 4;
59
60 let mut frames = Vec::with_capacity(frame_count);
61 for _ in 0..frame_count {
62 let (frame, new_pos) = decode_frame(data, pos)?;
63 frames.push(frame);
64 pos = new_pos;
65 }
66
67 reifydb_assertions! {
68 assert!(
69 pos == _total_size,
70 "the RBCF message header declared a total size that disagrees with the bytes consumed while decoding, so the message is truncated or carries trailing bytes and a peer would mis-frame the following message (declared={} consumed={})",
71 _total_size,
72 pos
73 );
74 }
75
76 Ok(frames)
77}
78
79struct FrameHeader {
80 row_count: usize,
81 column_count: usize,
82 op: Option<DiffType>,
83}
84
85fn decode_frame(data: &[u8], start: usize) -> Result<(Frame, usize), DecodeError> {
86 let (header, pos) = read_frame_header(data, start)?;
87 let (columns, pos) = read_frame_columns(data, pos, header.column_count)?;
88
89 Ok((
90 Frame {
91 batch: frame_batch(columns, header.row_count)?,
92 op: header.op,
93 },
94 pos,
95 ))
96}
97
98pub(crate) fn frame_batch(columns: Vec<(FieldRef, ArrayRef)>, row_count: usize) -> Result<RecordBatch, DecodeError> {
99 let (fields, arrays): (Vec<FieldRef>, Vec<ArrayRef>) = columns.into_iter().unzip();
100 RecordBatch::try_new_with_options(
101 Arc::new(Schema::new(fields)),
102 arrays,
103 &RecordBatchOptions::new().with_row_count(Some(row_count)),
104 )
105 .map_err(|error| DecodeError::InvalidData(format!("frame columns do not form a batch: {error}")))
106}
107
108#[inline]
109fn read_frame_header(data: &[u8], start: usize) -> Result<(FrameHeader, usize), DecodeError> {
110 let mut pos = start;
111 check_len(data, pos, FRAME_HEADER_SIZE)?;
112 let row_count = read_u32(data, pos) as usize;
113 pos += 4;
114 let column_count = read_u16(data, pos) as usize;
115 pos += 2;
116 let meta_flags = data[pos];
117 if meta_flags != 0 {
118 return Err(DecodeError::InvalidData(format!("frame meta byte must be 0, found 0x{meta_flags:02X}")));
119 }
120 pos += 1;
121 let op = match data[pos] {
122 0 => None,
123 raw => Some(DiffType::from_u8(raw)
124 .ok_or_else(|| DecodeError::InvalidData(format!("unknown frame op {raw}")))?),
125 };
126 pos += 1;
127 let _frame_size = read_u32(data, pos);
128 pos += 4;
129 Ok((
130 FrameHeader {
131 row_count,
132 column_count,
133 op,
134 },
135 pos,
136 ))
137}
138
139#[inline]
140fn read_frame_columns(
141 data: &[u8],
142 mut pos: usize,
143 column_count: usize,
144) -> Result<(Vec<(FieldRef, ArrayRef)>, usize), DecodeError> {
145 let mut columns = Vec::with_capacity(column_count);
146 for _ in 0..column_count {
147 let (col, new_pos) = decode_column(data, pos)?;
148 columns.push(col);
149 pos = new_pos;
150 }
151 Ok((columns, pos))
152}
153
154fn decode_column(data: &[u8], start: usize) -> Result<((FieldRef, ArrayRef), usize), DecodeError> {
155 let mut pos = start;
156 check_len(data, pos, COLUMN_DESCRIPTOR_SIZE)?;
157
158 let type_code = data[pos];
159 pos += 1;
160 let encoding_byte = data[pos];
161 pos += 1;
162 let flags = data[pos];
163 pos += 1;
164 let _reserved = data[pos];
165 pos += 1;
166 let name_len = read_u16(data, pos) as usize;
167 pos += 2;
168 let _reserved2 = read_u16(data, pos);
169 pos += 2;
170 let row_count = read_u32(data, pos) as usize;
171 pos += 4;
172 let nones_len = read_u32(data, pos) as usize;
173 pos += 4;
174 let data_len = read_u32(data, pos) as usize;
175 pos += 4;
176 let offsets_len = read_u32(data, pos) as usize;
177 pos += 4;
178 let extra_len = read_u32(data, pos) as usize;
179 pos += 4;
180
181 let encoding = Encoding::from_u8(encoding_byte).ok_or(DecodeError::UnknownEncoding(encoding_byte))?;
182 let has_nones = flags & COL_FLAG_HAS_NONES != 0;
183 let tag = TypeTag::from_byte(type_code)?;
184 let depth = tag.depth() as usize;
185 let base_code = tag.kind_bits();
186
187 check_len(data, pos, name_len)?;
188 let name = str::from_utf8(&data[pos..pos + name_len])
189 .map_err(|e| DecodeError::InvalidData(format!("invalid column name: {}", e)))?
190 .to_string();
191 pos += name_len;
192 let name_pad = (4 - (name_len % 4)) % 4;
193 pos += name_pad;
194
195 let result = (|| -> Result<((FieldRef, ArrayRef), usize), DecodeError> {
196 let mut pos = pos;
197
198 if has_nones != (depth > 0) {
199 return Err(DecodeError::InvalidData(format!(
200 "column type code 0x{type_code:02X} has option depth {depth} but the has-nones flag is {}",
201 if has_nones {
202 "set"
203 } else {
204 "clear"
205 }
206 )));
207 }
208 if depth > 1 {
209 return Err(DecodeError::InvalidData(format!(
210 "column type code 0x{type_code:02X} has option depth {depth}, but a column holds at most one option layer"
211 )));
212 }
213 let layer_len = row_count.div_ceil(8);
214 if nones_len != depth * layer_len {
215 return Err(DecodeError::InvalidData(format!(
216 "nones length {nones_len} disagrees with option depth {depth} and row count {row_count} (expected {})",
217 depth * layer_len
218 )));
219 }
220 check_len(data, pos, nones_len)?;
221 let layers: Vec<BooleanBuffer> = (0..depth)
222 .map(|layer| {
223 let start = pos + layer * layer_len;
224 decode_bitvec(&data[start..start + layer_len], row_count)
225 })
226 .collect();
227 pos += nones_len;
228
229 check_len(data, pos, data_len)?;
230 let data_bytes = &data[pos..pos + data_len];
231 pos += data_len;
232
233 check_len(data, pos, offsets_len)?;
234 let offsets_bytes = &data[pos..pos + offsets_len];
235 pos += offsets_len;
236
237 check_len(data, pos, extra_len)?;
238 let extra_bytes = &data[pos..pos + extra_len];
239 pos += extra_len;
240
241 let (value_type, array) = decode_column_dispatch(
242 base_code,
243 encoding,
244 flags,
245 row_count,
246 data_bytes,
247 offsets_bytes,
248 extra_bytes,
249 layers.last(),
250 )?;
251
252 Ok((decoded_column(&name, value_type, array, layers.into_iter().next())?, pos))
253 })()
254 .map_err(|e| DecodeError::ColumnDecodeFailed {
255 column_name: name.clone(),
256 row_index: None,
257 source: Box::new(e),
258 })?;
259
260 Ok(result)
261}
262
263pub(crate) fn decoded_column(
264 name: &str,
265 value_type: ValueType,
266 array: ArrayRef,
267 layer: Option<BooleanBuffer>,
268) -> Result<(FieldRef, ArrayRef), DecodeError> {
269 let declared_type = matches!(value_type, ValueType::List(_) | ValueType::Record(_) | ValueType::Tuple(_))
270 .then(|| value_type.clone());
271 let (value_type, array) = match layer {
272 Some(layer) => (ValueType::Option(Box::new(value_type)), attach_layer(array, layer)?),
273 None if array.logical_null_count() == 0 => (value_type, array),
274 None if matches!(value_type, ValueType::Digest { .. }) => {
275 (ValueType::Option(Box::new(value_type)), array)
276 }
277 None => {
278 return Err(DecodeError::InvalidData(format!(
279 "column of type {value_type:?} holds {} none cells but has no option layer",
280 array.logical_null_count()
281 )));
282 }
283 };
284 let field_type = FieldType {
285 value_type: Some(value_type),
286 declared_type,
287 ..FieldType::default()
288 };
289 Ok((Arc::new(to_field(name, &field_type)), array))
290}
291
292fn attach_layer(array: ArrayRef, layer: BooleanBuffer) -> Result<ArrayRef, DecodeError> {
293 let nulls = NullBuffer::union(Some(&NullBuffer::new(layer)), array.logical_nulls().as_ref());
294 let data =
295 array.to_data().into_builder().nulls(nulls).build().map_err(|error| {
296 DecodeError::InvalidData(format!("option layer does not fit the column: {error}"))
297 })?;
298 Ok(make_array(data))
299}
300
301pub(crate) fn column_type_from_code(type_code: u8) -> Result<ValueType, DecodeError> {
302 TypeTag::from_byte(type_code)?.to_type()
303}
304
305#[allow(clippy::too_many_arguments)]
306fn decode_column_dispatch(
307 type_code: u8,
308 encoding: Encoding,
309 flags: u8,
310 row_count: usize,
311 data: &[u8],
312 offsets: &[u8],
313 extra: &[u8],
314 defined: Option<&BooleanBuffer>,
315) -> Result<(ValueType, ArrayRef), DecodeError> {
316 if type_code == ValueKind::Digest.byte() {
317 if encoding != Encoding::Plain {
318 return Err(DecodeError::InvalidData(format!(
319 "digest column must use plain encoding, found {encoding:?}"
320 )));
321 }
322 return varlen::decode_digest_plain(row_count, data, offsets, extra);
323 }
324 if let Some(kind @ ValueKind::Decimal) = ValueKind::from_byte(type_code) {
325 return fixed::decode_unscaled_column(kind, encoding, row_count, data, extra);
326 }
327 let ty = column_type_from_code(type_code)?;
328
329 let array: ArrayRef = match encoding {
330 Encoding::Plain | Encoding::BitPack => {
331 if ty == ValueType::Any {
332 any::decode_any_column(row_count, data, defined)?
333 } else if let Some(result) = fixed::decode_fixed_plain(type_code, row_count, data) {
334 result?
335 } else if let Some(result) = varlen::decode_varlen_plain(type_code, row_count, data, offsets) {
336 result?
337 } else {
338 return Err(DecodeError::UnsupportedType(format!("{:?}", ty)));
339 }
340 }
341 Encoding::Dict => match ty {
342 ValueType::Utf8 => {
343 let index_width = dict_index_width_from_flags(flags);
344 let strings = decode_dict_utf8(data, extra, row_count, index_width)?;
345 Arc::new(LargeStringArray::from(strings))
346 }
347 ValueType::Blob => {
348 let index_width = dict_index_width_from_flags(flags);
349 let blobs = decode_dict_blob(data, extra, row_count, index_width)?;
350 Arc::new(blob_array(&blobs))
351 }
352 _ => {
353 return Err(DecodeError::InvalidData(format!(
354 "Dict encoding not supported for type {:?}",
355 ty
356 )));
357 }
358 },
359 Encoding::Rle => fixed::decode_rle_column(type_code, row_count, data)?,
360 Encoding::Delta => fixed::decode_delta_column(type_code, row_count, data)?,
361 Encoding::DeltaRle => fixed::decode_delta_rle_column(type_code, row_count, data)?,
362 };
363 Ok((ty, array))
364}
365
366fn decode_bitvec(data: &[u8], len: usize) -> BooleanBuffer {
367 BooleanBuffer::new(Buffer::from_vec(data.to_vec()), 0, len)
368}
369
370#[inline]
371fn read_u16(data: &[u8], pos: usize) -> u16 {
372 u16::read_le(&data[pos..])
373}
374
375#[inline]
376fn read_u32(data: &[u8], pos: usize) -> u32 {
377 u32::read_le(&data[pos..])
378}
379
380fn check_len(data: &[u8], pos: usize, needed: usize) -> Result<(), DecodeError> {
381 if pos + needed > data.len() {
382 Err(DecodeError::UnexpectedEof {
383 expected: needed,
384 available: data.len().saturating_sub(pos),
385 })
386 } else {
387 Ok(())
388 }
389}