use crate::types::FieldType;
use crate::varint::encode_varint64;
fn read_as_i64(point: &[u8], offset: usize, field_type: FieldType) -> i64 {
let bytes = &point[offset..];
match field_type {
FieldType::Int8 => i8::from_le_bytes([bytes[0]]) as i64,
FieldType::Uint8 => u8::from_le_bytes([bytes[0]]) as i64,
FieldType::Int16 => i16::from_le_bytes([bytes[0], bytes[1]]) as i64,
FieldType::Uint16 => u16::from_le_bytes([bytes[0], bytes[1]]) as i64,
FieldType::Int32 => i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as i64,
FieldType::Uint32 => u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as i64,
FieldType::Int64 => i64::from_le_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]),
FieldType::Uint64 => u64::from_le_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]) as i64,
_ => 0,
}
}
pub enum FieldEncoder {
Copy { offset: usize, size: usize },
Int {
offset: usize,
field_type: FieldType,
prev: i64,
},
Float32Lossy {
offset: usize,
multiplier: f32,
prev: i64,
},
Float64Lossy {
offset: usize,
multiplier: f64,
prev: i64,
},
Float64Xor { offset: usize, prev_bits: u64 },
FloatNLossy {
offsets: [usize; 4],
multipliers: [f32; 4],
prev: [i32; 4],
count: usize,
},
}
impl FieldEncoder {
pub fn reset(&mut self) {
match self {
FieldEncoder::Int { prev, .. } => *prev = 0,
FieldEncoder::Float32Lossy { prev, .. } => *prev = 0,
FieldEncoder::Float64Lossy { prev, .. } => *prev = 0,
FieldEncoder::Float64Xor { prev_bits, .. } => *prev_bits = 0,
FieldEncoder::FloatNLossy { prev, .. } => *prev = [0i32; 4],
FieldEncoder::Copy { .. } => {}
}
}
pub fn encode(&mut self, point: &[u8], out: &mut [u8]) -> usize {
match self {
FieldEncoder::Copy { offset, size } => {
out[..*size].copy_from_slice(&point[*offset..*offset + *size]);
*size
}
FieldEncoder::Int {
offset,
field_type,
prev,
} => {
let value = read_as_i64(point, *offset, *field_type);
let diff = value - *prev;
*prev = value;
encode_varint64(diff, out)
}
FieldEncoder::Float32Lossy {
offset,
multiplier,
prev,
} => {
let val = f32::from_le_bytes(point[*offset..*offset + 4].try_into().unwrap());
if val.is_nan() {
out[0] = 0; *prev = 0;
return 1;
}
let quantized = (val * *multiplier).round() as i64;
let diff = quantized - *prev;
*prev = quantized;
encode_varint64(diff, out)
}
FieldEncoder::Float64Lossy {
offset,
multiplier,
prev,
} => {
let val = f64::from_le_bytes(point[*offset..*offset + 8].try_into().unwrap());
if val.is_nan() {
out[0] = 0;
*prev = 0;
return 1;
}
let quantized = (val * *multiplier).round() as i64;
let diff = quantized - *prev;
*prev = quantized;
encode_varint64(diff, out)
}
FieldEncoder::Float64Xor { offset, prev_bits } => {
let current = u64::from_le_bytes(point[*offset..*offset + 8].try_into().unwrap());
let residual = current ^ *prev_bits;
*prev_bits = current;
out[..8].copy_from_slice(&residual.to_le_bytes());
8
}
FieldEncoder::FloatNLossy {
offsets,
multipliers,
prev,
count,
} => {
let n = *count;
let mut ptr = 0usize;
for i in 0..n {
let val =
f32::from_le_bytes(point[offsets[i]..offsets[i] + 4].try_into().unwrap());
if val.is_nan() {
out[ptr] = 0; prev[i] = 0;
ptr += 1;
} else {
let quantized = (val * multipliers[i]).round() as i32;
let delta = quantized.wrapping_sub(prev[i]);
prev[i] = quantized;
ptr += encode_varint64(delta as i64, &mut out[ptr..]);
}
}
ptr
}
}
}
}
pub fn build_encoders(
fields: &[crate::types::PointField],
encoding_opt: crate::types::EncodingOptions,
) -> Vec<FieldEncoder> {
use crate::types::EncodingOptions;
let mut encoders: Vec<FieldEncoder> = Vec::new();
let mut start_index = 0;
if encoding_opt == EncodingOptions::None {
for field in fields {
encoders.push(FieldEncoder::Copy {
offset: field.offset as usize,
size: field.field_type.size_of(),
});
}
return encoders;
}
if encoding_opt == EncodingOptions::Lossy {
let floats_count = fields
.iter()
.take_while(|f| f.field_type == FieldType::Float32 && f.resolution.is_some())
.count();
if floats_count == 3 || floats_count == 4 {
let mut offsets = [0usize; 4];
let mut multipliers = [0.0f32; 4];
for i in 0..floats_count {
offsets[i] = fields[i].offset as usize;
multipliers[i] = 1.0f32 / fields[i].resolution.unwrap();
}
encoders.push(FieldEncoder::FloatNLossy {
offsets,
multipliers,
prev: [0i32; 4],
count: floats_count,
});
start_index = floats_count;
}
}
for field in &fields[start_index..] {
let offset = field.offset as usize;
let encoder = match field.field_type {
FieldType::Float32 => {
if encoding_opt == EncodingOptions::Lossy {
if let Some(res) = field.resolution {
FieldEncoder::Float32Lossy {
offset,
multiplier: (1.0f64 / res as f64) as f32,
prev: 0,
}
} else {
FieldEncoder::Copy { offset, size: 4 }
}
} else {
FieldEncoder::Copy { offset, size: 4 }
}
}
FieldType::Float64 => {
if encoding_opt == EncodingOptions::Lossy {
if let Some(res) = field.resolution {
FieldEncoder::Float64Lossy {
offset,
multiplier: 1.0f64 / res as f64,
prev: 0,
}
} else {
FieldEncoder::Float64Xor {
offset,
prev_bits: 0,
}
}
} else {
FieldEncoder::Float64Xor {
offset,
prev_bits: 0,
}
}
}
FieldType::Int16 => FieldEncoder::Int {
offset,
field_type: FieldType::Int16,
prev: 0,
},
FieldType::Uint16 => FieldEncoder::Int {
offset,
field_type: FieldType::Uint16,
prev: 0,
},
FieldType::Int32 => FieldEncoder::Int {
offset,
field_type: FieldType::Int32,
prev: 0,
},
FieldType::Uint32 => FieldEncoder::Int {
offset,
field_type: FieldType::Uint32,
prev: 0,
},
FieldType::Int64 => FieldEncoder::Int {
offset,
field_type: FieldType::Int64,
prev: 0,
},
FieldType::Uint64 => FieldEncoder::Int {
offset,
field_type: FieldType::Uint64,
prev: 0,
},
FieldType::Int8 | FieldType::Uint8 => FieldEncoder::Copy { offset, size: 1 },
_ => panic!("Unsupported field type"),
};
encoders.push(encoder);
}
encoders
}