lance_encoding/encodings/logical/
primitive.rs1use std::{any::Any, collections::VecDeque, fmt::Debug, ops::Range, sync::Arc, vec};
5
6use crate::format::metadata::{self, CompressiveEncoding};
7use arrow_buffer::BooleanBufferBuilder;
8use arrow_schema::DataType;
9use bytes::Bytes;
10use futures::{FutureExt, future::BoxFuture};
11use lance_core::{
12 cache::{CacheKey, CacheKeySchema, Context, DeepSizeOf, KeyBuilder, LanceCache},
13 error::Error,
14};
15
16use crate::{compression::DecompressionStrategy, data::DataBlock};
17use lance_core::Result;
18
19use crate::{
20 EncodingsIo,
21 buffer::LanceBuffer,
22 data::DataBlockBuilder,
23 decoder::{DecodePageTask, DecodedPage, StructuralPageDecoder},
24 repdef::RepDefUnraveler,
25};
26
27#[cfg(test)]
29use crate::format::EncodingUtils;
30
31pub mod blob;
32mod chunk_index;
33pub mod constant;
34pub mod dict;
35pub mod fullzip;
36pub mod miniblock;
37mod planar;
38pub(crate) mod sparse;
39
40mod all_null;
41mod field;
42mod fullzip_decode;
43mod miniblock_decode;
44mod structural_encoder;
45
46pub(crate) use all_null::LevelCodec;
47pub use all_null::{
48 ComplexAllNullPageDecoder, ComplexAllNullScheduler, DecodeComplexAllNullTask,
49 SimpleAllNullPageDecoder, SimpleAllNullScheduler,
50};
51pub use field::{
52 StructuralCompositeDecodeArrayTask, StructuralPrimitiveFieldDecoder,
53 StructuralPrimitiveFieldScheduler,
54};
55pub use fullzip_decode::FullZipScheduler;
56pub use miniblock_decode::MiniBlockScheduler;
57pub use structural_encoder::{PrimitivePageEncoding, PrimitiveStructuralEncoder};
58
59const FILL_BYTE: u8 = 0xFE;
60const DEFAULT_DICT_DIVISOR: u64 = 2;
61const DEFAULT_DICT_MAX_CARDINALITY: u64 = 100_000;
62const DEFAULT_DICT_SIZE_RATIO: f64 = 0.8;
63const DEFAULT_DICT_MAX_SIZE_BYTES: usize = 8 * 1024 * 1024;
65const DEFAULT_DICT_VALUES_COMPRESSION: &str = "lz4";
66
67const MAX_LEGACY_RLE_LEVELS: u64 = ((u16::MAX as u64 - std::mem::size_of::<u64>() as u64)
72 / (std::mem::size_of::<u16>() as u64 + std::mem::size_of::<u8>() as u64))
73 * u8::MAX as u64;
74
75struct PageLoadTask {
76 decoder_fut: BoxFuture<'static, Result<Box<dyn StructuralPageDecoder>>>,
77 num_rows: u64,
78}
79
80#[derive(Debug)]
85struct PageInitialization {
86 buffer_ranges: Vec<Option<Range<u64>>>,
87}
88
89impl PageInitialization {
90 fn new(buffer_ranges: impl IntoIterator<Item = Option<Range<u64>>>) -> Self {
91 Self {
92 buffer_ranges: buffer_ranges.into_iter().collect(),
93 }
94 }
95
96 fn required_ranges(&self) -> impl Iterator<Item = &Range<u64>> {
97 self.buffer_ranges.iter().flatten()
98 }
99
100 fn num_required_ranges(&self) -> usize {
101 self.required_ranges().count()
102 }
103
104 fn with_buffers(self, buffers: Vec<Bytes>) -> Result<PageInitializationBuffers> {
105 let expected = self.num_required_ranges();
106 if buffers.len() != expected {
107 return Err(Error::invalid_input_source(
108 format!(
109 "Page initialization received {} buffers for {expected} declared ranges",
110 buffers.len()
111 )
112 .into(),
113 ));
114 }
115
116 let mut buffers = buffers.into_iter();
117 let mut buffer_slots = Vec::with_capacity(self.buffer_ranges.len());
118 for range in self.buffer_ranges {
119 if range.is_some() {
120 let buffer = buffers.next().ok_or_else(|| {
121 Error::internal("Page initialization buffer was not assigned")
122 })?;
123 buffer_slots.push(Some(buffer));
124 } else {
125 buffer_slots.push(None);
126 }
127 }
128 debug_assert!(
129 buffers.next().is_none(),
130 "validated page initialization buffer count must be exhausted"
131 );
132 Ok(PageInitializationBuffers { buffer_slots })
133 }
134}
135
136#[derive(Debug)]
137struct PageInitializationBuffers {
138 buffer_slots: Vec<Option<Bytes>>,
139}
140
141impl PageInitializationBuffers {
142 fn try_into_array<const N: usize>(self, layout: &str) -> Result<[Option<Bytes>; N]> {
143 let actual = self.buffer_slots.len();
144 self.buffer_slots.try_into().map_err(|_| {
145 Error::internal(format!(
146 "{layout} page initialization declared {actual} buffer slots, expected {N}"
147 ))
148 })
149 }
150
151 fn into_required(self, layout: &str) -> Result<Vec<Bytes>> {
152 self.buffer_slots
153 .into_iter()
154 .enumerate()
155 .map(|(index, buffer)| {
156 buffer.ok_or_else(|| {
157 Error::internal(format!(
158 "{layout} page initialization buffer slot {index} is absent"
159 ))
160 })
161 })
162 .collect()
163 }
164}
165
166trait StructuralPageScheduler: std::fmt::Debug + Send {
169 fn set_cache(&mut self, _cache: Arc<LanceCache>) {}
172
173 fn needs_initialization(&self) -> bool {
175 true
176 }
177
178 fn init_layout(&self) -> Result<PageInitialization>;
184
185 fn init_from_buffers<'a>(
189 &'a mut self,
190 buffers: PageInitializationBuffers,
191 io: &Arc<dyn EncodingsIo>,
192 ) -> BoxFuture<'a, Result<Arc<dyn CachedPageData>>>;
193
194 fn try_load(&mut self, data: &Arc<dyn CachedPageData>) -> Result<()>;
196
197 fn cache_view(&self) -> PageCacheView {
203 PageCacheView::Native
204 }
205
206 fn schedule_ranges(
215 &self,
216 ranges: &[Range<u64>],
217 io: &Arc<dyn EncodingsIo>,
218 ) -> Result<Vec<PageLoadTask>>;
219}
220
221#[derive(Debug)]
224struct LoadedChunk {
225 data: LanceBuffer,
226 items_in_chunk: u64,
227 byte_range: Range<u64>,
228 chunk_idx: usize,
229}
230
231impl Clone for LoadedChunk {
232 fn clone(&self) -> Self {
233 Self {
234 data: self.data.clone(),
236 items_in_chunk: self.items_in_chunk,
237 byte_range: self.byte_range.clone(),
238 chunk_idx: self.chunk_idx,
239 }
240 }
241}
242
243pub trait CachedPageData: Any + Send + Sync + DeepSizeOf + 'static {
244 fn as_arc_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync + 'static>;
245}
246
247pub struct NoCachedPageData;
248
249impl DeepSizeOf for NoCachedPageData {
250 fn deep_size_of_children(&self, _ctx: &mut Context) -> usize {
251 0
252 }
253}
254impl CachedPageData for NoCachedPageData {
255 fn as_arc_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync + 'static> {
256 self
257 }
258}
259
260#[doc = "Deprecated compatibility wrapper; new code uses per-page PageDataCacheKey entries."]
261pub struct CachedFieldData {
262 pages: Vec<Arc<dyn CachedPageData>>,
263}
264
265impl DeepSizeOf for CachedFieldData {
266 fn deep_size_of_children(&self, ctx: &mut Context) -> usize {
267 self.pages.deep_size_of_children(ctx)
268 }
269}
270
271#[derive(Debug, Clone)]
273pub struct FieldDataCacheKey {
274 pub column_index: u32,
275 pub view_tag: String,
276}
277
278impl CacheKey for FieldDataCacheKey {
279 type ValueType = CachedFieldData;
280
281 fn key(&self) -> std::borrow::Cow<'_, str> {
282 format!("{}:{}", self.column_index, self.view_tag).into()
283 }
284
285 fn type_name() -> &'static str {
286 "FieldData"
287 }
288
289 fn schema() -> CacheKeySchema {
290 CacheKeySchema::new("lance.encoding.logical.primitive.field-data-key", 1)
291 }
292
293 fn write_key(&self, builder: &mut KeyBuilder) {
294 builder.write_u32(self.column_index);
295 builder.write_str(&self.view_tag);
296 }
297}
298
299pub struct CachedPage {
303 data: Arc<dyn CachedPageData>,
304}
305
306impl DeepSizeOf for CachedPage {
307 fn deep_size_of_children(&self, ctx: &mut Context) -> usize {
308 self.data.deep_size_of_children(ctx)
309 }
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq)]
322#[non_exhaustive]
323pub enum PageCacheView {
324 Native,
326 BlobDescriptor,
328 BlobPayload,
330}
331
332impl PageCacheView {
333 fn cache_tag(self) -> &'static str {
334 match self {
335 Self::Native => "n",
336 Self::BlobDescriptor => "bd",
337 Self::BlobPayload => "bp",
338 }
339 }
340}
341
342#[derive(Debug, Clone)]
343pub struct PageDataCacheKey {
344 pub column_index: u32,
345 pub page_index: u32,
346 pub view: PageCacheView,
347 pub target_data_type_tag: Arc<str>,
348}
349
350impl CacheKey for PageDataCacheKey {
351 type ValueType = CachedPage;
352
353 fn key(&self) -> std::borrow::Cow<'_, str> {
354 format!(
355 "v2/{}/{}/{}/{}",
356 self.column_index,
357 self.page_index,
358 self.view.cache_tag(),
359 self.target_data_type_tag
360 )
361 .into()
362 }
363
364 fn type_name() -> &'static str {
365 "PageData"
366 }
367
368 fn schema() -> CacheKeySchema {
369 CacheKeySchema::new("lance.encoding.logical.primitive.page-data-key", 2)
370 }
371
372 fn write_key(&self, builder: &mut KeyBuilder) {
373 builder.write_u32(self.column_index);
374 builder.write_u32(self.page_index);
375 builder.write_variant(match self.view {
376 PageCacheView::Native => 0,
377 PageCacheView::BlobDescriptor => 1,
378 PageCacheView::BlobPayload => 2,
379 });
380 builder.write_str(&self.target_data_type_tag);
381 }
382}
383
384const MINIBLOCK_ALIGNMENT: usize = 8;
399
400#[cfg(test)]
401#[allow(clippy::single_range_in_vec_init)]
402mod tests {
403 use std::sync::Arc;
404
405 use crate::buffer::LanceBuffer;
406 use crate::compression::BlockCompressor;
407 use crate::data::{BlockInfo, DataBlock, FixedWidthDataBlock};
408 use crate::encodings::physical::rle::{RleEncoder, RunLengthWidth};
409
410 use super::PageInitialization;
411
412 #[test]
413 fn page_initialization_preserves_optional_buffer_slots() {
414 let initialization = PageInitialization::new([None, Some(20..22), None, Some(10..11)]);
415 assert_eq!(
416 initialization
417 .required_ranges()
418 .cloned()
419 .collect::<Vec<_>>(),
420 [20..22, 10..11]
421 );
422
423 let buffers = initialization
424 .with_buffers(vec![
425 bytes::Bytes::from_static(b"ab"),
426 bytes::Bytes::from_static(b"c"),
427 ])
428 .unwrap();
429 let [first, second, third, fourth] = buffers.try_into_array("test").unwrap();
430 assert!(first.is_none());
431 assert_eq!(second.unwrap(), bytes::Bytes::from_static(b"ab"));
432 assert!(third.is_none());
433 assert_eq!(fourth.unwrap(), bytes::Bytes::from_static(b"c"));
434
435 let error = PageInitialization::new([Some(0..1)])
436 .with_buffers(Vec::new())
437 .unwrap_err();
438 assert!(matches!(error, lance_core::Error::InvalidInput { .. }));
439 assert!(error.to_string().contains("1 declared ranges"));
440 }
441
442 pub(super) fn encoded_u16_frame(
443 levels: &[u16],
444 run_length_width: RunLengthWidth,
445 ) -> LanceBuffer {
446 let block = DataBlock::FixedWidth(FixedWidthDataBlock {
447 data: LanceBuffer::reinterpret_slice(Arc::from(levels)),
448 bits_per_value: 16,
449 num_values: levels.len() as u64,
450 block_info: BlockInfo::new(),
451 });
452 BlockCompressor::compress(&RleEncoder::with_run_length_width(run_length_width), block)
453 .unwrap()
454 .0
455 .unwrap()
456 }
457}