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
305pub 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}