use crate::error::Result;
use crate::native::io::ClickHouseRead;
pub(crate) const END_OF_GRANULE_FLAG: u64 = 1 << 62;
#[derive(Debug, Default, Clone)]
pub(crate) struct SparseDeserializeState {
pub(crate) num_trailing_defaults: u64,
pub(crate) has_value_after_defaults: bool,
}
#[allow(clippy::cast_possible_truncation)]
pub(crate) async fn read_sparse_offsets<R: ClickHouseRead>(
reader: &mut R,
num_rows: usize,
state: &mut SparseDeserializeState,
) -> Result<Vec<usize>> {
let mut offsets = Vec::new();
let mut current_position: u64 = 0;
if state.num_trailing_defaults > 0 {
current_position += state.num_trailing_defaults;
state.num_trailing_defaults = 0;
}
if state.has_value_after_defaults {
if (current_position as usize) < num_rows {
offsets.push(current_position as usize);
}
current_position += 1;
state.has_value_after_defaults = false;
}
loop {
let group_size = reader.read_var_uint().await?;
let is_end_of_granule = (group_size & END_OF_GRANULE_FLAG) != 0;
let actual_group_size = group_size & !END_OF_GRANULE_FLAG;
current_position += actual_group_size;
if is_end_of_granule {
if current_position > num_rows as u64 {
state.num_trailing_defaults = current_position - num_rows as u64;
}
break;
}
if (current_position as usize) < num_rows {
offsets.push(current_position as usize);
current_position += 1;
} else {
state.has_value_after_defaults = true;
}
}
Ok(offsets)
}
#[cfg(test)]
mod tests {
use super::*;
fn encode_var_uint(value: u64) -> Vec<u8> {
let mut result = Vec::new();
let mut v = value;
loop {
let byte = (v & 0x7f) as u8;
v >>= 7;
if v == 0 {
result.push(byte);
break;
}
result.push(byte | 0x80);
}
result
}
#[tokio::test]
async fn test_read_sparse_offsets_simple() {
let mut data = Vec::new();
data.extend(encode_var_uint(2)); data.extend(encode_var_uint(1)); data.extend(encode_var_uint(3 | END_OF_GRANULE_FLAG));
let mut bytes = std::io::Cursor::new(data);
let mut state = SparseDeserializeState::default();
let offsets = read_sparse_offsets(&mut bytes, 8, &mut state)
.await
.unwrap();
assert_eq!(offsets, vec![2, 4]);
}
#[tokio::test]
async fn test_read_sparse_offsets_all_defaults() {
let mut data = Vec::new();
data.extend(encode_var_uint(4 | END_OF_GRANULE_FLAG));
let mut bytes = std::io::Cursor::new(data);
let mut state = SparseDeserializeState::default();
let offsets = read_sparse_offsets(&mut bytes, 4, &mut state)
.await
.unwrap();
assert_eq!(offsets, [] as [usize; 0]);
}
#[tokio::test]
async fn test_read_sparse_offsets_no_defaults() {
let mut data = Vec::new();
data.extend(encode_var_uint(0)); data.extend(encode_var_uint(0)); data.extend(encode_var_uint(0)); data.extend(encode_var_uint(END_OF_GRANULE_FLAG));
let mut bytes = std::io::Cursor::new(data);
let mut state = SparseDeserializeState::default();
let offsets = read_sparse_offsets(&mut bytes, 3, &mut state)
.await
.unwrap();
assert_eq!(offsets, vec![0, 1, 2]);
}
#[tokio::test]
async fn test_read_sparse_offsets_first_is_value() {
let mut data = Vec::new();
data.extend(encode_var_uint(0)); data.extend(encode_var_uint(2)); data.extend(encode_var_uint(END_OF_GRANULE_FLAG));
let mut bytes = std::io::Cursor::new(data);
let mut state = SparseDeserializeState::default();
let offsets = read_sparse_offsets(&mut bytes, 4, &mut state)
.await
.unwrap();
assert_eq!(offsets, vec![0, 3]);
}
#[tokio::test]
async fn test_single_row_default() {
let mut data = Vec::new();
data.extend(encode_var_uint(1 | END_OF_GRANULE_FLAG));
let mut bytes = std::io::Cursor::new(data);
let mut state = SparseDeserializeState::default();
let offsets = read_sparse_offsets(&mut bytes, 1, &mut state)
.await
.unwrap();
assert_eq!(offsets, [] as [usize; 0]);
}
#[tokio::test]
async fn test_single_row_non_default() {
let mut data = Vec::new();
data.extend(encode_var_uint(0)); data.extend(encode_var_uint(END_OF_GRANULE_FLAG));
let mut bytes = std::io::Cursor::new(data);
let mut state = SparseDeserializeState::default();
let offsets = read_sparse_offsets(&mut bytes, 1, &mut state)
.await
.unwrap();
assert_eq!(offsets, vec![0]);
}
#[tokio::test]
async fn test_large_gap_value_at_end() {
let mut data = Vec::new();
data.extend(encode_var_uint(999)); data.extend(encode_var_uint(END_OF_GRANULE_FLAG));
let mut bytes = std::io::Cursor::new(data);
let mut state = SparseDeserializeState::default();
let offsets = read_sparse_offsets(&mut bytes, 1000, &mut state)
.await
.unwrap();
assert_eq!(offsets, vec![999]);
}
#[tokio::test]
async fn test_value_at_position_zero_only() {
let mut data = Vec::new();
data.extend(encode_var_uint(0)); data.extend(encode_var_uint(0x63 | END_OF_GRANULE_FLAG));
let mut bytes = std::io::Cursor::new(data);
let mut state = SparseDeserializeState::default();
let offsets = read_sparse_offsets(&mut bytes, 100, &mut state)
.await
.unwrap();
assert_eq!(offsets, vec![0]);
}
#[tokio::test]
async fn test_consecutive_non_defaults() {
let mut data = Vec::new();
data.extend(encode_var_uint(0)); data.extend(encode_var_uint(0)); data.extend(encode_var_uint(0)); data.extend(encode_var_uint(2)); data.extend(encode_var_uint(END_OF_GRANULE_FLAG));
let mut bytes = std::io::Cursor::new(data);
let mut state = SparseDeserializeState::default();
let offsets = read_sparse_offsets(&mut bytes, 6, &mut state)
.await
.unwrap();
assert_eq!(offsets, vec![0, 1, 2, 5]);
}
#[tokio::test]
async fn test_state_carry_trailing_defaults() {
let mut state = SparseDeserializeState {
num_trailing_defaults: 2, has_value_after_defaults: false,
};
let mut data = Vec::new();
data.extend(encode_var_uint(0)); data.extend(encode_var_uint(1 | END_OF_GRANULE_FLAG));
let mut bytes = std::io::Cursor::new(data);
let offsets = read_sparse_offsets(&mut bytes, 3, &mut state)
.await
.unwrap();
assert_eq!(offsets, vec![2]);
assert_eq!(state.num_trailing_defaults, 1);
assert!(!state.has_value_after_defaults);
}
}