use crate::{error::Error, storage::sstable::bti::node::BtiResult, types::Value};
const OSS50_NEXT_COMPONENT: u8 = 0x40;
fn encode_varlen_oss50(bytes: &[u8], out: &mut Vec<u8>) {
for &b in bytes {
out.push(b);
if b == 0x00 {
out.push(0xFE);
}
}
out.push(0x00);
out.push(0xFF);
}
fn encode_clustering_component_oss50(value: &Value, out: &mut Vec<u8>) -> BtiResult<()> {
match value {
Value::Integer(v) => {
out.extend_from_slice(&((*v as u32) ^ 0x8000_0000).to_be_bytes());
Ok(())
}
Value::BigInt(v) | Value::Counter(v) => {
out.extend_from_slice(&((*v as u64) ^ 0x8000_0000_0000_0000).to_be_bytes());
Ok(())
}
Value::SmallInt(v) => {
out.extend_from_slice(&((*v as u16) ^ 0x8000).to_be_bytes());
Ok(())
}
Value::TinyInt(v) => {
out.push((*v as u8) ^ 0x80);
Ok(())
}
Value::Boolean(b) => {
out.push(if *b { 0x01 } else { 0x00 });
Ok(())
}
Value::Timestamp(v) => {
out.extend_from_slice(&((*v as u64) ^ 0x8000_0000_0000_0000).to_be_bytes());
Ok(())
}
Value::Uuid(bytes) => {
out.extend_from_slice(bytes);
Ok(())
}
Value::Text(s) => {
encode_varlen_oss50(s.as_ref(), out);
Ok(())
}
Value::Blob(b) | Value::Inet(b) => {
encode_varlen_oss50(b, out);
Ok(())
}
other => Err(Error::Parse(format!(
"BTI range_query: byte-comparable encoding not implemented for {:?}",
other.data_type()
))),
}
}
pub fn encode_clustering_bound_oss50(values: &[Value]) -> BtiResult<Vec<u8>> {
let mut out = Vec::new();
for (i, v) in values.iter().enumerate() {
if i > 0 {
out.push(OSS50_NEXT_COMPONENT);
}
encode_clustering_component_oss50(v, &mut out)?;
}
Ok(out)
}
pub fn encode_clustering_bound_oss50_with_order(
values: &[Value],
is_reversed: &[bool],
) -> BtiResult<Vec<u8>> {
let mut out = Vec::new();
for (i, v) in values.iter().enumerate() {
if i > 0 {
out.push(OSS50_NEXT_COMPONENT);
}
if is_reversed.get(i).copied().unwrap_or(false) {
let mut scratch = Vec::new();
encode_clustering_component_oss50(v, &mut scratch)?;
for b in &scratch {
out.push(0xFF ^ *b);
}
} else {
encode_clustering_component_oss50(v, &mut out)?;
}
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn oss50_clustering_encoder() {
assert_eq!(
encode_clustering_bound_oss50(&[Value::Integer(8)]).unwrap(),
vec![0x80, 0x00, 0x00, 0x08]
);
assert_eq!(
encode_clustering_bound_oss50(&[Value::Integer(-1)]).unwrap(),
vec![0x7F, 0xFF, 0xFF, 0xFF]
);
let neg = encode_clustering_bound_oss50(&[Value::Integer(-1)]).unwrap();
let zero = encode_clustering_bound_oss50(&[Value::Integer(0)]).unwrap();
let pos = encode_clustering_bound_oss50(&[Value::Integer(100)]).unwrap();
assert!(neg < zero && zero < pos);
assert_eq!(
encode_clustering_bound_oss50(&[Value::BigInt(1)]).unwrap(),
vec![0x80, 0, 0, 0, 0, 0, 0, 0x01]
);
assert_eq!(
encode_clustering_bound_oss50(&[Value::Integer(1), Value::text("ab".to_string())])
.unwrap(),
vec![0x80, 0x00, 0x00, 0x01, 0x40, b'a', b'b', 0x00, 0xFF]
);
assert_eq!(
encode_clustering_bound_oss50(&[Value::text("a".to_string())]).unwrap(),
vec![b'a', 0x00, 0xFF]
);
assert_eq!(
encode_clustering_bound_oss50(&[Value::blob(vec![0x01, 0x00, 0x02])]).unwrap(),
vec![0x01, 0x00, 0xFE, 0x02, 0x00, 0xFF]
);
let a = encode_clustering_bound_oss50(&[Value::text("a".to_string())]).unwrap();
let ab = encode_clustering_bound_oss50(&[Value::text("ab".to_string())]).unwrap();
assert!(a < ab);
assert!(encode_clustering_bound_oss50(&[Value::Float(1.0)]).is_err());
}
#[test]
fn oss50_clustering_encoder_reversed_order() {
assert_eq!(
encode_clustering_bound_oss50_with_order(&[Value::Integer(8)], &[true]).unwrap(),
vec![0x7F, 0xFF, 0xFF, 0xF7]
);
assert_eq!(
encode_clustering_bound_oss50_with_order(&[Value::Integer(8)], &[false]).unwrap(),
encode_clustering_bound_oss50(&[Value::Integer(8)]).unwrap()
);
let enc = |v: i32| {
encode_clustering_bound_oss50_with_order(&[Value::Integer(v)], &[true]).unwrap()
};
let write_order = [5, 4, 3, 2, 1, 0];
for w in write_order.windows(2) {
assert!(
enc(w[0]) < enc(w[1]),
"DESC: value {} written before {} must yield smaller separator bytes",
w[0],
w[1]
);
}
let mixed = encode_clustering_bound_oss50_with_order(
&[Value::Integer(1), Value::Integer(8)],
&[false, true],
)
.unwrap();
assert_eq!(
mixed,
vec![0x80, 0x00, 0x00, 0x01, 0x40, 0x7F, 0xFF, 0xFF, 0xF7],
"ASC component bare, 0x40 framing un-inverted, DESC component complemented"
);
let m = |a: i32, b: i32| {
encode_clustering_bound_oss50_with_order(
&[Value::Integer(a), Value::Integer(b)],
&[false, true],
)
.unwrap()
};
assert!(m(1, 9) < m(1, 8) && m(1, 8) < m(1, 7));
assert!(m(1, 0) < m(2, 9));
}
}