molframe_core/column/encoded/
model.rs1use super::super::bits::{bit_width, pack_mapped, unpack_one};
17use super::iter::ColumnIter;
18use crate::symbol::SymbolId;
19use std::mem::size_of;
20
21pub trait ColumnValue: Copy + PartialEq {
27 const IS_INTEGER: bool;
35
36 fn to_bits(self) -> u64;
38 fn from_bits(bits: u64) -> Option<Self>;
40}
41
42macro_rules! unsigned_column_value {
43 ($($type:ty),*) => {
44 $(impl ColumnValue for $type {
45 const IS_INTEGER: bool = true;
46
47 fn to_bits(self) -> u64 {
48 u64::from(self)
49 }
50
51 fn from_bits(bits: u64) -> Option<Self> {
52 Self::try_from(bits).ok()
53 }
54 })*
55 };
56}
57
58macro_rules! signed_column_value {
59 ($($type:ty),*) => {
60 $(impl ColumnValue for $type {
61 const IS_INTEGER: bool = true;
62
63 fn to_bits(self) -> u64 {
64 i64::from(self).cast_unsigned()
65 }
66
67 fn from_bits(bits: u64) -> Option<Self> {
68 Self::try_from(bits.cast_signed()).ok()
69 }
70 })*
71 };
72}
73
74unsigned_column_value!(u8, u16, u32);
75signed_column_value!(i8, i16, i32);
76
77impl ColumnValue for u64 {
78 const IS_INTEGER: bool = true;
79
80 fn to_bits(self) -> u64 {
81 self
82 }
83
84 fn from_bits(bits: u64) -> Option<Self> {
85 Some(bits)
86 }
87}
88
89impl ColumnValue for i64 {
90 const IS_INTEGER: bool = true;
91
92 fn to_bits(self) -> u64 {
93 self.cast_unsigned()
94 }
95
96 fn from_bits(bits: u64) -> Option<Self> {
97 Some(bits.cast_signed())
98 }
99}
100
101impl ColumnValue for f32 {
102 const IS_INTEGER: bool = false;
103
104 fn to_bits(self) -> u64 {
105 u64::from(self.to_bits())
106 }
107
108 fn from_bits(bits: u64) -> Option<Self> {
109 u32::try_from(bits).ok().map(Self::from_bits)
110 }
111}
112
113impl ColumnValue for SymbolId {
114 const IS_INTEGER: bool = true;
115
116 fn to_bits(self) -> u64 {
117 u64::from(self.get())
118 }
119
120 fn from_bits(bits: u64) -> Option<Self> {
121 u32::try_from(bits).ok().map(Self::from_raw)
122 }
123}
124
125#[derive(Clone, PartialEq, Debug)]
141pub enum EncodedColumn<T> {
142 Plain(Vec<T>),
144 Constant {
146 value: T,
148 len: u32,
150 },
151 RunLength {
153 values: Vec<T>,
155 run_ends: Vec<u32>,
157 },
158 BitPacked {
160 data: Vec<u8>,
162 width: u8,
164 len: u32,
166 },
167 Delta {
169 first: T,
171 deltas: Vec<i32>,
173 checkpoints: Vec<i64>,
181 },
182}
183
184impl<T: ColumnValue> EncodedColumn<T> {
185 #[must_use]
188 pub fn len(&self) -> usize {
189 match self {
190 Self::Plain(values) => values.len(),
191 Self::Constant { len, .. } | Self::BitPacked { len, .. } => *len as usize,
192 Self::RunLength { run_ends, .. } => match run_ends.last() {
193 Some(end) => *end as usize,
194 None => 0,
195 },
196 Self::Delta { deltas, .. } => deltas.len() + 1,
197 }
198 }
199
200 #[must_use]
202 pub fn is_empty(&self) -> bool {
203 self.len() == 0
204 }
205
206 #[must_use]
208 pub const fn is_constant(&self) -> bool {
209 matches!(self, Self::Constant { .. })
210 }
211
212 #[must_use]
218 pub fn as_slice(&self) -> Option<&[T]> {
219 match self {
220 Self::Plain(values) => Some(values),
221 _ => None,
222 }
223 }
224
225 #[must_use]
227 pub fn get(&self, position: u32) -> Option<T> {
228 if position as usize >= self.len() {
229 return None;
230 }
231
232 match self {
233 Self::Plain(values) => copied_at(values, position),
234 Self::Constant { value, .. } => Some(*value),
235 Self::RunLength { values, run_ends } => {
236 let run = run_ends.partition_point(|end| *end <= position);
237
238 values.get(run).copied()
239 }
240 Self::BitPacked { data, width, .. } => {
241 unpack_one(data, *width, position).and_then(T::from_bits)
242 }
243 Self::Delta {
244 first,
245 deltas,
246 checkpoints,
247 } => delta_at(*first, deltas, checkpoints, position),
248 }
249 }
250
251 pub fn iter(&self) -> ColumnIter<'_, T> {
256 ColumnIter::new(self)
257 }
258
259 #[must_use]
267 pub fn encode(values: &[T]) -> Self {
268 let Some(first) = values.first() else {
269 return Self::Plain(Vec::new());
270 };
271
272 if values.iter().skip(1).all(|value| value == first)
273 && let Ok(len) = u32::try_from(values.len())
274 {
275 return Self::Constant { value: *first, len };
276 }
277
278 if let Some(column) = Self::try_run_length(values) {
279 return column;
280 }
281
282 if let Some(column) = Self::try_bit_packed(values) {
283 return column;
284 }
285
286 if let Some(column) = Self::try_delta(values) {
287 return column;
288 }
289
290 Self::Plain(copy_values(values))
291 }
292
293 #[must_use]
298 pub fn plain(values: &[T]) -> Self {
299 Self::Plain(copy_values(values))
300 }
301
302 fn try_run_length(values: &[T]) -> Option<Self> {
303 let runs = run_count(values);
304
305 if runs > values.len() / 4 {
308 return None;
309 }
310
311 let mut heads = Vec::with_capacity(runs);
312 let mut run_ends = Vec::with_capacity(runs);
313
314 for (position, value) in values.iter().enumerate() {
315 let end = u32::try_from(position.checked_add(1)?).ok()?;
316
317 if heads.last() == Some(value) {
318 if let Some(last_end) = run_ends.last_mut() {
319 *last_end = end;
320 }
321
322 continue;
323 }
324
325 heads.push(*value);
326 run_ends.push(end);
327 }
328
329 Some(Self::RunLength {
330 values: heads,
331 run_ends,
332 })
333 }
334
335 fn try_bit_packed(values: &[T]) -> Option<Self> {
336 if !T::IS_INTEGER {
337 return None;
338 }
339
340 let max = values.iter().map(|value| value.to_bits()).max()?;
341
342 let width = bit_width(max);
343
344 if usize::from(width) >= size_of::<T>() * 8 {
345 return None;
346 }
347
348 Some(Self::BitPacked {
349 data: pack_mapped(values, width, |value| value.to_bits())?,
350 width,
351 len: u32::try_from(values.len()).ok()?,
352 })
353 }
354
355 fn try_delta(values: &[T]) -> Option<Self> {
356 if !T::IS_INTEGER {
357 return None;
358 }
359
360 let first = *values.first()?;
361 let delta_count = if values.is_empty() {
362 0
363 } else {
364 values.len() - 1
365 };
366 let mut deltas = Vec::with_capacity(delta_count);
367
368 for pair in values.windows(2) {
369 let [earlier, later] = pair else {
370 return None;
371 };
372
373 let step = later
374 .to_bits()
375 .cast_signed()
376 .checked_sub(earlier.to_bits().cast_signed())?;
377
378 deltas.push(i32::try_from(step).ok()?);
379 }
380
381 let mut checkpoints = Vec::with_capacity(deltas.len() / DELTA_CHECKPOINT_STRIDE);
382 let mut running = first.to_bits().cast_signed();
383 for (index, delta) in deltas.iter().enumerate() {
384 running = running.checked_add(i64::from(*delta))?;
385 if (index + 1).is_multiple_of(DELTA_CHECKPOINT_STRIDE) {
386 checkpoints.push(running);
387 }
388 }
389
390 Some(Self::Delta {
391 first,
392 deltas,
393 checkpoints,
394 })
395 }
396}
397
398fn run_count<T: PartialEq>(values: &[T]) -> usize {
399 if values.is_empty() {
400 return 0;
401 }
402
403 1 + values
404 .windows(2)
405 .filter(|pair| pair.first() != pair.get(1))
406 .count()
407}
408
409fn copy_values<T: Copy>(values: &[T]) -> Vec<T> {
410 values.to_vec()
411}
412
413fn copied_at<T: Copy>(values: &[T], position: u32) -> Option<T> {
414 usize::try_from(position)
415 .ok()
416 .and_then(|index| values.get(index))
417 .copied()
418}
419
420const DELTA_CHECKPOINT_STRIDE: usize = 64;
425
426fn delta_at<T: ColumnValue>(
427 first: T,
428 deltas: &[i32],
429 checkpoints: &[i64],
430 position: u32,
431) -> Option<T> {
432 let end = usize::try_from(position).ok()?;
433 if end > deltas.len() {
434 return None;
435 }
436
437 let block = end / DELTA_CHECKPOINT_STRIDE;
440 let (start, base) = match block.checked_sub(1).and_then(|slot| checkpoints.get(slot)) {
441 Some(checkpoint) => (block * DELTA_CHECKPOINT_STRIDE, *checkpoint),
442 None => (0, first.to_bits().cast_signed()),
443 };
444
445 let value = deltas
446 .get(start..end)?
447 .iter()
448 .fold(base, |value, delta| value + i64::from(*delta));
449
450 T::from_bits(value.cast_unsigned())
451}
452
453#[cfg(test)]
454#[path = "model_tests.rs"]
455mod tests;