Skip to main content

exoware_sdk/
kv_codec.rs

1use std::cmp::Ordering;
2
3use bytes::{Buf, BufMut, Bytes};
4use commonware_codec::{
5    EncodeSize, Error as CodecError, FixedSize, RangeCfg, Read, ReadExt, Write,
6};
7
8use crate::keys::{read_bit_be, read_bits_to_bytes, write_bit_be, Key};
9
10#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
11pub struct Utf8(pub String);
12
13impl std::ops::Deref for Utf8 {
14    type Target = str;
15    fn deref(&self) -> &str {
16        &self.0
17    }
18}
19
20impl PartialEq<str> for Utf8 {
21    fn eq(&self, other: &str) -> bool {
22        self.0 == other
23    }
24}
25
26impl PartialEq<&str> for Utf8 {
27    fn eq(&self, other: &&str) -> bool {
28        self.0 == *other
29    }
30}
31
32impl PartialEq<String> for Utf8 {
33    fn eq(&self, other: &String) -> bool {
34        self.0 == *other
35    }
36}
37
38impl std::fmt::Display for Utf8 {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        self.0.fmt(f)
41    }
42}
43
44impl From<String> for Utf8 {
45    fn from(s: String) -> Self {
46        Self(s)
47    }
48}
49
50impl From<&str> for Utf8 {
51    fn from(s: &str) -> Self {
52        Self(s.to_string())
53    }
54}
55
56impl From<Utf8> for String {
57    fn from(s: Utf8) -> Self {
58        s.0
59    }
60}
61
62impl Write for Utf8 {
63    fn write(&self, buf: &mut impl BufMut) {
64        self.0.as_bytes().write(buf);
65    }
66}
67
68impl EncodeSize for Utf8 {
69    fn encode_size(&self) -> usize {
70        self.0.as_bytes().encode_size()
71    }
72}
73
74impl Read for Utf8 {
75    type Cfg = ();
76    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
77        let range: RangeCfg<usize> = (..).into();
78        let bytes = Vec::<u8>::read_cfg(buf, &(range, ()))?;
79        let s =
80            String::from_utf8(bytes).map_err(|_| CodecError::Invalid("Utf8", "invalid utf8"))?;
81        Ok(Utf8(s))
82    }
83}
84
85#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
86pub enum KvFieldKind {
87    Int64,
88    UInt64,
89    Float64,
90    Boolean,
91    Utf8,
92    Date32,
93    Date64,
94    Timestamp,
95    Decimal128,
96    Decimal256,
97    FixedSizeBinary(u8),
98}
99
100#[derive(Clone, Debug, PartialEq, Eq, Hash)]
101pub enum KvFieldRef {
102    Key {
103        byte_offset: u16,
104        kind: KvFieldKind,
105    },
106    ZOrderKey {
107        bit_offset: u16,
108        field_position: u8,
109        field_widths: Vec<u8>,
110        kind: KvFieldKind,
111    },
112    Value {
113        index: u16,
114        kind: KvFieldKind,
115        nullable: bool,
116    },
117}
118
119#[derive(Clone, Debug, PartialEq)]
120pub enum KvExpr {
121    Field(KvFieldRef),
122    Literal(KvReducedValue),
123    Add(Box<KvExpr>, Box<KvExpr>),
124    Sub(Box<KvExpr>, Box<KvExpr>),
125    Mul(Box<KvExpr>, Box<KvExpr>),
126    Div(Box<KvExpr>, Box<KvExpr>),
127    Lower(Box<KvExpr>),
128    DateTruncDay(Box<KvExpr>),
129    CastFloat64(Box<KvExpr>),
130}
131
132#[derive(Clone, Debug, PartialEq)]
133pub enum KvPredicateConstraint {
134    StringEq(String),
135    BoolEq(bool),
136    FixedSizeBinaryEq(Bytes),
137    IntRange {
138        min: Option<i64>,
139        max: Option<i64>,
140    },
141    UInt64Range {
142        min: Option<u64>,
143        max: Option<u64>,
144    },
145    FloatRange {
146        min: Option<(f64, bool)>,
147        max: Option<(f64, bool)>,
148    },
149    Decimal128Range {
150        min: Option<i128>,
151        max: Option<i128>,
152    },
153    Decimal256Range {
154        min: Option<[u8; 32]>,
155        max: Option<[u8; 32]>,
156    },
157    IsNull,
158    IsNotNull,
159    StringIn(Vec<String>),
160    IntIn(Vec<i64>),
161    UInt64In(Vec<u64>),
162    FixedSizeBinaryIn(Vec<Bytes>),
163}
164
165#[derive(Clone, Debug, PartialEq)]
166pub struct KvPredicateCheck {
167    pub field: KvFieldRef,
168    pub constraint: KvPredicateConstraint,
169}
170
171#[derive(Clone, Debug, PartialEq)]
172pub struct KvPredicate {
173    pub checks: Vec<KvPredicateCheck>,
174    pub contradiction: bool,
175}
176
177#[derive(Clone, Debug, PartialEq)]
178pub enum KvReducedValue {
179    Int64(i64),
180    UInt64(u64),
181    Float64(f64),
182    Boolean(bool),
183    Utf8(String),
184    Date32(i32),
185    Date64(i64),
186    Timestamp(i64),
187    Decimal128(i128),
188    Decimal256([u8; 32]),
189    FixedSizeBinary(Bytes),
190}
191
192#[derive(Debug, Clone)]
193pub enum StoredValue {
194    Int64(i64),
195    UInt64(u64),
196    Float64(f64),
197    Boolean(bool),
198    Utf8(String),
199    Bytes(Vec<u8>),
200    List(Vec<StoredValue>),
201}
202
203impl Write for StoredValue {
204    fn write(&self, buf: &mut impl BufMut) {
205        match self {
206            StoredValue::Int64(v) => {
207                0u8.write(buf);
208                v.write(buf);
209            }
210            StoredValue::UInt64(v) => {
211                1u8.write(buf);
212                v.write(buf);
213            }
214            StoredValue::Float64(v) => {
215                2u8.write(buf);
216                v.write(buf);
217            }
218            StoredValue::Boolean(v) => {
219                3u8.write(buf);
220                v.write(buf);
221            }
222            StoredValue::Utf8(v) => {
223                4u8.write(buf);
224                v.as_bytes().write(buf);
225            }
226            StoredValue::Bytes(v) => {
227                5u8.write(buf);
228                v.as_slice().write(buf);
229            }
230            StoredValue::List(items) => {
231                6u8.write(buf);
232                items.as_slice().write(buf);
233            }
234        }
235    }
236}
237
238impl EncodeSize for StoredValue {
239    fn encode_size(&self) -> usize {
240        1 + match self {
241            StoredValue::Int64(_) => i64::SIZE,
242            StoredValue::UInt64(_) => u64::SIZE,
243            StoredValue::Float64(_) => f64::SIZE,
244            StoredValue::Boolean(_) => bool::SIZE,
245            StoredValue::Utf8(v) => v.as_bytes().encode_size(),
246            StoredValue::Bytes(v) => v.as_slice().encode_size(),
247            StoredValue::List(items) => items.as_slice().encode_size(),
248        }
249    }
250}
251
252impl Read for StoredValue {
253    type Cfg = ();
254    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
255        let range: RangeCfg<usize> = (..).into();
256        match u8::read(buf)? {
257            0 => Ok(StoredValue::Int64(i64::read(buf)?)),
258            1 => Ok(StoredValue::UInt64(u64::read(buf)?)),
259            2 => Ok(StoredValue::Float64(f64::read(buf)?)),
260            3 => Ok(StoredValue::Boolean(bool::read(buf)?)),
261            4 => Ok(StoredValue::Utf8(Utf8::read(buf)?.0)),
262            5 => {
263                let bytes = Vec::<u8>::read_cfg(buf, &(range, ()))?;
264                Ok(StoredValue::Bytes(bytes))
265            }
266            6 => {
267                let items = Vec::<StoredValue>::read_cfg(buf, &(range, ()))?;
268                Ok(StoredValue::List(items))
269            }
270            v => Err(CodecError::InvalidEnum(v)),
271        }
272    }
273}
274
275#[derive(Debug, Clone)]
276pub struct StoredRow {
277    pub values: Vec<Option<StoredValue>>,
278}
279
280impl Write for StoredRow {
281    fn write(&self, buf: &mut impl BufMut) {
282        self.values.as_slice().write(buf);
283    }
284}
285
286impl EncodeSize for StoredRow {
287    fn encode_size(&self) -> usize {
288        self.values.as_slice().encode_size()
289    }
290}
291
292impl Read for StoredRow {
293    type Cfg = ();
294    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
295        let range: RangeCfg<usize> = (..).into();
296        let values = Vec::<Option<StoredValue>>::read_cfg(buf, &(range, ()))?;
297        Ok(StoredRow { values })
298    }
299}
300
301pub fn decode_stored_row(value: &[u8]) -> Result<StoredRow, CodecError> {
302    StoredRow::read_cfg(&mut &*value, &())
303}
304
305/// Read a typed field. Key fields do not require a decoded stored row.
306pub fn extract_field(
307    key: &Key,
308    archived: Option<&StoredRow>,
309    field: &KvFieldRef,
310) -> Result<Option<KvReducedValue>, String> {
311    match field {
312        KvFieldRef::Key { byte_offset, kind } => {
313            extract_key_field(key, usize::from(*byte_offset), *kind)
314                .map(Some)
315                .ok_or_else(|| "invalid key field".to_string())
316        }
317        KvFieldRef::ZOrderKey {
318            bit_offset,
319            field_position,
320            field_widths,
321            kind,
322        } => extract_zorder_key_field(
323            key,
324            usize::from(*bit_offset),
325            usize::from(*field_position),
326            field_widths,
327            *kind,
328        )
329        .map(Some)
330        .ok_or_else(|| "invalid z-order key field".to_string()),
331        KvFieldRef::Value {
332            index,
333            kind,
334            nullable,
335        } => extract_stored_field(
336            archived.ok_or_else(|| "value field requires stored row".to_string())?,
337            usize::from(*index),
338            *kind,
339            *nullable,
340        ),
341    }
342}
343
344pub fn predicate_needs_value(predicate: &KvPredicate) -> bool {
345    predicate
346        .checks
347        .iter()
348        .any(|check| matches!(check.field, KvFieldRef::Value { .. }))
349}
350
351pub fn eval_predicate(
352    key: &Key,
353    archived: Option<&StoredRow>,
354    predicate: &KvPredicate,
355) -> Result<bool, String> {
356    if predicate.contradiction {
357        return Ok(false);
358    }
359    for check in &predicate.checks {
360        let value = extract_field(key, archived, &check.field)?;
361        if !matches_predicate_constraint(value.as_ref(), &check.constraint) {
362            return Ok(false);
363        }
364    }
365    Ok(true)
366}
367
368pub fn interleave_ordered_key_fields(fields: &[Vec<u8>]) -> Vec<u8> {
369    let total_bits = fields.iter().map(|field| field.len() * 8).sum::<usize>();
370    let mut out = vec![0u8; total_bits.div_ceil(8)];
371    let max_bits = fields
372        .iter()
373        .map(|field| field.len() * 8)
374        .max()
375        .unwrap_or(0);
376    let mut out_bit = 0usize;
377    for bit_idx in 0..max_bits {
378        for field in fields {
379            if bit_idx < field.len() * 8 {
380                if read_bit_be(field, bit_idx) {
381                    write_bit_be(&mut out, out_bit, true);
382                }
383                out_bit += 1;
384            }
385        }
386    }
387    out
388}
389
390pub fn deinterleave_ordered_key_fields(
391    interleaved: &[u8],
392    field_widths: &[u8],
393) -> Option<Vec<Vec<u8>>> {
394    let total_bits = field_widths
395        .iter()
396        .map(|width| usize::from(*width) * 8)
397        .sum::<usize>();
398    if interleaved.len() * 8 != total_bits {
399        return None;
400    }
401    let max_bits = field_widths
402        .iter()
403        .map(|width| usize::from(*width) * 8)
404        .max()
405        .unwrap_or(0);
406    let mut out = field_widths
407        .iter()
408        .map(|width| vec![0u8; usize::from(*width)])
409        .collect::<Vec<_>>();
410    let mut in_bit = 0usize;
411    for bit_idx in 0..max_bits {
412        for field in &mut out {
413            if bit_idx < field.len() * 8 {
414                let bit = read_bit_be(interleaved, in_bit);
415                write_bit_be(field, bit_idx, bit);
416                in_bit += 1;
417            }
418        }
419    }
420    Some(out)
421}
422
423pub fn extract_key_field(
424    key: &Key,
425    byte_offset: usize,
426    kind: KvFieldKind,
427) -> Option<KvReducedValue> {
428    let width = key_field_width(kind);
429    let end = byte_offset.checked_add(width)?;
430    let bytes = key.get(byte_offset..end)?;
431    decode_ordered_key_field_bytes(bytes, kind)
432}
433
434pub fn extract_zorder_key_field(
435    key: &Key,
436    bit_offset: usize,
437    field_position: usize,
438    field_widths: &[u8],
439    kind: KvFieldKind,
440) -> Option<KvReducedValue> {
441    let total_width = field_widths
442        .iter()
443        .map(|width| usize::from(*width))
444        .sum::<usize>();
445    let interleaved = extract_key_bytes(key, bit_offset, total_width)?;
446    let fields = deinterleave_ordered_key_fields(&interleaved, field_widths)?;
447    let bytes = fields.get(field_position)?;
448    decode_ordered_key_field_bytes(bytes, kind)
449}
450
451fn extract_key_bytes(key: &Key, bit_offset: usize, width: usize) -> Option<Vec<u8>> {
452    let end_bit = bit_offset.checked_add(width.checked_mul(8)?)?;
453    if end_bit > key.len() * 8 {
454        return None;
455    }
456    if bit_offset.is_multiple_of(8) {
457        let byte_offset = bit_offset / 8;
458        return Some(key.get(byte_offset..byte_offset + width)?.to_vec());
459    }
460    let mut out = vec![0u8; width];
461    read_bits_to_bytes(key, bit_offset, &mut out, width * 8);
462    Some(out)
463}
464
465pub fn extract_stored_field(
466    archived: &StoredRow,
467    index: usize,
468    kind: KvFieldKind,
469    nullable: bool,
470) -> Result<Option<KvReducedValue>, String> {
471    let stored_opt = archived
472        .values
473        .get(index)
474        .ok_or_else(|| "stored row index out of bounds".to_string())?
475        .as_ref();
476    let Some(stored) = stored_opt else {
477        if nullable {
478            return Ok(None);
479        }
480        return Err("non-nullable field stored as null".to_string());
481    };
482
483    let value = match (kind, stored) {
484        (KvFieldKind::Int64, StoredValue::Int64(v)) => KvReducedValue::Int64(*v),
485        (KvFieldKind::UInt64, StoredValue::UInt64(v)) => KvReducedValue::UInt64(*v),
486        (KvFieldKind::Float64, StoredValue::Float64(v)) => KvReducedValue::Float64(*v),
487        (KvFieldKind::Float64, StoredValue::Int64(v)) => KvReducedValue::Float64(*v as f64),
488        (KvFieldKind::Boolean, StoredValue::Boolean(v)) => KvReducedValue::Boolean(*v),
489        (KvFieldKind::Utf8, StoredValue::Utf8(v)) => KvReducedValue::Utf8(v.as_str().to_string()),
490        (KvFieldKind::Date32, StoredValue::Int64(v)) => KvReducedValue::Date32(*v as i32),
491        (KvFieldKind::Date64, StoredValue::Int64(v)) => KvReducedValue::Date64(*v),
492        (KvFieldKind::Timestamp, StoredValue::Int64(v)) => KvReducedValue::Timestamp(*v),
493        (KvFieldKind::Decimal128, StoredValue::Bytes(bytes)) => {
494            let raw: [u8; 16] = bytes
495                .as_slice()
496                .try_into()
497                .map_err(|_| "invalid Decimal128 byte width".to_string())?;
498            KvReducedValue::Decimal128(i128::from_le_bytes(raw))
499        }
500        (KvFieldKind::Decimal256, StoredValue::Bytes(bytes)) => {
501            let raw: [u8; 32] = bytes
502                .as_slice()
503                .try_into()
504                .map_err(|_| "invalid Decimal256 byte width".to_string())?;
505            KvReducedValue::Decimal256(raw)
506        }
507        (KvFieldKind::FixedSizeBinary(width), StoredValue::Bytes(bytes)) => {
508            if bytes.as_slice().len() != usize::from(width) {
509                return Err("invalid FixedSizeBinary byte width".to_string());
510            }
511            KvReducedValue::FixedSizeBinary(Bytes::copy_from_slice(bytes))
512        }
513        _ => return Err("stored field type mismatch".to_string()),
514    };
515    Ok(Some(value))
516}
517
518fn key_field_width(kind: KvFieldKind) -> usize {
519    match kind {
520        KvFieldKind::Int64
521        | KvFieldKind::UInt64
522        | KvFieldKind::Float64
523        | KvFieldKind::Date64
524        | KvFieldKind::Timestamp => 8,
525        KvFieldKind::Boolean => 1,
526        KvFieldKind::Utf8 => 16,
527        KvFieldKind::Date32 => 4,
528        KvFieldKind::Decimal128 => 16,
529        KvFieldKind::Decimal256 => 32,
530        KvFieldKind::FixedSizeBinary(width) => usize::from(width),
531    }
532}
533
534fn decode_ordered_key_field_bytes(bytes: &[u8], kind: KvFieldKind) -> Option<KvReducedValue> {
535    Some(match kind {
536        KvFieldKind::Int64 => {
537            let raw = bytes.try_into().ok()?;
538            KvReducedValue::Int64(decode_i64_ordered(raw))
539        }
540        KvFieldKind::UInt64 => {
541            let raw = bytes.try_into().ok()?;
542            KvReducedValue::UInt64(u64::from_be_bytes(raw))
543        }
544        KvFieldKind::Float64 => {
545            let raw = bytes.try_into().ok()?;
546            KvReducedValue::Float64(decode_f64_ordered(raw))
547        }
548        KvFieldKind::Boolean => KvReducedValue::Boolean(*bytes.first()? != 0),
549        KvFieldKind::Utf8 => KvReducedValue::Utf8(decode_fixed_text(bytes)?),
550        KvFieldKind::Date32 => {
551            let raw = bytes.try_into().ok()?;
552            KvReducedValue::Date32(decode_i32_ordered(raw))
553        }
554        KvFieldKind::Date64 => {
555            let raw = bytes.try_into().ok()?;
556            KvReducedValue::Date64(decode_i64_ordered(raw))
557        }
558        KvFieldKind::Timestamp => {
559            let raw = bytes.try_into().ok()?;
560            KvReducedValue::Timestamp(decode_i64_ordered(raw))
561        }
562        KvFieldKind::Decimal128 => {
563            let raw = bytes.try_into().ok()?;
564            KvReducedValue::Decimal128(decode_i128_ordered(raw))
565        }
566        KvFieldKind::Decimal256 => {
567            let raw = bytes.try_into().ok()?;
568            KvReducedValue::Decimal256(decode_i256_ordered(raw))
569        }
570        KvFieldKind::FixedSizeBinary(width) => {
571            if bytes.len() != usize::from(width) {
572                return None;
573            }
574            KvReducedValue::FixedSizeBinary(Bytes::copy_from_slice(bytes))
575        }
576    })
577}
578
579fn matches_predicate_constraint(
580    value: Option<&KvReducedValue>,
581    constraint: &KvPredicateConstraint,
582) -> bool {
583    match (value, constraint) {
584        (None, KvPredicateConstraint::IsNull) => return true,
585        (None, KvPredicateConstraint::IsNotNull) => return false,
586        (Some(_), KvPredicateConstraint::IsNull) => return false,
587        (Some(_), KvPredicateConstraint::IsNotNull) => return true,
588        (None, _) => return false,
589        _ => {}
590    }
591    let value = value.expect("checked above");
592    match (value, constraint) {
593        (KvReducedValue::Utf8(v), KvPredicateConstraint::StringEq(expected)) => v == expected,
594        (KvReducedValue::Boolean(v), KvPredicateConstraint::BoolEq(expected)) => v == expected,
595        (KvReducedValue::Int64(v), KvPredicateConstraint::IntRange { min, max }) => {
596            in_i64_bounds(*v, *min, *max)
597        }
598        (KvReducedValue::Date32(v), KvPredicateConstraint::IntRange { min, max }) => {
599            in_i64_bounds(*v as i64, *min, *max)
600        }
601        (KvReducedValue::Date64(v), KvPredicateConstraint::IntRange { min, max }) => {
602            in_i64_bounds(*v, *min, *max)
603        }
604        (KvReducedValue::Timestamp(v), KvPredicateConstraint::IntRange { min, max }) => {
605            in_i64_bounds(*v, *min, *max)
606        }
607        (KvReducedValue::Float64(v), KvPredicateConstraint::FloatRange { min, max }) => {
608            in_f64_bounds(*v, min, max)
609        }
610        (KvReducedValue::Decimal128(v), KvPredicateConstraint::Decimal128Range { min, max }) => {
611            in_i128_bounds(*v, *min, *max)
612        }
613        (KvReducedValue::Decimal256(v), KvPredicateConstraint::Decimal256Range { min, max }) => {
614            in_i256_le_bounds(v, min.as_ref(), max.as_ref())
615        }
616        (KvReducedValue::Utf8(v), KvPredicateConstraint::StringIn(values)) => values.contains(v),
617        (KvReducedValue::Int64(v), KvPredicateConstraint::IntIn(values)) => values.contains(v),
618        (KvReducedValue::UInt64(v), KvPredicateConstraint::UInt64Range { min, max }) => {
619            in_u64_bounds(*v, *min, *max)
620        }
621        (KvReducedValue::UInt64(v), KvPredicateConstraint::UInt64In(values)) => values.contains(v),
622        (
623            KvReducedValue::FixedSizeBinary(v),
624            KvPredicateConstraint::FixedSizeBinaryEq(expected),
625        ) => v == expected,
626        (KvReducedValue::FixedSizeBinary(v), KvPredicateConstraint::FixedSizeBinaryIn(values)) => {
627            values.contains(v)
628        }
629        _ => false,
630    }
631}
632
633fn in_i64_bounds(value: i64, min: Option<i64>, max: Option<i64>) -> bool {
634    min.is_none_or(|lower| value >= lower) && max.is_none_or(|upper| value <= upper)
635}
636
637fn in_u64_bounds(value: u64, min: Option<u64>, max: Option<u64>) -> bool {
638    min.is_none_or(|lower| value >= lower) && max.is_none_or(|upper| value <= upper)
639}
640
641fn in_i128_bounds(value: i128, min: Option<i128>, max: Option<i128>) -> bool {
642    min.is_none_or(|lower| value >= lower) && max.is_none_or(|upper| value <= upper)
643}
644
645fn in_f64_bounds(value: f64, min: &Option<(f64, bool)>, max: &Option<(f64, bool)>) -> bool {
646    min.is_none_or(|(bound, inclusive)| {
647        let order = value.total_cmp(&bound);
648        order.is_gt() || (inclusive && order.is_eq())
649    }) && max.is_none_or(|(bound, inclusive)| {
650        let order = value.total_cmp(&bound);
651        order.is_lt() || (inclusive && order.is_eq())
652    })
653}
654
655fn decode_i64_ordered(bytes: [u8; 8]) -> i64 {
656    (u64::from_be_bytes(bytes) ^ 0x8000_0000_0000_0000) as i64
657}
658
659fn decode_f64_ordered(bytes: [u8; 8]) -> f64 {
660    let bits = u64::from_be_bytes(bytes);
661    let decoded = if bits & 0x8000_0000_0000_0000 != 0 {
662        bits ^ 0x8000_0000_0000_0000
663    } else {
664        !bits
665    };
666    f64::from_bits(decoded)
667}
668
669fn decode_i32_ordered(bytes: [u8; 4]) -> i32 {
670    (u32::from_be_bytes(bytes) ^ 0x8000_0000) as i32
671}
672
673fn decode_i128_ordered(bytes: [u8; 16]) -> i128 {
674    (u128::from_be_bytes(bytes) ^ (1u128 << 127)) as i128
675}
676
677fn decode_i256_ordered(mut bytes: [u8; 32]) -> [u8; 32] {
678    bytes[0] ^= 0x80;
679    let mut le = [0u8; 32];
680    for i in 0..32 {
681        le[i] = bytes[31 - i];
682    }
683    le
684}
685
686fn cmp_i256_le_bytes(a: &[u8; 32], b: &[u8; 32]) -> Ordering {
687    let a_neg = a[31] & 0x80 != 0;
688    let b_neg = b[31] & 0x80 != 0;
689    match (a_neg, b_neg) {
690        (true, false) => Ordering::Less,
691        (false, true) => Ordering::Greater,
692        _ => {
693            for i in (0..32).rev() {
694                match a[i].cmp(&b[i]) {
695                    Ordering::Equal => continue,
696                    ord => return ord,
697                }
698            }
699            Ordering::Equal
700        }
701    }
702}
703
704fn in_i256_le_bounds(value: &[u8; 32], min: Option<&[u8; 32]>, max: Option<&[u8; 32]>) -> bool {
705    min.is_none_or(|lower| cmp_i256_le_bytes(value, lower) != Ordering::Less)
706        && max.is_none_or(|upper| cmp_i256_le_bytes(value, upper) != Ordering::Greater)
707}
708
709fn decode_fixed_text(bytes: &[u8]) -> Option<String> {
710    let end = bytes.iter().position(|b| *b == 0).unwrap_or(bytes.len());
711    std::str::from_utf8(&bytes[..end])
712        .ok()
713        .map(ToString::to_string)
714}
715
716#[cfg(test)]
717mod tests {
718    use super::*;
719    use crate::keys::MAX_KEY_LEN;
720    use commonware_codec::Encode;
721
722    #[test]
723    fn float_bounds_follow_total_order() {
724        for value in [
725            f64::NEG_INFINITY,
726            -0.0,
727            0.0,
728            f64::INFINITY,
729            f64::NAN,
730            -f64::NAN,
731        ] {
732            assert!(in_f64_bounds(value, &None, &None));
733            for bound in [-0.0, 0.0, f64::NAN, -f64::NAN] {
734                assert_eq!(
735                    in_f64_bounds(value, &Some((bound, true)), &None),
736                    value.total_cmp(&bound).is_ge()
737                );
738                assert_eq!(
739                    in_f64_bounds(value, &None, &Some((bound, false))),
740                    value.total_cmp(&bound).is_lt()
741                );
742            }
743        }
744    }
745
746    #[test]
747    fn fixed_utf8_key_round_trip() {
748        let mut key = vec![0u8; MAX_KEY_LEN];
749        key[4..8].copy_from_slice(b"west");
750        let key = Key::from(key);
751        let value = extract_key_field(&key, 4, KvFieldKind::Utf8).expect("utf8 field");
752        assert_eq!(value, KvReducedValue::Utf8("west".to_string()));
753    }
754
755    #[test]
756    fn int64_key_round_trip() {
757        let mut key = vec![0u8; MAX_KEY_LEN];
758        let encoded = ((-42i64 as u64) ^ 0x8000_0000_0000_0000).to_be_bytes();
759        key[8..16].copy_from_slice(&encoded);
760        let key = Key::from(key);
761        let value = extract_key_field(&key, 8, KvFieldKind::Int64).expect("int64 field");
762        assert_eq!(value, KvReducedValue::Int64(-42));
763    }
764
765    #[test]
766    fn stored_decimal128_field_round_trip() {
767        let row = StoredRow {
768            values: vec![Some(StoredValue::Bytes(123i128.to_le_bytes().to_vec()))],
769        };
770        let bytes = row.encode();
771        let decoded = decode_stored_row(&bytes).expect("decoded row");
772        let value = extract_stored_field(&decoded, 0, KvFieldKind::Decimal128, false)
773            .expect("valid field")
774            .expect("present field");
775        assert_eq!(value, KvReducedValue::Decimal128(123));
776    }
777
778    #[test]
779    fn non_nullable_missing_field_is_invalid() {
780        let row = StoredRow { values: vec![None] };
781        let bytes = row.encode();
782        let decoded = decode_stored_row(&bytes).expect("decoded row");
783        let err = extract_stored_field(&decoded, 0, KvFieldKind::Int64, false)
784            .expect_err("missing non-nullable field should fail");
785        assert!(err.contains("non-nullable"));
786    }
787
788    #[test]
789    fn zorder_interleave_round_trips_mixed_width_fields() {
790        let fields = vec![vec![0x80, 0x01, 0x02, 0x03], vec![0xAA, 0xBB], vec![0xCC]];
791        let interleaved = interleave_ordered_key_fields(&fields);
792        let decoded =
793            deinterleave_ordered_key_fields(&interleaved, &[4, 2, 1]).expect("deinterleave");
794        assert_eq!(decoded, fields);
795    }
796
797    #[test]
798    fn eval_predicate_supports_zorder_key_fields() {
799        let mut key_buf = vec![0u8; 19];
800        let x = ((2i64 as u64) ^ 0x8000_0000_0000_0000)
801            .to_be_bytes()
802            .to_vec();
803        let y = ((1i64 as u64) ^ 0x8000_0000_0000_0000)
804            .to_be_bytes()
805            .to_vec();
806        let interleaved = interleave_ordered_key_fields(&[x, y]);
807        key_buf[3..3 + interleaved.len()].copy_from_slice(&interleaved);
808        let key = Key::from(key_buf);
809
810        let predicate = KvPredicate {
811            checks: vec![
812                KvPredicateCheck {
813                    field: KvFieldRef::ZOrderKey {
814                        bit_offset: 24,
815                        field_position: 0,
816                        field_widths: vec![8, 8],
817                        kind: KvFieldKind::Int64,
818                    },
819                    constraint: KvPredicateConstraint::IntRange {
820                        min: Some(1),
821                        max: Some(2),
822                    },
823                },
824                KvPredicateCheck {
825                    field: KvFieldRef::ZOrderKey {
826                        bit_offset: 24,
827                        field_position: 1,
828                        field_widths: vec![8, 8],
829                        kind: KvFieldKind::Int64,
830                    },
831                    constraint: KvPredicateConstraint::IntRange {
832                        min: Some(1),
833                        max: Some(2),
834                    },
835                },
836            ],
837            contradiction: false,
838        };
839
840        assert!(eval_predicate(&key, None, &predicate).expect("predicate eval"));
841    }
842}