Skip to main content

linkrs_core/value/
ordered_codec.rs

1//! Ordered key codec — order-preserving binary encoding for `Value`.
2//!
3//! The byte-level comparison of encoded values matches the semantic ordering
4//! of values within the same index type. Distinct types are ordered by their
5//! stable type tags. This enables range and prefix scans on index keys without
6//! post-filtering while preserving the original value type in the key format.
7//!
8//! # Format (version 2)
9//!
10//! Each field starts with a type tag byte, followed by the encoded body.
11//! Fixed-length types encode to a fixed byte count; variable-length types
12//! (String, Blob) use an escaped 0x00 terminator. A zero byte in the data is
13//! encoded as `00 01`; the terminator is `00 00`.
14//!
15//! The terminator enables tight prefix upper bounds: for a string or blob
16//! prefix value P, the range scan bound is `[encode(P), tag +
17//! prefix_upper_bound(P_data_bytes))`, covering all values that start with P
18//! and excluding those that do not.
19//!
20//! Composite keys concatenate multiple field encodings; the entity tie-breaker
21//! is appended as the final field.
22
23use crate::types::storage_ids::VertexId;
24use crate::value::date_time::{DateTimeValue, DateValue, TimeValue};
25use crate::value::UuidValue;
26use crate::wal::EntityRef;
27use crate::{NullType, StorageError, Value};
28use compact_str::CompactString;
29
30/// Current wire format version.
31pub const ORDERED_CODEC_VERSION: u8 = 0x02;
32
33// ── Type tags (1 byte) ──────────────────────────────────────────────────────
34
35const TAG_EMPTY: u8 = 0x00;
36const TAG_NULL: u8 = 0x01;
37const TAG_BOOL: u8 = 0x02;
38const TAG_SMALL_INT: u8 = 0x03;
39const TAG_INT: u8 = 0x04;
40const TAG_BIG_INT: u8 = 0x05;
41const TAG_FLOAT: u8 = 0x06;
42const TAG_DOUBLE: u8 = 0x07;
43const TAG_DECIMAL: u8 = 0x08;
44const TAG_DATE: u8 = 0x09;
45const TAG_TIME: u8 = 0x0A;
46const TAG_DATE_TIME: u8 = 0x0B;
47const TAG_STRING: u8 = 0x0C;
48const TAG_FIXED_STRING: u8 = 0x0D;
49const TAG_BLOB: u8 = 0x0E;
50const TAG_VERTEX_ID: u8 = 0x0F;
51const TAG_EDGE_REF: u8 = 0x10;
52const TAG_UUID: u8 = 0x1A;
53const TAG_NULL_LAST: u8 = 0xFF;
54const NULL_KIND_EMPTY: u8 = 0xFF;
55
56/// Order-preserving codec for index keys.
57///
58/// Stateless: can be shared and reused freely.
59#[derive(Debug, Clone, Copy)]
60pub struct OrderedCodec {
61    version: u8,
62}
63
64impl OrderedCodec {
65    pub const fn new() -> Self {
66        Self {
67            version: ORDERED_CODEC_VERSION,
68        }
69    }
70
71    pub const fn version(&self) -> u8 {
72        self.version
73    }
74
75    /// Whether `OrderedCodec` can encode values of the given type as an
76    /// ordered index key.
77    ///
78    /// This is the single source of truth for the set of indexable types:
79    /// it must stay in sync with [`OrderedCodec::encode_value`]. A type that
80    /// returns `false` here is rejected at index-creation time and would also
81    /// be rejected at encode time.
82    pub fn supports_ordered_key(data_type: &crate::DataType) -> bool {
83        matches!(
84            data_type,
85            crate::DataType::Bool
86                | crate::DataType::SmallInt
87                | crate::DataType::Int
88                | crate::DataType::BigInt
89                | crate::DataType::Float
90                | crate::DataType::Double
91                | crate::DataType::Decimal128
92                | crate::DataType::String
93                | crate::DataType::FixedString(_)
94                | crate::DataType::Blob
95                | crate::DataType::Date
96                | crate::DataType::Time
97                | crate::DataType::DateTime
98                | crate::DataType::Uuid
99        )
100    }
101
102    /// Encode a single `Value` into order-preserving bytes.
103    pub fn encode(&self, value: &Value) -> Result<Vec<u8>, StorageError> {
104        let mut buf = Vec::new();
105        self.encode_value(value, &mut buf, false)?;
106        Ok(buf)
107    }
108
109    /// Encode value with null-last placement (sorts after all non-null values).
110    pub fn encode_null_last(&self, value: &Value) -> Result<Vec<u8>, StorageError> {
111        let mut buf = Vec::new();
112        self.encode_value(value, &mut buf, true)?;
113        Ok(buf)
114    }
115
116    /// Decode a single `Value` from bytes produced by [`encode`].
117    pub fn decode(&self, bytes: &[u8]) -> Result<Value, StorageError> {
118        let (value, consumed) = self.decode_value_inner(bytes)?;
119        if consumed != bytes.len() {
120            return Err(StorageError::deserialize_error(format!(
121                "OrderedCodec decode: {} bytes trailing data after value",
122                bytes.len() - consumed
123            )));
124        }
125        Ok(value)
126    }
127
128    /// Decode the first value; return the value and bytes consumed.
129    pub fn decode_value_inner(&self, bytes: &[u8]) -> Result<(Value, usize), StorageError> {
130        if bytes.is_empty() {
131            return Err(StorageError::deserialize_error(
132                "OrderedCodec decode_value: empty input",
133            ));
134        }
135        let tag = bytes[0];
136        match tag {
137            TAG_EMPTY => Ok((Value::Empty, 1)),
138            TAG_NULL | TAG_NULL_LAST => {
139                if bytes.len() < 2 {
140                    return Err(StorageError::deserialize_error("truncated null value"));
141                }
142                let kind = bytes[1];
143                if tag == TAG_NULL_LAST && kind == NULL_KIND_EMPTY {
144                    Ok((Value::Empty, 2))
145                } else {
146                    Ok((Value::Null(decode_null_type(kind)?), 2))
147                }
148            }
149            TAG_BOOL => {
150                if bytes.len() < 2 {
151                    return Err(StorageError::deserialize_error("truncated bool"));
152                }
153                Ok((Value::Bool(bytes[1] != 0), 2))
154            }
155            TAG_SMALL_INT => {
156                if bytes.len() < 3 {
157                    return Err(StorageError::deserialize_error("truncated smallint"));
158                }
159                let raw = u16::from_be_bytes([bytes[1], bytes[2]]);
160                Ok((Value::SmallInt((raw ^ 0x8000) as i16), 3))
161            }
162            TAG_INT => {
163                if bytes.len() < 5 {
164                    return Err(StorageError::deserialize_error("truncated int"));
165                }
166                let raw = u32::from_be_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]);
167                Ok((Value::Int((raw ^ 0x8000_0000) as i32), 5))
168            }
169            TAG_BIG_INT => {
170                if bytes.len() < 9 {
171                    return Err(StorageError::deserialize_error("truncated bigint"));
172                }
173                let raw = u64::from_be_bytes([
174                    bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8],
175                ]);
176                Ok((Value::BigInt((raw ^ 0x8000_0000_0000_0000) as i64), 9))
177            }
178            TAG_FLOAT => {
179                if bytes.len() < 5 {
180                    return Err(StorageError::deserialize_error("truncated float"));
181                }
182                let raw = u32::from_be_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]);
183                Ok((Value::Float(decode_f32_ordered(raw)), 5))
184            }
185            TAG_DOUBLE => {
186                if bytes.len() < 9 {
187                    return Err(StorageError::deserialize_error("truncated double"));
188                }
189                let raw = u64::from_be_bytes([
190                    bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8],
191                ]);
192                Ok((Value::Double(decode_f64_ordered(raw)), 9))
193            }
194            TAG_DECIMAL => {
195                let (value, consumed) = decode_decimal(bytes)?;
196                Ok((value, consumed))
197            }
198            TAG_STRING | TAG_FIXED_STRING | TAG_BLOB => {
199                let (data, end) = decode_escaped_bytes(bytes, 1)?;
200                if tag == TAG_STRING {
201                    let s = String::from_utf8(data).map_err(|e| {
202                        StorageError::deserialize_error(format!("invalid UTF-8: {}", e))
203                    })?;
204                    Ok((Value::String(CompactString::from(s)), end))
205                } else if tag == TAG_BLOB {
206                    Ok((Value::Blob(data), end))
207                } else {
208                    let s = String::from_utf8(data).map_err(|e| {
209                        StorageError::deserialize_error(format!("invalid UTF-8: {}", e))
210                    })?;
211                    Ok((Value::FixedString(s), end))
212                }
213            }
214            TAG_DATE => {
215                if bytes.len() < 7 {
216                    return Err(StorageError::deserialize_error("truncated date"));
217                }
218                let raw_year = u32::from_be_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]);
219                let year = (raw_year ^ 0x8000_0000) as i32;
220                let month = bytes[5] as u32;
221                let day = bytes[6] as u32;
222                Ok((Value::Date(DateValue { year, month, day }), 7))
223            }
224            TAG_TIME => {
225                if bytes.len() < 9 {
226                    return Err(StorageError::deserialize_error("truncated time"));
227                }
228                let total_micros = u64::from_be_bytes([
229                    bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8],
230                ]);
231                let microsec = (total_micros % 1_000_000) as u32;
232                let total_secs = total_micros / 1_000_000;
233                let hour = (total_secs / 3600) as u32;
234                let minute = ((total_secs % 3600) / 60) as u32;
235                let sec = (total_secs % 60) as u32;
236                Ok((
237                    Value::Time(TimeValue {
238                        hour,
239                        minute,
240                        sec,
241                        microsec,
242                    }),
243                    9,
244                ))
245            }
246            TAG_DATE_TIME => {
247                if bytes.len() < 15 {
248                    return Err(StorageError::deserialize_error("truncated datetime"));
249                }
250                let raw_year = u32::from_be_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]);
251                let year = (raw_year ^ 0x8000_0000) as i32;
252                let month = bytes[5] as u32;
253                let day = bytes[6] as u32;
254                let total_micros = u64::from_be_bytes([
255                    bytes[7], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13],
256                    bytes[14],
257                ]);
258                let microsec = (total_micros % 1_000_000) as u32;
259                let total_secs = total_micros / 1_000_000;
260                let hour = (total_secs / 3600) as u32;
261                let minute = ((total_secs % 3600) / 60) as u32;
262                let sec = (total_secs % 60) as u32;
263                Ok((
264                    Value::DateTime(DateTimeValue {
265                        year,
266                        month,
267                        day,
268                        hour,
269                        minute,
270                        sec,
271                        microsec,
272                    }),
273                    15,
274                ))
275            }
276            TAG_UUID => {
277                if bytes.len() < 17 {
278                    return Err(StorageError::deserialize_error("truncated uuid"));
279                }
280                let mut uuid_bytes = [0u8; 16];
281                uuid_bytes.copy_from_slice(&bytes[1..17]);
282                Ok((Value::Uuid(UuidValue(uuid_bytes)), 17))
283            }
284            TAG_VERTEX_ID => {
285                let (entity, consumed) = self.decode_entity_bytes(bytes)?;
286                match entity {
287                    EntityRef::Vertex(vid) => {
288                        let val = Value::VertexId(vid);
289                        Ok((val, consumed))
290                    }
291                    EntityRef::Edge { .. } => Err(StorageError::deserialize_error(
292                        "expected vertex tag but found edge ref",
293                    )),
294                }
295            }
296            TAG_EDGE_REF => {
297                let (_entity, consumed) = self.decode_entity_bytes(bytes)?;
298                // Edge refs are rare as index key values; encode as debug string
299                Ok((Value::String(CompactString::new("(edge)")), consumed))
300            }
301            _ => Err(StorageError::deserialize_error(format!(
302                "unknown type tag 0x{:02x}",
303                tag
304            ))),
305        }
306    }
307
308    /// Encode a composite index key from multiple values and an optional entity
309    /// tie-breaker.
310    pub fn encode_composite(
311        &self,
312        values: &[&Value],
313        entity: Option<&EntityRef>,
314        null_last: bool,
315    ) -> Result<Vec<u8>, StorageError> {
316        let mut buf = Vec::new();
317        for v in values {
318            self.encode_value(v, &mut buf, null_last)?;
319        }
320        if let Some(e) = entity {
321            self.encode_entity(e, &mut buf)?;
322        }
323        Ok(buf)
324    }
325
326    /// Encode an entity reference (Vertex or Edge) as a tie-breaker.
327    pub fn encode_entity(&self, entity: &EntityRef, buf: &mut Vec<u8>) -> Result<(), StorageError> {
328        match entity {
329            EntityRef::Vertex(vid) => {
330                buf.push(TAG_VERTEX_ID);
331                write_vertex_id(vid, buf);
332                Ok(())
333            }
334            EntityRef::Edge {
335                src,
336                dst,
337                edge_type,
338                ranking,
339            } => {
340                buf.push(TAG_EDGE_REF);
341                write_vertex_id(src, buf);
342                write_vertex_id(dst, buf);
343                buf.extend_from_slice(&edge_type.to_be_bytes());
344                buf.extend_from_slice(&ranking.to_be_bytes());
345                Ok(())
346            }
347        }
348    }
349
350    /// Decode an entity reference from bytes.
351    pub fn decode_entity_bytes(&self, bytes: &[u8]) -> Result<(EntityRef, usize), StorageError> {
352        if bytes.is_empty() {
353            return Err(StorageError::deserialize_error("empty entity input"));
354        }
355        let tag = bytes[0];
356        match tag {
357            TAG_VERTEX_ID => {
358                let (vid, consumed) = read_vertex_id(&bytes[1..])?;
359                Ok((EntityRef::Vertex(vid), 1 + consumed))
360            }
361            TAG_EDGE_REF => {
362                let mut pos = 1;
363                let (src, c1) = read_vertex_id(&bytes[pos..])?;
364                pos += c1;
365                let (dst, c2) = read_vertex_id(&bytes[pos..])?;
366                pos += c2;
367                if bytes.len() < pos + 12 {
368                    return Err(StorageError::deserialize_error("truncated edge ref"));
369                }
370                let et_bytes: [u8; 4] = bytes[pos..pos + 4]
371                    .try_into()
372                    .map_err(|_| StorageError::deserialize_error("invalid edge type bytes"))?;
373                let rank_bytes: [u8; 8] = bytes[pos + 4..pos + 12]
374                    .try_into()
375                    .map_err(|_| StorageError::deserialize_error("invalid edge ranking bytes"))?;
376                pos += 12;
377                Ok((
378                    EntityRef::Edge {
379                        src,
380                        dst,
381                        edge_type: u32::from_be_bytes(et_bytes),
382                        ranking: i64::from_be_bytes(rank_bytes),
383                    },
384                    pos,
385                ))
386            }
387            _ => Err(StorageError::deserialize_error(format!(
388                "unknown entity tag 0x{:02x}",
389                tag
390            ))),
391        }
392    }
393
394    /// Compute the prefix upper bound for range scanning.
395    ///
396    /// Increments the last byte of the prefix (with carry) to produce an
397    /// exclusive upper bound for a tight prefix range.
398    pub fn prefix_upper_bound(prefix: &[u8]) -> Vec<u8> {
399        let mut end = prefix.to_vec();
400        for i in (0..end.len()).rev() {
401            if end[i] == 0xFF {
402                end[i] = 0x00;
403            } else {
404                end[i] += 1;
405                break;
406            }
407        }
408        end
409    }
410
411    /// Return tight (inclusive lower, exclusive upper) byte bounds for a
412    /// prefix scan over the given value.
413    ///
414    /// For fixed-length types the bounds are the encoded value and its
415    /// [`prefix_upper_bound`].  For variable-length types (String, Blob)
416    /// the lower bound is the full encoded value (including terminator)
417    /// and the upper bound is the tag followed by
418    /// `prefix_upper_bound(data_bytes)` — this correctly covers all values
419    /// whose encoded prefix equals the given value's encoding while
420    /// excluding the next value in order.
421    pub fn prefix_bounds(&self, value: &Value) -> Result<(Vec<u8>, Vec<u8>), StorageError> {
422        match value {
423            // Fixed-length types: simple upper bound from the encoded form.
424            Value::Null(_)
425            | Value::Empty
426            | Value::Bool(_)
427            | Value::SmallInt(_)
428            | Value::Int(_)
429            | Value::BigInt(_)
430            | Value::Float(_)
431            | Value::Double(_)
432            | Value::Date(_)
433            | Value::Time(_)
434            | Value::DateTime(_)
435            | Value::Uuid(_) => {
436                let lower = self.encode(value)?;
437                let upper = Self::prefix_upper_bound(&lower);
438                Ok((lower, upper))
439            }
440
441            // String / fixed string / blob: escaped-terminator based, tight
442            // upper bound. The length suffix of a fixed string is deliberately
443            // outside the prefix range so all values with the requested data
444            // prefix remain covered.
445            // Lower = full encoded value (TAG + data + 0x00).
446            // Upper = TAG + prefix_upper_bound(data).
447            Value::String(_) | Value::FixedString(_) | Value::Blob(_) => {
448                let lower = self.encode(value)?;
449                let tag = lower[0];
450                let (_, terminator_end) = decode_escaped_bytes(&lower, 1)?;
451                let data = &lower[1..terminator_end - 2];
452                let mut upper = vec![tag];
453                if data.is_empty() {
454                    upper = Self::prefix_upper_bound(&upper);
455                } else {
456                    upper.extend_from_slice(&Self::prefix_upper_bound(data));
457                }
458                Ok((lower, upper))
459            }
460
461            // Complex / non-indexable types: error.
462            other => Err(StorageError::db_error(format!(
463                "prefix_bounds not supported for type {:?}",
464                other
465            ))),
466        }
467    }
468
469    // ── Internal encoding ────────────────────────────────────────────────────
470
471    fn encode_value(
472        &self,
473        value: &Value,
474        buf: &mut Vec<u8>,
475        null_last: bool,
476    ) -> Result<(), StorageError> {
477        match value {
478            Value::Empty => {
479                if null_last {
480                    buf.extend_from_slice(&[TAG_NULL_LAST, NULL_KIND_EMPTY]);
481                } else {
482                    buf.push(TAG_EMPTY);
483                }
484            }
485            Value::Null(null_type) => {
486                buf.push(if null_last { TAG_NULL_LAST } else { TAG_NULL });
487                buf.push(encode_null_type(null_type));
488            }
489            Value::Bool(b) => {
490                buf.push(TAG_BOOL);
491                buf.push(u8::from(*b));
492            }
493            Value::SmallInt(n) => {
494                buf.push(TAG_SMALL_INT);
495                buf.extend_from_slice(&((*n as u16) ^ 0x8000).to_be_bytes());
496            }
497            Value::Int(n) => {
498                buf.push(TAG_INT);
499                buf.extend_from_slice(&((*n as u32) ^ 0x8000_0000).to_be_bytes());
500            }
501            Value::BigInt(n) => {
502                buf.push(TAG_BIG_INT);
503                buf.extend_from_slice(&((*n as u64) ^ 0x8000_0000_0000_0000).to_be_bytes());
504            }
505            Value::Float(f) => {
506                buf.push(TAG_FLOAT);
507                buf.extend_from_slice(&encode_f32_ordered(*f).to_be_bytes());
508            }
509            Value::Double(f) => {
510                buf.push(TAG_DOUBLE);
511                buf.extend_from_slice(&encode_f64_ordered(*f).to_be_bytes());
512            }
513            Value::Decimal128(decimal) => {
514                encode_decimal(decimal, buf)?;
515            }
516            Value::String(s) => {
517                buf.push(TAG_STRING);
518                encode_escaped_bytes(s.as_bytes(), buf);
519            }
520            Value::FixedString(data) => {
521                buf.push(TAG_FIXED_STRING);
522                encode_escaped_bytes(data.as_bytes(), buf);
523            }
524            Value::Blob(b) => {
525                buf.push(TAG_BLOB);
526                encode_escaped_bytes(b, buf);
527            }
528            Value::Date(d) => {
529                buf.push(TAG_DATE);
530                buf.extend_from_slice(&((d.year as u32) ^ 0x8000_0000).to_be_bytes());
531                buf.push(d.month as u8);
532                buf.push(d.day as u8);
533            }
534            Value::Time(t) => {
535                buf.push(TAG_TIME);
536                let total_micros = (t.hour as u64) * 3_600_000_000
537                    + (t.minute as u64) * 60_000_000
538                    + (t.sec as u64) * 1_000_000
539                    + t.microsec as u64;
540                buf.extend_from_slice(&total_micros.to_be_bytes());
541            }
542            Value::DateTime(dt) => {
543                buf.push(TAG_DATE_TIME);
544                buf.extend_from_slice(&((dt.year as u32) ^ 0x8000_0000).to_be_bytes());
545                buf.push(dt.month as u8);
546                buf.push(dt.day as u8);
547                let total_micros = (dt.hour as u64) * 3_600_000_000
548                    + (dt.minute as u64) * 60_000_000
549                    + (dt.sec as u64) * 1_000_000
550                    + dt.microsec as u64;
551                buf.extend_from_slice(&total_micros.to_be_bytes());
552            }
553            Value::Uuid(u) => {
554                buf.push(TAG_UUID);
555                buf.extend_from_slice(&u.0);
556            }
557            // Complex values are not valid ordered index fields. Silently
558            // encoding them as NULL would create false index matches.
559            Value::Vertex(_)
560            | Value::Edge(_)
561            | Value::EdgeHeader(_)
562            | Value::Path(_)
563            | Value::List(_)
564            | Value::Map(_)
565            | Value::Set(_)
566            | Value::Geography(_)
567            | Value::Vector(_)
568            | Value::DataSet(_)
569            | Value::Json(_)
570            | Value::JsonB(_)
571            | Value::Interval(_)
572            | Value::VertexId(_)
573            | Value::EdgeId(_)
574            | Value::Struct(_)
575            | Value::Array(_) => {
576                return Err(StorageError::invalid_input(format!(
577                    "Value type {:?} is not supported by OrderedCodec",
578                    value.get_type()
579                )));
580            }
581        }
582        Ok(())
583    }
584}
585
586fn encode_escaped_bytes(data: &[u8], buf: &mut Vec<u8>) {
587    for byte in data {
588        if *byte == 0 {
589            buf.extend_from_slice(&[0, 1]);
590        } else {
591            buf.push(*byte);
592        }
593    }
594    buf.extend_from_slice(&[0, 0]);
595}
596
597fn encode_null_type(null_type: &NullType) -> u8 {
598    match null_type {
599        NullType::Null => 0,
600        NullType::NaN => 1,
601        NullType::BadData => 2,
602        NullType::BadType => 3,
603        NullType::ErrOverflow => 4,
604        NullType::UnknownProp => 5,
605        NullType::DivByZero => 6,
606        NullType::OutOfRange => 7,
607    }
608}
609
610fn decode_null_type(code: u8) -> Result<NullType, StorageError> {
611    match code {
612        0 => Ok(NullType::Null),
613        1 => Ok(NullType::NaN),
614        2 => Ok(NullType::BadData),
615        3 => Ok(NullType::BadType),
616        4 => Ok(NullType::ErrOverflow),
617        5 => Ok(NullType::UnknownProp),
618        6 => Ok(NullType::DivByZero),
619        7 => Ok(NullType::OutOfRange),
620        _ => Err(StorageError::deserialize_error(format!(
621            "invalid null type code {code}"
622        ))),
623    }
624}
625
626fn decode_escaped_bytes(
627    bytes: &[u8],
628    mut position: usize,
629) -> Result<(Vec<u8>, usize), StorageError> {
630    let mut data = Vec::new();
631    while position < bytes.len() {
632        let byte = bytes[position];
633        position += 1;
634        if byte != 0 {
635            data.push(byte);
636            continue;
637        }
638        let escape = *bytes.get(position).ok_or_else(|| {
639            StorageError::deserialize_error("missing variable-length value terminator")
640        })?;
641        position += 1;
642        match escape {
643            0 => return Ok((data, position)),
644            1 => data.push(0),
645            _ => {
646                return Err(StorageError::deserialize_error(
647                    "invalid variable-length value escape",
648                ))
649            }
650        }
651    }
652    Err(StorageError::deserialize_error(
653        "missing variable-length value terminator",
654    ))
655}
656
657fn encode_decimal(
658    decimal: &crate::value::Decimal128Value,
659    buf: &mut Vec<u8>,
660) -> Result<(), StorageError> {
661    let text = decimal.to_string();
662    let negative = text.starts_with('-');
663    let unsigned = text
664        .strip_prefix('-')
665        .or_else(|| text.strip_prefix('+'))
666        .unwrap_or(&text);
667    let (mantissa, exponent_part) = unsigned
668        .find(['e', 'E'])
669        .map_or((unsigned, "0"), |position| {
670            (&unsigned[..position], &unsigned[position + 1..])
671        });
672    let exponent10 = exponent_part.parse::<i32>().map_err(|error| {
673        StorageError::serialize_error(format!("Invalid decimal exponent: {error}"))
674    })?;
675    let (whole, fractional) = mantissa.split_once('.').unwrap_or((mantissa, ""));
676    let mut digits = whole.bytes().chain(fractional.bytes()).collect::<Vec<_>>();
677    if digits.is_empty() || digits.iter().any(|digit| !digit.is_ascii_digit()) {
678        return Err(StorageError::serialize_error(format!(
679            "Invalid decimal value {text}"
680        )));
681    }
682    let is_zero = digits.iter().all(|digit| *digit == b'0');
683    let (digit_exponent, negative) = if is_zero {
684        digits.clear();
685        digits.push(b'0');
686        (i32::MIN, false)
687    } else {
688        let leading_zero_count = digits.iter().take_while(|digit| **digit == b'0').count();
689        let whole_len = i32::try_from(whole.len())
690            .map_err(|_| StorageError::serialize_error("Decimal integral part is too long"))?;
691        let leading_zero_count = i32::try_from(leading_zero_count)
692            .map_err(|_| StorageError::serialize_error("Decimal significand is too long"))?;
693        let digit_exponent = whole_len
694            .checked_add(exponent10)
695            .and_then(|value| value.checked_sub(leading_zero_count))
696            .ok_or_else(|| StorageError::serialize_error("Decimal exponent is out of range"))?;
697
698        digits.drain(..leading_zero_count as usize);
699        while digits.last() == Some(&b'0') {
700            digits.pop();
701        }
702        (digit_exponent, negative)
703    };
704    let biased = (digit_exponent as u32) ^ 0x8000_0000;
705    buf.push(TAG_DECIMAL);
706    buf.push(u8::from(!negative));
707    buf.extend_from_slice(&(if negative { !biased } else { biased }).to_be_bytes());
708    if negative {
709        buf.extend(digits.into_iter().map(|digit| !digit));
710        buf.push(0xFF);
711    } else {
712        buf.extend_from_slice(&digits);
713        buf.push(0x00);
714    }
715    Ok(())
716}
717
718fn decode_decimal(bytes: &[u8]) -> Result<(Value, usize), StorageError> {
719    if bytes.len() < 7 {
720        return Err(StorageError::deserialize_error("truncated decimal"));
721    }
722    let sign = bytes[1];
723    if sign > 1 {
724        return Err(StorageError::deserialize_error("invalid decimal sign"));
725    }
726    let raw = u32::from_be_bytes([bytes[2], bytes[3], bytes[4], bytes[5]]);
727    let wire_exponent = if sign == 0 { !raw } else { raw };
728    let exponent = (wire_exponent ^ 0x8000_0000) as i32;
729    let terminator = if sign == 0 { 0xFF } else { 0x00 };
730    let mut position = 6;
731    let mut digits = Vec::new();
732    while position < bytes.len() && bytes[position] != terminator {
733        let digit = if sign == 0 {
734            !bytes[position]
735        } else {
736            bytes[position]
737        };
738        if !digit.is_ascii_digit() {
739            return Err(StorageError::deserialize_error(
740                "invalid decimal significand",
741            ));
742        }
743        digits.push(digit);
744        position += 1;
745    }
746    if digits.is_empty() || position == bytes.len() {
747        return Err(StorageError::deserialize_error(
748            "missing decimal terminator",
749        ));
750    }
751    position += 1;
752    if digits.iter().all(|digit| *digit == b'0') {
753        let value = "0"
754            .parse::<crate::value::Decimal128Value>()
755            .map_err(|error| {
756                StorageError::deserialize_error(format!("invalid decimal: {error}"))
757            })?;
758        return Ok((Value::Decimal128(value), position));
759    }
760    let decimal_position = exponent;
761    let digit_count = i32::try_from(digits.len())
762        .map_err(|_| StorageError::deserialize_error("decimal significand is too long"))?;
763    let exponent10 = decimal_position
764        .checked_sub(digit_count)
765        .ok_or_else(|| StorageError::deserialize_error("decimal exponent is out of range"))?;
766    let mut text = String::new();
767    if sign == 0 && digits.iter().any(|digit| *digit != b'0') {
768        text.push('-');
769    }
770    text.extend(digits.iter().map(|digit| char::from(*digit)));
771    if exponent10 != 0 {
772        text.push('e');
773        text.push_str(&exponent10.to_string());
774    }
775    let value = text
776        .parse::<crate::value::Decimal128Value>()
777        .map_err(|error| StorageError::deserialize_error(format!("invalid decimal: {error}")))?;
778    Ok((Value::Decimal128(value), position))
779}
780
781impl Default for OrderedCodec {
782    fn default() -> Self {
783        Self::new()
784    }
785}
786
787// ── VertexId I/O ────────────────────────────────────────────────────────────
788
789/// Write a VertexId as fixed-size 34 bytes for order-preserving comparison.
790/// Format: [data:32] [len:1] [kind:1]. The zero padding keeps byte ordering
791/// identical to the raw VertexId bytes within one kind, including prefix
792/// relationships; the trailing kind byte disambiguates equal payloads.
793fn write_vertex_id(vid: &VertexId, buf: &mut Vec<u8>) {
794    let bytes = vid.as_bytes();
795    buf.extend_from_slice(bytes);
796    let pad = 32usize.saturating_sub(bytes.len());
797    buf.extend(std::iter::repeat_n(0u8, pad));
798    buf.push(bytes.len() as u8);
799    buf.push(vid.kind().as_u8());
800}
801
802/// Read a VertexId from 34 fixed-size bytes.
803fn read_vertex_id(bytes: &[u8]) -> Result<(VertexId, usize), StorageError> {
804    use crate::types::storage_ids::VertexIdKind;
805    if bytes.len() < 34 {
806        return Err(StorageError::deserialize_error(
807            "truncated vertex id (need 34)",
808        ));
809    }
810    let len = bytes[32] as usize;
811    if len > 32 {
812        return Err(StorageError::deserialize_error("invalid vertex id length"));
813    }
814    if bytes[len..32].iter().any(|byte| *byte != 0) {
815        return Err(StorageError::deserialize_error(
816            "non-zero vertex id padding",
817        ));
818    }
819    let kind = VertexIdKind::from_u8(bytes[33])
820        .ok_or_else(|| StorageError::deserialize_error("unknown vertex id kind"))?;
821    let data = &bytes[..len];
822    VertexId::from_typed_bytes(kind, data)
823        .map(|vid| (vid, 34))
824        .map_err(StorageError::deserialize_error)
825}
826
827// ── IEEE 754 total-order encoding ───────────────────────────────────────────
828
829fn encode_f32_ordered(f: f32) -> u32 {
830    if f.is_nan() {
831        return 0;
832    }
833    if f == 0.0 {
834        return 0x8000_0001;
835    }
836    let bits = f.to_bits();
837    let ordered = if f.is_sign_negative() {
838        !bits
839    } else {
840        bits ^ 0x8000_0000
841    };
842    ordered + 1
843}
844
845fn decode_f32_ordered(bits: u32) -> f32 {
846    if bits == 0 {
847        return f32::NAN;
848    }
849    let ordered = bits - 1;
850    if ordered & 0x8000_0000 != 0 {
851        f32::from_bits(ordered ^ 0x8000_0000)
852    } else {
853        f32::from_bits(!ordered)
854    }
855}
856
857fn encode_f64_ordered(f: f64) -> u64 {
858    if f.is_nan() {
859        return 0;
860    }
861    if f == 0.0 {
862        return 0x8000_0000_0000_0001;
863    }
864    let bits = f.to_bits();
865    let ordered = if f.is_sign_negative() {
866        !bits
867    } else {
868        bits ^ 0x8000_0000_0000_0000
869    };
870    ordered + 1
871}
872
873fn decode_f64_ordered(bits: u64) -> f64 {
874    if bits == 0 {
875        return f64::NAN;
876    }
877    let ordered = bits - 1;
878    if ordered & 0x8000_0000_0000_0000 != 0 {
879        f64::from_bits(ordered ^ 0x8000_0000_0000_0000)
880    } else {
881        f64::from_bits(!ordered)
882    }
883}
884
885#[cfg(test)]
886mod tests {
887    use super::*;
888    use crate::value::date_time::{DateTimeValue, DateValue, TimeValue};
889    use crate::value::UuidValue;
890
891    fn codec() -> OrderedCodec {
892        OrderedCodec::new()
893    }
894
895    #[test]
896    fn test_null() {
897        let v = Value::Null(NullType::Null);
898        let enc = codec().encode(&v).unwrap();
899        let dec = codec().decode(&enc).unwrap();
900        assert_eq!(dec, v);
901    }
902
903    #[test]
904    fn test_null_variants_roundtrip_in_wire_order() {
905        let values = [
906            NullType::Null,
907            NullType::NaN,
908            NullType::BadData,
909            NullType::BadType,
910            NullType::ErrOverflow,
911            NullType::UnknownProp,
912            NullType::DivByZero,
913            NullType::OutOfRange,
914        ];
915        for pair in values.windows(2) {
916            let left = Value::Null(pair[0].clone());
917            let right = Value::Null(pair[1].clone());
918            assert_eq!(
919                codec()
920                    .encode(&left)
921                    .unwrap()
922                    .cmp(&codec().encode(&right).unwrap()),
923                std::cmp::Ordering::Less
924            );
925        }
926        for null_type in values {
927            let value = Value::Null(null_type);
928            assert_eq!(
929                codec().decode(&codec().encode(&value).unwrap()).unwrap(),
930                value
931            );
932        }
933    }
934
935    #[test]
936    fn test_null_last_preserves_empty_and_null_variants() {
937        for value in [Value::Empty, Value::Null(NullType::BadType)] {
938            let encoded = codec().encode_null_last(&value).unwrap();
939            assert_eq!(codec().decode(&encoded).unwrap(), value);
940        }
941    }
942
943    #[test]
944    fn test_empty() {
945        let v = Value::Empty;
946        let enc = codec().encode(&v).unwrap();
947        let dec = codec().decode(&enc).unwrap();
948        assert_eq!(dec, v);
949    }
950
951    #[test]
952    fn test_bool() {
953        for v in &[Value::Bool(false), Value::Bool(true)] {
954            let enc = codec().encode(v).unwrap();
955            let dec = codec().decode(&enc).unwrap();
956            assert_eq!(dec, *v);
957        }
958    }
959
960    #[test]
961    fn test_bool_order() {
962        let f = codec().encode(&Value::Bool(false)).unwrap();
963        let t = codec().encode(&Value::Bool(true)).unwrap();
964        assert!(f < t, "false < true");
965    }
966
967    #[test]
968    fn test_ints() {
969        let cases = [
970            Value::SmallInt(i16::MIN),
971            Value::SmallInt(-1),
972            Value::SmallInt(0),
973            Value::SmallInt(42),
974            Value::SmallInt(i16::MAX),
975            Value::Int(i32::MIN),
976            Value::Int(-1),
977            Value::Int(0),
978            Value::Int(42),
979            Value::Int(i32::MAX),
980            Value::BigInt(i64::MIN),
981            Value::BigInt(-1),
982            Value::BigInt(0),
983            Value::BigInt(42),
984            Value::BigInt(i64::MAX),
985        ];
986        for v in &cases {
987            let enc = codec().encode(v).unwrap();
988            let dec = codec().decode(&enc).unwrap();
989            assert_eq!(dec, *v, "roundtrip failed for {:?}", v);
990        }
991    }
992
993    #[test]
994    fn test_int_order() {
995        let neg = codec().encode(&Value::Int(-5)).unwrap();
996        let zero = codec().encode(&Value::Int(0)).unwrap();
997        let pos = codec().encode(&Value::Int(5)).unwrap();
998        assert!(neg < zero, "-5 < 0");
999        assert!(zero < pos, "0 < 5");
1000    }
1001
1002    #[test]
1003    fn test_float() {
1004        let cases = [
1005            Value::Float(f32::NEG_INFINITY),
1006            Value::Float(-1.0),
1007            Value::Float(-0.0),
1008            Value::Float(0.0),
1009            Value::Float(1.0),
1010            Value::Float(f32::INFINITY),
1011            Value::Double(f64::NEG_INFINITY),
1012            Value::Double(-1.0),
1013            Value::Double(-0.0),
1014            Value::Double(0.0),
1015            Value::Double(1.0),
1016            Value::Double(f64::INFINITY),
1017        ];
1018        for v in &cases {
1019            let enc = codec().encode(v).unwrap();
1020            let dec = codec().decode(&enc).unwrap();
1021            assert_eq!(dec, *v, "roundtrip failed for {:?}", v);
1022        }
1023    }
1024
1025    #[test]
1026    fn test_float_order() {
1027        let neg = codec().encode(&Value::Float(-1.0)).unwrap();
1028        let zero = codec().encode(&Value::Float(0.0)).unwrap();
1029        let pos = codec().encode(&Value::Float(1.0)).unwrap();
1030        assert!(neg < zero, "-1 < 0");
1031        assert!(zero < pos, "0 < 1");
1032    }
1033
1034    #[test]
1035    fn test_float_special_values_follow_value_order() {
1036        let nan = Value::Float(f32::NAN);
1037        let negative_zero = Value::Float(-0.0);
1038        let positive_zero = Value::Float(0.0);
1039        let positive = Value::Float(1.0);
1040
1041        assert_eq!(
1042            codec().encode(&negative_zero).unwrap(),
1043            codec().encode(&positive_zero).unwrap()
1044        );
1045        assert_eq!(
1046            codec()
1047                .encode(&nan)
1048                .unwrap()
1049                .cmp(&codec().encode(&positive).unwrap()),
1050            nan.cmp(&positive)
1051        );
1052        assert_eq!(codec().decode(&codec().encode(&nan).unwrap()).unwrap(), nan);
1053    }
1054
1055    #[test]
1056    fn test_double_special_values_follow_value_order() {
1057        let nan = Value::Double(f64::NAN);
1058        let negative_zero = Value::Double(-0.0);
1059        let positive_zero = Value::Double(0.0);
1060        let positive = Value::Double(1.0);
1061
1062        assert_eq!(
1063            codec().encode(&negative_zero).unwrap(),
1064            codec().encode(&positive_zero).unwrap()
1065        );
1066        assert_eq!(
1067            codec()
1068                .encode(&nan)
1069                .unwrap()
1070                .cmp(&codec().encode(&positive).unwrap()),
1071            nan.cmp(&positive)
1072        );
1073        assert_eq!(codec().decode(&codec().encode(&nan).unwrap()).unwrap(), nan);
1074    }
1075
1076    #[test]
1077    fn test_string_roundtrip() {
1078        let cases = [
1079            Value::string(""),
1080            Value::string("hello"),
1081            Value::string("世界"),
1082            Value::FixedString("fixed".to_string()),
1083        ];
1084        for v in &cases {
1085            let enc = codec().encode(v).unwrap();
1086            let dec = codec().decode(&enc).unwrap();
1087            assert_eq!(dec, *v);
1088        }
1089    }
1090
1091    #[test]
1092    fn test_string_order_aa_less_than_b() {
1093        let aa = codec().encode(&Value::string("aa")).unwrap();
1094        let b = codec().encode(&Value::string("b")).unwrap();
1095        assert!(
1096            aa < b,
1097            "encoded('aa') < encoded('b') must hold for semantic ordering"
1098        );
1099    }
1100
1101    #[test]
1102    fn test_string_order() {
1103        let a = codec().encode(&Value::string("a")).unwrap();
1104        let b = codec().encode(&Value::string("b")).unwrap();
1105        assert!(a < b);
1106    }
1107
1108    #[test]
1109    fn test_blob_roundtrip() {
1110        let cases = vec![
1111            Value::Blob(vec![]),
1112            Value::Blob(vec![0x01, 0x02, 0x03]),
1113            Value::Blob(vec![0xFF, 0xFE]),
1114        ];
1115        for v in cases {
1116            let enc = codec().encode(&v).unwrap();
1117            let dec = codec().decode(&enc).unwrap();
1118            assert_eq!(dec, v, "roundtrip failed for {:?}", v);
1119        }
1120    }
1121
1122    #[test]
1123    fn test_blob_roundtrips_zero_byte() {
1124        let v = Value::Blob(vec![0x00, 0x01]);
1125        assert_eq!(codec().decode(&codec().encode(&v).unwrap()).unwrap(), v);
1126    }
1127
1128    #[test]
1129    fn test_string_roundtrips_zero_byte() {
1130        let v = Value::string("a\x00b");
1131        assert_eq!(codec().decode(&codec().encode(&v).unwrap()).unwrap(), v);
1132    }
1133
1134    #[test]
1135    fn test_prefix_bounds_string() {
1136        let (lower, upper) = codec().prefix_bounds(&Value::string("a")).unwrap();
1137        // lower = [TAG_STRING, 0x61, 0x00]
1138        assert_eq!(lower[0], TAG_STRING);
1139        assert_eq!(&lower[1..lower.len() - 2], b"a");
1140        assert_eq!(lower[lower.len() - 1], 0x00);
1141
1142        // upper = [TAG_STRING, 0x62]
1143        assert_eq!(upper, vec![TAG_STRING, 0x62]);
1144
1145        // Verify all "a*" strings fall within bounds
1146        for suffix in &["", "a", "aa", "az"] {
1147            let s_val = format!("a{}", suffix);
1148            let enc = codec().encode(&Value::string(s_val)).unwrap();
1149            assert!(enc.as_slice() >= lower.as_slice(), "{:?} >= lower", suffix);
1150            assert!(enc.as_slice() < upper.as_slice(), "{:?} < upper", suffix);
1151        }
1152
1153        // "b" is excluded
1154        let b_enc = codec().encode(&Value::string("b")).unwrap();
1155        assert!(b_enc.as_slice() >= upper.as_slice());
1156    }
1157
1158    #[test]
1159    fn test_prefix_bounds_multi_byte() {
1160        let (lower, upper) = codec().prefix_bounds(&Value::string("ab")).unwrap();
1161        assert_eq!(&lower[1..lower.len() - 2], b"ab");
1162        assert_eq!(upper, vec![TAG_STRING, 0x61, 0x63]);
1163
1164        let aba = codec().encode(&Value::string("aba")).unwrap();
1165        assert!(aba.as_slice() < upper.as_slice());
1166
1167        let ac = codec().encode(&Value::string("ac")).unwrap();
1168        assert!(ac.as_slice() >= upper.as_slice());
1169    }
1170
1171    #[test]
1172    fn test_prefix_bounds_blob() {
1173        let (lower, upper) = codec()
1174            .prefix_bounds(&Value::Blob(vec![0x01, 0x02]))
1175            .unwrap();
1176        assert_eq!(lower[0], TAG_BLOB);
1177        assert_eq!(&lower[1..lower.len() - 2], &[0x01, 0x02]);
1178        assert_eq!(upper, vec![TAG_BLOB, 0x01, 0x03]);
1179    }
1180
1181    #[test]
1182    fn test_prefix_bounds_int() {
1183        let (lower, upper) = codec().prefix_bounds(&Value::Int(42)).unwrap();
1184        assert!(lower < upper);
1185        // No other int with bytes starting with 42 exists (fixed-length),
1186        // so upper should follow right after lower
1187        let forty_two = codec().encode(&Value::Int(42)).unwrap();
1188        assert_eq!(lower, forty_two);
1189    }
1190
1191    #[test]
1192    fn test_encoded_order_matches_semantic_order() {
1193        // Verify that ordering is preserved for pairs that the length-prefixed
1194        // encoding got wrong.
1195        let pairs: [(Value, Value); 4] = [
1196            (Value::string("aa"), Value::string("b")),
1197            (Value::string("a"), Value::string("aa")),
1198            (Value::string(""), Value::string("a")),
1199            (Value::string("a"), Value::string("ab")),
1200        ];
1201        for (a, b) in &pairs {
1202            let enc_a = codec().encode(a).unwrap();
1203            let enc_b = codec().encode(b).unwrap();
1204            assert!(
1205                enc_a < enc_b,
1206                "ordered_codec: {:?} < {:?} should hold, but enc({:?}) >= enc({:?})",
1207                a,
1208                b,
1209                a,
1210                b
1211            );
1212        }
1213    }
1214
1215    #[test]
1216    fn test_prefix_bounds_overflow() {
1217        // Prefix with last byte = 0xFF should carry correctly
1218        let (lower, upper) = codec()
1219            .prefix_bounds(&Value::Blob(vec![0x01, 0xFF]))
1220            .unwrap();
1221        assert_eq!(&lower[1..lower.len() - 2], &[0x01, 0xFF]);
1222        // prefix_upper_bound([0x01, 0xFF]) = [0x02, 0x00]
1223        assert_eq!(upper, vec![TAG_BLOB, 0x02, 0x00]);
1224
1225        // Blob starting with [0x01, 0xFF] should be within bounds
1226        let test = codec()
1227            .encode(&Value::Blob(vec![0x01, 0xFF, 0x01]))
1228            .unwrap();
1229        assert!(test.as_slice() < upper.as_slice(), "0x01,0xFF,0x01 < upper");
1230        // Blob starting with [0x02] should be outside bounds
1231        let next = codec().encode(&Value::Blob(vec![0x02])).unwrap();
1232        assert!(next.as_slice() >= upper.as_slice(), "0x02 >= upper");
1233    }
1234
1235    #[test]
1236    fn test_uuid() {
1237        let u = Value::Uuid(UuidValue::from_bytes([
1238            0x55, 0x0E, 0x84, 0x00, 0xE2, 0x9B, 0x41, 0xD4, 0xA7, 0x16, 0x44, 0x66, 0x55, 0x44,
1239            0x00, 0x00,
1240        ]));
1241        let enc = codec().encode(&u).unwrap();
1242        let dec = codec().decode(&enc).unwrap();
1243        assert_eq!(dec, u);
1244    }
1245
1246    #[test]
1247    fn test_date() {
1248        let v = Value::Date(DateValue {
1249            year: 2024,
1250            month: 6,
1251            day: 15,
1252        });
1253        let enc = codec().encode(&v).unwrap();
1254        let dec = codec().decode(&enc).unwrap();
1255        assert_eq!(dec, v);
1256    }
1257
1258    #[test]
1259    fn test_time() {
1260        let v = Value::Time(TimeValue {
1261            hour: 14,
1262            minute: 30,
1263            sec: 0,
1264            microsec: 500_000,
1265        });
1266        let enc = codec().encode(&v).unwrap();
1267        let dec = codec().decode(&enc).unwrap();
1268        assert_eq!(dec, v);
1269    }
1270
1271    #[test]
1272    fn test_datetime() {
1273        let v = Value::DateTime(DateTimeValue {
1274            year: 2024,
1275            month: 6,
1276            day: 15,
1277            hour: 14,
1278            minute: 30,
1279            sec: 0,
1280            microsec: 500,
1281        });
1282        let enc = codec().encode(&v).unwrap();
1283        let dec = codec().decode(&enc).unwrap();
1284        assert_eq!(dec, v);
1285    }
1286
1287    #[test]
1288    fn test_composite_key() {
1289        let v1 = Value::string("name");
1290        let v2 = Value::Int(42);
1291        let vid = VertexId::try_from_int64(123).expect("test vertex id");
1292        let entity = EntityRef::Vertex(vid);
1293
1294        let key = codec()
1295            .encode_composite(&[&v1, &v2], Some(&entity), false)
1296            .unwrap();
1297        assert!(!key.is_empty());
1298
1299        let (dv1, c1) = codec().decode_value_inner(&key).unwrap();
1300        assert_eq!(dv1, v1);
1301        let (dv2, _c2) = codec().decode_value_inner(&key[c1..]).unwrap();
1302        assert_eq!(dv2, v2);
1303    }
1304
1305    #[test]
1306    fn test_composite_order() {
1307        let pre = Value::string("a");
1308        let k1 = codec()
1309            .encode_composite(&[&pre, &Value::Int(1)], None, false)
1310            .unwrap();
1311        let k2 = codec()
1312            .encode_composite(&[&pre, &Value::Int(2)], None, false)
1313            .unwrap();
1314        assert!(k1 < k2);
1315
1316        let k3 = codec()
1317            .encode_composite(&[&Value::string("b"), &Value::Int(0)], None, false)
1318            .unwrap();
1319        assert!(k1 < k3, "'a' prefix < 'b' prefix");
1320    }
1321
1322    #[test]
1323    fn test_prefix_upper_bound() {
1324        assert_eq!(OrderedCodec::prefix_upper_bound(&[1, 2, 3]), vec![1, 2, 4]);
1325        assert_eq!(OrderedCodec::prefix_upper_bound(&[1, 255]), vec![2, 0]);
1326        assert_eq!(
1327            OrderedCodec::prefix_upper_bound(&[255, 255, 255]),
1328            vec![0, 0, 0]
1329        );
1330    }
1331
1332    #[test]
1333    fn test_vertex_id_tie_breaker() {
1334        let vid = VertexId::try_from_int64(42).expect("test vertex id");
1335        let v = Value::string("hello");
1336        let key = codec()
1337            .encode_composite(&[&v], Some(&EntityRef::Vertex(vid)), false)
1338            .unwrap();
1339        let (dec, _c) = codec().decode_value_inner(&key).unwrap();
1340        assert_eq!(dec, v);
1341    }
1342
1343    #[test]
1344    fn test_vertex_id_entity_roundtrip_supports_max_length() {
1345        let vid =
1346            VertexId::try_from_string("12345678901234567890123456789012").expect("test vertex id");
1347        let entity = EntityRef::Vertex(vid);
1348        let mut encoded = Vec::new();
1349        codec().encode_entity(&entity, &mut encoded).unwrap();
1350
1351        let (decoded, consumed) = codec().decode_entity_bytes(&encoded).unwrap();
1352        assert_eq!(decoded, entity);
1353        assert_eq!(consumed, encoded.len());
1354    }
1355
1356    #[test]
1357    fn ordered_codec_integer_property() {
1358        let groups = [
1359            vec![
1360                Value::SmallInt(i16::MIN),
1361                Value::SmallInt(-1),
1362                Value::SmallInt(0),
1363                Value::SmallInt(i16::MAX),
1364            ],
1365            vec![
1366                Value::Int(i32::MIN),
1367                Value::Int(-1),
1368                Value::Int(0),
1369                Value::Int(i32::MAX),
1370            ],
1371            vec![
1372                Value::BigInt(i64::MIN),
1373                Value::BigInt(-1),
1374                Value::BigInt(0),
1375                Value::BigInt(i64::MAX),
1376            ],
1377        ];
1378        for values in groups {
1379            for pair in values.windows(2) {
1380                let semantic = pair[0].cmp(&pair[1]);
1381                let encoded = codec()
1382                    .encode(&pair[0])
1383                    .unwrap()
1384                    .cmp(&codec().encode(&pair[1]).unwrap());
1385                assert_eq!(
1386                    encoded, semantic,
1387                    "integer order changed for {:?} and {:?}",
1388                    pair[0], pair[1]
1389                );
1390            }
1391        }
1392    }
1393
1394    #[test]
1395    fn ordered_codec_string_and_blob_property() {
1396        let strings = ["", "a", "aa", "b", "世界", "z\0a"];
1397        for pair in strings.windows(2) {
1398            let left = Value::string(pair[0]);
1399            let right = Value::string(pair[1]);
1400            assert_eq!(
1401                codec()
1402                    .encode(&left)
1403                    .unwrap()
1404                    .cmp(&codec().encode(&right).unwrap()),
1405                left.cmp(&right)
1406            );
1407        }
1408
1409        let blobs = [vec![], vec![0], vec![0, 1], vec![1], vec![1, 255], vec![2]];
1410        for pair in blobs.windows(2) {
1411            let left = Value::Blob(pair[0].clone());
1412            let right = Value::Blob(pair[1].clone());
1413            assert_eq!(
1414                codec()
1415                    .encode(&left)
1416                    .unwrap()
1417                    .cmp(&codec().encode(&right).unwrap()),
1418                left.cmp(&right)
1419            );
1420        }
1421    }
1422
1423    #[test]
1424    fn ordered_codec_decimal_property() {
1425        let values = [
1426            "-100.5", "-1.25", "-0.01", "0", "0.01", "0.1", "1.2", "1.20", "10", "100.5", "1e3",
1427        ]
1428        .into_iter()
1429        .map(|text| Value::Decimal128(text.parse().expect("decimal fixture should parse")))
1430        .collect::<Vec<_>>();
1431        for pair in values.windows(2) {
1432            assert_eq!(
1433                codec()
1434                    .encode(&pair[0])
1435                    .unwrap()
1436                    .cmp(&codec().encode(&pair[1]).unwrap()),
1437                pair[0].cmp(&pair[1])
1438            );
1439            assert_eq!(
1440                codec().decode(&codec().encode(&pair[0]).unwrap()).unwrap(),
1441                pair[0]
1442            );
1443        }
1444    }
1445
1446    #[test]
1447    fn ordered_codec_rejects_unrepresentable_decimal_exponents() {
1448        let bytes = [TAG_DECIMAL, 1, 0, 0, 0, 0, b'1', 0];
1449        assert!(codec().decode(&bytes).is_err());
1450    }
1451
1452    #[test]
1453    fn ordered_codec_composite_prefix_property() {
1454        let first = Value::string("user");
1455        let second = Value::Int(1);
1456        let third = Value::Int(2);
1457        let first_key = codec()
1458            .encode_composite(&[&first, &second], None, false)
1459            .unwrap();
1460        let second_key = codec()
1461            .encode_composite(&[&first, &third], None, false)
1462            .unwrap();
1463        let other_key = codec()
1464            .encode_composite(&[&Value::string("user2"), &second], None, false)
1465            .unwrap();
1466        assert!(first_key < second_key);
1467        assert!(second_key < other_key);
1468        let (decoded_first, consumed) = codec().decode_value_inner(&first_key).unwrap();
1469        let (decoded_second, _) = codec().decode_value_inner(&first_key[consumed..]).unwrap();
1470        assert_eq!(decoded_first, first);
1471        assert_eq!(decoded_second, second);
1472    }
1473
1474    #[test]
1475    fn ordered_codec_supported_type_tags_follow_value_priority() {
1476        let values = [
1477            Value::Empty,
1478            Value::Null(NullType::Null),
1479            Value::Bool(false),
1480            Value::SmallInt(0),
1481            Value::Int(0),
1482            Value::BigInt(0),
1483            Value::Float(0.0),
1484            Value::Double(0.0),
1485            Value::Decimal128("0".parse().expect("decimal fixture should parse")),
1486            Value::Date(DateValue {
1487                year: 2024,
1488                month: 1,
1489                day: 1,
1490            }),
1491            Value::Time(TimeValue {
1492                hour: 0,
1493                minute: 0,
1494                sec: 0,
1495                microsec: 0,
1496            }),
1497            Value::DateTime(DateTimeValue {
1498                year: 2024,
1499                month: 1,
1500                day: 1,
1501                hour: 0,
1502                minute: 0,
1503                sec: 0,
1504                microsec: 0,
1505            }),
1506            Value::string(""),
1507            Value::FixedString(String::new()),
1508            Value::Blob(Vec::new()),
1509            Value::Uuid(UuidValue::from_bytes([0; 16])),
1510        ];
1511        for pair in values.windows(2) {
1512            assert_eq!(
1513                codec()
1514                    .encode(&pair[0])
1515                    .unwrap()
1516                    .cmp(&codec().encode(&pair[1]).unwrap()),
1517                std::cmp::Ordering::Less,
1518                "type order changed for {:?} and {:?}",
1519                pair[0],
1520                pair[1]
1521            );
1522        }
1523    }
1524}