Skip to main content

rushdb_types/
keys.rs

1use crate::ids::{OwnerKind, TenantId};
2use std::fmt::Write as _;
3
4/// Hex encoding used only for the small set of legacy-style, human-readable
5/// tenant metadata keys. Canonical record, vector, text, and security state
6/// remains under the binary tenant keyspace (`[TENANT_NS][tenant u32]/...`).
7#[must_use]
8pub fn tenant_hex(tenant: TenantId) -> String {
9    let mut out = String::with_capacity(8);
10    for byte in tenant.to_be_bytes() {
11        let _ = write!(out, "{byte:02x}");
12    }
13    out
14}
15
16/// Namespace byte of system rows (not tenant-scoped), storage format v3.
17pub const SYSTEM_NS: u8 = 0x00;
18/// Namespace byte of tenant-owned rows, storage format v3.
19pub const TENANT_NS: u8 = 0x01;
20/// Length of the tenant key prefix `[TENANT_NS][tenant u32 BE]`.
21pub const TENANT_PREFIX_LEN: usize = 5;
22
23/// System family: tenant scope digest → `(ordinal u32 BE, state u8)`.
24pub const SYS_TENANT_BY_SCOPE: u8 = 0x02;
25/// System family: tenant ordinal → `(scope digest, state u8)`.
26pub const SYS_TENANT_BY_ORD: u8 = 0x03;
27/// System family: next tenant ordinal to assign (`u32 BE`).
28pub const SYS_TENANT_SEQ: u8 = 0x04;
29
30/// The 5-byte key prefix owned by `tenant`.
31#[must_use]
32pub fn tenant_prefix(tenant: TenantId) -> [u8; TENANT_PREFIX_LEN] {
33    let [a, b, c, d] = tenant.to_be_bytes();
34    [TENANT_NS, a, b, c, d]
35}
36
37/// The tenant owning a tenant-namespace key, if `key` is one.
38#[must_use]
39pub fn parse_tenant(key: &[u8]) -> Option<TenantId> {
40    if key.first() != Some(&TENANT_NS) {
41        return None;
42    }
43    let bytes: [u8; 4] = key.get(1..TENANT_PREFIX_LEN)?.try_into().ok()?;
44    Some(TenantId(u32::from_be_bytes(bytes)))
45}
46
47/// Directory row: scope digest → ordinal.
48#[must_use]
49pub fn tenant_by_scope_key(scope: &crate::ids::TenantScope) -> Vec<u8> {
50    let mut out = Vec::with_capacity(34);
51    out.extend_from_slice(&[SYSTEM_NS, SYS_TENANT_BY_SCOPE]);
52    out.extend_from_slice(&scope.0);
53    out
54}
55
56/// Directory row: ordinal → scope digest.
57#[must_use]
58pub fn tenant_by_ord_key(tenant: TenantId) -> Vec<u8> {
59    let mut out = Vec::with_capacity(6);
60    out.extend_from_slice(&[SYSTEM_NS, SYS_TENANT_BY_ORD]);
61    out.extend_from_slice(&tenant.to_be_bytes());
62    out
63}
64
65/// Prefix of every ordinal → scope directory row (tenant enumeration).
66#[must_use]
67pub fn tenant_by_ord_prefix() -> [u8; 2] {
68    [SYSTEM_NS, SYS_TENANT_BY_ORD]
69}
70
71/// Directory row holding the next ordinal to assign.
72#[must_use]
73pub fn tenant_seq_key() -> [u8; 2] {
74    [SYSTEM_NS, SYS_TENANT_SEQ]
75}
76
77/// Prefix for durable vector-index definitions owned by one tenant.
78#[must_use]
79pub fn vector_index_meta_prefix(tenant: TenantId) -> String {
80    format!("vector/index/{}/", tenant_hex(tenant))
81}
82
83/// Prefix for durable ANN manifests owned by one tenant.
84#[must_use]
85pub fn ann_manifest_meta_prefix(tenant: TenantId) -> String {
86    format!("vector/ann-manifest/{}/", tenant_hex(tenant))
87}
88
89/// Key segment for tenant metadata rows.
90pub const P_META: &[u8] = b"meta";
91/// Key segment for relationship rows by public id.
92pub const P_REL: &[u8] = b"relationship";
93/// Key segment for relationship rows by dense ordinal.
94pub const P_REL_BY_ORD: &[u8] = b"relationship-ordinal";
95/// Per-relationship captured endpoint labels (cascade-safe decrement source).
96pub const P_REL_GROUP: &[u8] = b"rel-group";
97/// Key segment for property-family signatures.
98pub const P_FAMILY_SIG: &[u8] = b"property-family-signature";
99/// Key segment for property-family rows.
100pub const P_FAMILY: &[u8] = b"property-family";
101/// Key segment for property-definition rows.
102pub const P_DEFINITION: &[u8] = b"property-definition";
103/// Build a tenant keyspace key: `[TENANT_NS][tenant u32 BE]` followed by
104/// `/`-separated segments.
105#[must_use]
106pub fn key(tenant: TenantId, segments: &[&[u8]]) -> Vec<u8> {
107    let mut out =
108        Vec::with_capacity(TENANT_PREFIX_LEN + segments.iter().map(|s| s.len() + 1).sum::<usize>());
109    out.extend_from_slice(&tenant_prefix(tenant));
110    for s in segments {
111        out.push(b'/');
112        out.extend_from_slice(s);
113    }
114    out
115}
116
117/// Inclusive start / exclusive end range covering all keys under a prefix.
118///
119/// # Examples
120///
121/// ```
122/// use rushdb_types::keys::prefix_range;
123///
124/// let (start, end) = prefix_range(b"ab");
125/// assert_eq!(start, b"ab");
126/// assert_eq!(end, b"ac");
127/// ```
128#[must_use]
129pub fn prefix_range(prefix: &[u8]) -> (Vec<u8>, Vec<u8>) {
130    let mut end = prefix.to_vec();
131    loop {
132        match end.last_mut() {
133            Some(last) if *last != 0xFF => {
134                *last += 1;
135                return (prefix.to_vec(), end);
136            }
137            Some(_) => {
138                end.pop();
139            }
140            None => return (prefix.to_vec(), vec![]),
141        }
142    }
143}
144
145/// Big-endian 8-byte encoding of an ordinal, used as a key segment.
146#[must_use]
147pub fn u64be(v: u64) -> [u8; 8] {
148    v.to_be_bytes()
149}
150
151/// Key prefix for every row owned by records or relationships.
152#[must_use]
153pub fn owner_prefix(kind: OwnerKind) -> &'static [u8] {
154    match kind {
155        OwnerKind::Record => b"record",
156        OwnerKind::Relationship => b"rel",
157    }
158}
159
160/// Key segment for embedding-space definitions.
161pub const P_EMB_SPACE: &[u8] = b"emb-space";
162/// Key segment for embedding-space alias mappings.
163pub const P_EMB_ALIAS: &[u8] = b"emb-alias";
164/// Key segment for embedding bindings.
165pub const P_EMB_BINDING: &[u8] = b"emb-binding";
166/// Key segment for stored embedding vectors.
167pub const P_EMB_VECTOR: &[u8] = b"vec";
168
169/// Key for an embedding-space definition by space id.
170#[must_use]
171pub fn emb_space_key(tenant: TenantId, sid: u64) -> Vec<u8> {
172    key(tenant, &[P_EMB_SPACE, &u64be(sid)])
173}
174
175/// Key mapping an embedding-space alias to its space.
176#[must_use]
177pub fn emb_alias_key(tenant: TenantId, alias: &str) -> Vec<u8> {
178    key(tenant, &[P_EMB_ALIAS, alias.as_bytes()])
179}
180
181/// Key for one embedding binding of an owner property element.
182#[must_use]
183pub fn emb_binding_key(
184    tenant: TenantId,
185    space: u64,
186    kind: OwnerKind,
187    owner_ord: u64,
188    pid: u64,
189    elem: u64,
190) -> Vec<u8> {
191    key(
192        tenant,
193        &[
194            P_EMB_BINDING,
195            &u64be(space),
196            &[kind.as_byte()],
197            &u64be(owner_ord),
198            &u64be(pid),
199            &u64be(elem),
200        ],
201    )
202}
203
204/// Prefix covering every embedding binding in a space.
205#[must_use]
206pub fn emb_bindings_prefix(tenant: TenantId, space: u64) -> Vec<u8> {
207    key(tenant, &[P_EMB_BINDING, &u64be(space)])
208}
209
210/// Prefix covering one owner's embedding bindings in a space: every
211/// property element of that record or relationship.
212#[must_use]
213pub fn emb_owner_bindings_prefix(
214    tenant: TenantId,
215    space: u64,
216    kind: OwnerKind,
217    owner_ord: u64,
218) -> Vec<u8> {
219    key(
220        tenant,
221        &[
222            P_EMB_BINDING,
223            &u64be(space),
224            &[kind.as_byte()],
225            &u64be(owner_ord),
226        ],
227    )
228}
229
230/// The `(space, kind, owner, pid, elem)` an [`emb_binding_key`] names, read
231/// from its fixed-width tail (`space / kind / owner / pid / elem`), so a
232/// scan can skip a binding by owner without decoding its value, and the
233/// binding value need not repeat what the key holds.
234#[must_use]
235pub fn parse_emb_binding_key(key: &[u8]) -> Option<(u64, OwnerKind, u64, u64, u64)> {
236    // space (8) '/' kind (1) '/' owner (8) '/' pid (8) '/' elem (8)
237    let kind_at = key.len().checked_sub(28)?;
238    let kind = OwnerKind::from_byte(*key.get(kind_at)?)?;
239    Some((
240        read_u64(key, kind_at.checked_sub(9)?)?,
241        kind,
242        read_u64(key, kind_at + 2)?,
243        read_u64(key, kind_at + 11)?,
244        read_u64(key, kind_at + 20)?,
245    ))
246}
247
248/// Key for a stored embedding vector slot within a space.
249#[must_use]
250pub fn emb_vector_key(tenant: TenantId, space: u64, slot: u64) -> Vec<u8> {
251    key(tenant, &[P_EMB_VECTOR, &u64be(space), &u64be(slot)])
252}
253
254/// Key segment for per-space binding versions.
255pub const P_EMB_VERSION: &[u8] = b"emb-version";
256
257/// Version row of a space's binding set: `prev_ci || ci` (16 bytes), the
258/// commit that last changed the space's bindings and the one before it. A
259/// cached ANN generation is current when its version equals `ci`, and an
260/// incremental absorb of commit `ci` chains onto it when its version equals
261/// `prev_ci` — independent of any other space or tenant's commits.
262#[must_use]
263pub fn emb_space_version_key(tenant: TenantId, space: u64) -> Vec<u8> {
264    key(tenant, &[P_EMB_VERSION, &u64be(space)])
265}
266
267/// Encode a space version row.
268#[must_use]
269pub fn emb_space_version_value(prev_ci: u64, ci: u64) -> Vec<u8> {
270    let mut out = Vec::with_capacity(16);
271    out.extend_from_slice(&u64be(prev_ci));
272    out.extend_from_slice(&u64be(ci));
273    out
274}
275
276/// Decode a space version row; `None` for a malformed value.
277#[must_use]
278pub fn parse_emb_space_version(raw: &[u8]) -> Option<(u64, u64)> {
279    let prev = u64::from_be_bytes(raw.get(..8)?.try_into().ok()?);
280    let ci = u64::from_be_bytes(raw.get(8..16)?.try_into().ok()?);
281    (raw.len() == 16).then_some((prev, ci))
282}
283
284/// Key segment for per-space ready-vector counts.
285pub const P_EMB_READY: &[u8] = b"emb-ready";
286
287/// Number of a space's bindings in `Ready` status, maintained by every
288/// commit that writes a binding (big-endian `u64`). It is the pool a vector
289/// search ranks, read in one `get` to choose exact or ANN search.
290#[must_use]
291pub fn emb_ready_count_key(tenant: TenantId, space: u64) -> Vec<u8> {
292    key(tenant, &[P_EMB_READY, &u64be(space)])
293}
294
295/// Key segment for pending embedding-slot reclamations.
296pub const P_EMB_RECLAIM: &[u8] = b"emb-reclaim";
297
298/// Pending reclamation of a vector slot superseded at commit `after_ci`. The
299/// slot's bytes may still be read by a pinned view until the GC watermark
300/// passes `after_ci`; the reclamation sweep deletes them only then. Ordered by
301/// `after_ci` so the sweep ranges straight to the reclaimable rows.
302#[must_use]
303pub fn emb_reclaim_key(tenant: TenantId, after_ci: u64, space: u64, slot: u64) -> Vec<u8> {
304    key(
305        tenant,
306        &[P_EMB_RECLAIM, &u64be(after_ci), &u64be(space), &u64be(slot)],
307    )
308}
309
310/// Prefix covering every pending embedding-slot reclamation for a tenant.
311#[must_use]
312pub fn emb_reclaim_prefix(tenant: TenantId) -> Vec<u8> {
313    key(tenant, &[P_EMB_RECLAIM])
314}
315
316/// Value of a reclamation row: `after_ci || space || slot` (24 bytes), so the
317/// sweep can recover the vector key to delete without re-parsing the key.
318#[must_use]
319pub fn emb_reclaim_value(after_ci: u64, space: u64, slot: u64) -> Vec<u8> {
320    let mut out = Vec::with_capacity(24);
321    out.extend_from_slice(&u64be(after_ci));
322    out.extend_from_slice(&u64be(space));
323    out.extend_from_slice(&u64be(slot));
324    out
325}
326
327/// Decode a reclamation row value into `(after_ci, space, slot)`.
328#[must_use]
329pub fn parse_emb_reclaim_value(value: &[u8]) -> Option<(u64, u64, u64)> {
330    if value.len() != 24 {
331        return None;
332    }
333    let after_ci = u64::from_be_bytes(value[0..8].try_into().ok()?);
334    let space = u64::from_be_bytes(value[8..16].try_into().ok()?);
335    let slot = u64::from_be_bytes(value[16..24].try_into().ok()?);
336    Some((after_ci, space, slot))
337}
338
339/// Legacy raw global generation key. Retained only for old-command replay and
340/// migration detection; new writes use `security_generation_key(tenant)`.
341pub const SECURITY_GENERATION_KEY: &str = "security/generation";
342
343/// Legacy raw global policy key; new writes use `security_policy_key(tenant)`.
344pub const SECURITY_POLICY_ACTIVE_KEY: &str = "security/policy/active";
345
346/// Prefix covering all security rows of a tenant.
347#[must_use]
348pub fn security_prefix(tenant: TenantId) -> Vec<u8> {
349    key(tenant, &[b"security", b""])
350}
351
352/// Key for a tenant's security generation watermark.
353#[must_use]
354pub fn security_generation_key(tenant: TenantId) -> Vec<u8> {
355    key(tenant, &[b"security", b"generation"])
356}
357
358/// Key for a tenant's active security policy.
359#[must_use]
360pub fn security_policy_key(tenant: TenantId) -> Vec<u8> {
361    key(tenant, &[b"security", b"policy", b"active"])
362}
363
364// ---- storage format v3 typed families -------------------------
365//
366// Typed keys are `[TENANT_NS][tenant u32 BE][family u8][fields…]`. Family tags
367// are below `/` (0x2F), so they never collide with the `/`-separated generic
368// keys built by [`key`]. Integers are fixed-width big-endian; a variable-length
369// string that is not the last field is `u16 BE length + bytes`.
370
371/// Typed family: public record id → record ordinal.
372pub const F_RECORD_ID: u8 = 0x03;
373/// Typed family: record ordinal → `(public id, label)` header.
374pub const F_RECORD_HDR: u8 = 0x04;
375/// Typed family: `(label, record ordinal)` label membership.
376pub const F_LABEL: u8 = 0x05;
377/// Typed family: derived counters (see the `C_*` sub-families).
378pub const F_COUNTER: u8 = 0x09;
379/// Typed family: per-`(kind, label, pid)` value dictionary with refcounts.
380pub const F_VALUE_DICT: u8 = 0x0A;
381/// Typed family: sticky array markers.
382pub const F_MARKER: u8 = 0x11;
383/// Typed family: one canonical occurrence per `(kind, owner, pid, elem)`.
384pub const F_OCC: u8 = 0x12;
385/// Typed family: `(kind, label, pid, nullness, owner)` presence.
386pub const F_PRESENCE: u8 = 0x13;
387/// Typed family: index id → [`crate::index::IndexDef`] (JSON).
388pub const F_INDEX_DEF: u8 = 0x14;
389/// Typed family: index name → index id.
390pub const F_INDEX_NAME: u8 = 0x15;
391/// Typed family (derived): `(index id, tag, payload, owner)` ordered entry.
392pub const F_INDEX_ENTRY: u8 = 0x16;
393/// Typed family: `(text index id, owner)` → commit index of the newest
394/// change the text applier has not yet indexed (base rows).
395pub const F_INDEX_PENDING: u8 = 0x17;
396/// Typed family: the tenant's logical stored bytes (key + value lengths of
397/// every live row under the tenant prefix except this one), maintained by the
398/// storage backend in the batch that changes them.
399pub const F_USAGE_BYTES: u8 = 0x18;
400
401/// Coarse family name of any store key, for accounting and diagnostics:
402/// tenant families by their family byte, legacy segment keys by their first
403/// segment, and system/global keys by namespace. One classifier serves the
404/// layout and read-accounting tools.
405#[must_use]
406pub fn family_label(key: &[u8]) -> String {
407    match key.first() {
408        Some(&TENANT_NS) if key.len() > TENANT_PREFIX_LEN => {
409            let rest = &key[TENANT_PREFIX_LEN..];
410            match rest[0] {
411                F_RECORD_ID => "record-id".into(),
412                F_RECORD_HDR => "record-header".into(),
413                F_LABEL => "label-member".into(),
414                F_COUNTER => format!("counter/{:02x}", rest.get(1).copied().unwrap_or(0)),
415                F_VALUE_DICT => "value-dictionary".into(),
416                F_MARKER => "marker".into(),
417                F_OCC => "occurrence".into(),
418                F_PRESENCE => "presence".into(),
419                F_INDEX_DEF => "index-def".into(),
420                F_INDEX_NAME => "index-name".into(),
421                F_INDEX_ENTRY => "index-entry".into(),
422                F_INDEX_PENDING => "index-pending".into(),
423                F_USAGE_BYTES => "usage-bytes".into(),
424                b'/' => {
425                    let segment = rest[1..].split(|byte| *byte == b'/').next().unwrap_or(&[]);
426                    format!("legacy/{}", String::from_utf8_lossy(segment))
427                }
428                other => format!("tenant-family/{other:02x}"),
429            }
430        }
431        Some(&SYSTEM_NS) => format!("system/{:02x}", key.get(1).copied().unwrap_or(0)),
432        _ => {
433            let segment = key.split(|byte| *byte == b'/').next().unwrap_or(&[]);
434            format!("global/{}", String::from_utf8_lossy(segment))
435        }
436    }
437}
438
439/// Human-readable rendering of a storage key for errors and logs: the
440/// tenant, the typed family name, and the remainder in hex (legacy
441/// `/`-separated keys keep their printable path).
442#[must_use]
443pub fn describe_key(key: &[u8]) -> String {
444    use std::fmt::Write as _;
445    let Some(tenant) = parse_tenant(key) else {
446        return key.escape_ascii().to_string();
447    };
448    let rest = &key[TENANT_PREFIX_LEN..];
449    let family = match rest.first() {
450        Some(&F_RECORD_ID) => "record-id",
451        Some(&F_RECORD_HDR) => "record-header",
452        Some(&F_LABEL) => "label-member",
453        Some(&F_COUNTER) => "counter",
454        Some(&F_VALUE_DICT) => "value-dictionary",
455        Some(&F_MARKER) => "marker",
456        Some(&F_OCC) => "occurrence",
457        Some(&F_PRESENCE) => "presence",
458        Some(&F_INDEX_DEF) => "index-def",
459        Some(&F_INDEX_NAME) => "index-name",
460        Some(&F_INDEX_ENTRY) => "index-entry",
461        Some(&F_INDEX_PENDING) => "index-pending",
462        Some(&F_USAGE_BYTES) => "usage-bytes",
463        _ => return format!("tenant {tenant} {}", rest.escape_ascii()),
464    };
465    let mut out = format!("tenant {tenant} {family} ");
466    for byte in &rest[1..] {
467        let _ = write!(out, "{byte:02x}");
468    }
469    out
470}
471
472/// Counter: live records per label.
473pub const C_LABEL: u8 = 0x01;
474/// Counter: owners carrying a pid (all labels).
475pub const C_SUPPORT_TOTAL: u8 = 0x02;
476/// Counter: owners carrying a pid, by `(kind, pid, label)`.
477pub const C_SUPPORT_BY_PID: u8 = 0x03;
478/// Counter: owners carrying a pid, by `(kind, label, pid)`.
479pub const C_SUPPORT_BY_LABEL: u8 = 0x04;
480/// Counter: explicit-null occurrences of a pid.
481pub const C_NULL: u8 = 0x05;
482/// Counter: adjacency degree of a node in one direction.
483pub const C_DEGREE: u8 = 0x06;
484/// Counter: distinct dictionary values of `(kind, label, pid)`.
485pub const C_DISTINCT: u8 = 0x07;
486/// Counter: relationships by `(type, source label, target label)`.
487pub const C_REL_CATALOG: u8 = 0x08;
488/// Counter: occurrences of `(kind, label, pid)` too long for the dictionary.
489pub const C_LONG_VALUES: u8 = 0x09;
490/// Counter: relationships of `(type, source label, target label)` carrying a
491/// pid.
492pub const C_REL_SHAPE_PROP: u8 = 0x0A;
493/// Counter: tenant-wide usage totals, one row per `U_*` metric.
494pub const C_USAGE: u8 = 0x0B;
495/// Counter: occurrences of one long value of `(kind, label, pid)`, keyed by
496/// its digest (`long_value_digest`): the value dictionary's refcount for
497/// values too long for it.
498pub const C_LONG_VALUE: u8 = 0x0C;
499/// Counter: distinct long values of `(kind, label, pid)` (non-zero
500/// `C_LONG_VALUE` rows), as `C_DISTINCT` is for the dictionary.
501pub const C_LONG_DISTINCT: u8 = 0x0D;
502
503/// Usage metric: live records.
504pub const U_RECORDS: u8 = 0x01;
505/// Usage metric: live relationships.
506pub const U_RELATIONSHIPS: u8 = 0x02;
507/// Usage metric: labels with at least one live record.
508pub const U_LABELS: u8 = 0x03;
509/// Usage metric: property ids carried by at least one live record.
510pub const U_PROPERTIES: u8 = 0x04;
511
512/// Marker: array-holding owners of a pid under a label (a count).
513pub const M_ARRAY_LABEL: u8 = 0x02;
514
515/// Presence nullness: the owner has at least one non-null value for the pid.
516pub const NULLNESS_VALUE: u8 = 0;
517/// Presence nullness: every value the owner has for the pid is null.
518pub const NULLNESS_NULL: u8 = 1;
519
520/// Longest encoded `(tag + payload)` value that enters the value dictionary
521/// (DC10 refinement): longer values are counted in [`C_LONG_VALUES`] instead.
522pub const DICT_MAX_VALUE_BYTES: usize = 96;
523
524fn typed(tenant: TenantId, family: u8, capacity: usize) -> Vec<u8> {
525    let mut out = Vec::with_capacity(TENANT_PREFIX_LEN + 1 + capacity);
526    out.extend_from_slice(&tenant_prefix(tenant));
527    out.push(family);
528    out
529}
530
531/// Append a `u16 BE length + bytes` string field.
532///
533/// # Panics
534/// Panics if the string is longer than `u16::MAX` bytes (labels and
535/// relationship types are bounded far below that at the protocol edge).
536pub fn push_str(out: &mut Vec<u8>, value: &str) {
537    let len = u16::try_from(value.len()).expect("key string field fits u16");
538    out.extend_from_slice(&len.to_be_bytes());
539    out.extend_from_slice(value.as_bytes());
540}
541
542/// Read a `u16 BE length + bytes` string field at `pos`; returns the string
543/// and the position after it.
544#[must_use]
545pub fn read_str(key: &[u8], pos: usize) -> Option<(&str, usize)> {
546    let len = usize::from(u16::from_be_bytes(key.get(pos..pos + 2)?.try_into().ok()?));
547    let start = pos + 2;
548    let text = std::str::from_utf8(key.get(start..start + len)?).ok()?;
549    Some((text, start + len))
550}
551
552/// Read a big-endian `u64` at `pos`.
553#[must_use]
554pub fn read_u64(key: &[u8], pos: usize) -> Option<u64> {
555    Some(u64::from_be_bytes(key.get(pos..pos + 8)?.try_into().ok()?))
556}
557
558/// Append `value` as an order-preserving variable-length integer: one length
559/// byte (0–8), then the value's big-endian bytes without leading zeros
560/// (`0` is the lone byte `0x00`). A shorter encoding is a smaller number, so
561/// byte order equals numeric order, and the length byte makes the field
562/// self-delimiting, so a key prefix ending in it never matches a longer value
563/// (lever C: owner ordinals and property ids are mostly 1–4 bytes).
564pub fn push_ord(out: &mut Vec<u8>, value: u64) {
565    let bytes = value.to_be_bytes();
566    let skip = usize::try_from(value.leading_zeros() / 8).unwrap_or(8);
567    out.push(u8::try_from(8 - skip).unwrap_or(8));
568    out.extend_from_slice(&bytes[skip..]);
569}
570
571/// Read an integer written by [`push_ord`] at `pos`; returns the value and
572/// the position after it. Non-minimal encodings (a leading zero byte, or a
573/// length over 8) are rejected so every value has exactly one key.
574#[must_use]
575pub fn read_ord(key: &[u8], pos: usize) -> Option<(u64, usize)> {
576    let len = usize::from(*key.get(pos)?);
577    if len > 8 {
578        return None;
579    }
580    let digits = key.get(pos + 1..pos + 1 + len)?;
581    if digits.first() == Some(&0) {
582        return None;
583    }
584    let mut buf = [0u8; 8];
585    buf[8 - len..].copy_from_slice(digits);
586    Some((u64::from_be_bytes(buf), pos + 1 + len))
587}
588
589/// Read a [`push_ord`] integer that must end `key` exactly at `pos`.
590#[must_use]
591pub fn read_ord_tail(key: &[u8], pos: usize) -> Option<u64> {
592    let (value, end) = read_ord(key, pos)?;
593    (end == key.len()).then_some(value)
594}
595
596/// `RECORD_ID`: public id → ordinal.
597#[must_use]
598pub fn record_id_key(tenant: TenantId, id: &[u8; 16]) -> Vec<u8> {
599    let mut out = record_id_prefix(tenant);
600    out.extend_from_slice(id);
601    out
602}
603
604/// Prefix of every record identity row of a tenant.
605#[must_use]
606pub fn record_id_prefix(tenant: TenantId) -> Vec<u8> {
607    typed(tenant, F_RECORD_ID, 16)
608}
609
610/// `RECORD_HDR`: ordinal → header.
611#[must_use]
612pub fn record_hdr_key(tenant: TenantId, ordinal: u64) -> Vec<u8> {
613    let mut out = typed(tenant, F_RECORD_HDR, 9);
614    push_ord(&mut out, ordinal);
615    out
616}
617
618/// The record ordinal of a `RECORD_HDR` key.
619#[must_use]
620pub fn parse_record_hdr_key(key: &[u8]) -> Option<u64> {
621    if key.get(TENANT_PREFIX_LEN) != Some(&F_RECORD_HDR) {
622        return None;
623    }
624    read_ord_tail(key, TENANT_PREFIX_LEN + 1)
625}
626
627/// Prefix of every record header of a tenant.
628#[must_use]
629pub fn record_hdr_prefix(tenant: TenantId) -> Vec<u8> {
630    typed(tenant, F_RECORD_HDR, 0)
631}
632
633/// Encode a record header value: `id (16) || label`.
634#[must_use]
635pub fn encode_record_hdr(id: &[u8; 16], label: &str) -> Vec<u8> {
636    let mut out = Vec::with_capacity(16 + label.len());
637    out.extend_from_slice(id);
638    out.extend_from_slice(label.as_bytes());
639    out
640}
641
642/// Decode a record header value into `(id, label)`.
643#[must_use]
644pub fn decode_record_hdr(raw: &[u8]) -> Option<([u8; 16], &str)> {
645    let id: [u8; 16] = raw.get(..16)?.try_into().ok()?;
646    Some((id, std::str::from_utf8(raw.get(16..)?).ok()?))
647}
648
649/// `LABEL`: membership row of a record ordinal in a label.
650#[must_use]
651pub fn label_member_key(tenant: TenantId, label: &str, ordinal: u64) -> Vec<u8> {
652    let mut out = label_member_prefix(tenant, label);
653    push_ord(&mut out, ordinal);
654    out
655}
656
657/// Prefix of every member of one label.
658#[must_use]
659pub fn label_member_prefix(tenant: TenantId, label: &str) -> Vec<u8> {
660    let mut out = typed(tenant, F_LABEL, 2 + label.len() + 9);
661    push_str(&mut out, label);
662    out
663}
664
665/// Prefix of every label-membership row of a tenant.
666#[must_use]
667pub fn label_members_prefix(tenant: TenantId) -> Vec<u8> {
668    typed(tenant, F_LABEL, 0)
669}
670
671/// Parse a membership key into `(label, ordinal)`.
672#[must_use]
673pub fn parse_label_member_key(key: &[u8]) -> Option<(&str, u64)> {
674    if key.get(TENANT_PREFIX_LEN) != Some(&F_LABEL) {
675        return None;
676    }
677    let (label, pos) = read_str(key, TENANT_PREFIX_LEN + 1)?;
678    Some((label, read_ord_tail(key, pos)?))
679}
680
681/// `OCC`: the canonical occurrence of one `(kind, owner, pid, elem)`.
682#[must_use]
683pub fn occ_key(tenant: TenantId, kind: OwnerKind, owner: u64, pid: u64, elem: u64) -> Vec<u8> {
684    let mut out = occ_pid_prefix(tenant, kind, owner, pid);
685    push_ord(&mut out, elem);
686    out
687}
688
689/// Prefix of every occurrence of one owner.
690#[must_use]
691pub fn occ_owner_prefix(tenant: TenantId, kind: OwnerKind, owner: u64) -> Vec<u8> {
692    let mut out = typed(tenant, F_OCC, 1 + 9 + 9 + 9);
693    out.push(kind.as_byte());
694    push_ord(&mut out, owner);
695    out
696}
697
698/// Prefix of every element of one owner's pid.
699#[must_use]
700pub fn occ_pid_prefix(tenant: TenantId, kind: OwnerKind, owner: u64, pid: u64) -> Vec<u8> {
701    let mut out = occ_owner_prefix(tenant, kind, owner);
702    push_ord(&mut out, pid);
703    out
704}
705
706/// Prefix of every occurrence of one owner kind in a tenant.
707#[must_use]
708pub fn occ_kind_prefix(tenant: TenantId, kind: OwnerKind) -> Vec<u8> {
709    let mut out = typed(tenant, F_OCC, 1);
710    out.push(kind.as_byte());
711    out
712}
713
714/// Parse an occurrence key into `(tenant, kind, owner, pid, elem)`.
715#[must_use]
716pub fn parse_occ_key(key: &[u8]) -> Option<(TenantId, OwnerKind, u64, u64, u64)> {
717    let tenant = parse_tenant(key)?;
718    if key.get(TENANT_PREFIX_LEN) != Some(&F_OCC) {
719        return None;
720    }
721    let kind = OwnerKind::from_byte(*key.get(TENANT_PREFIX_LEN + 1)?)?;
722    let (owner, pos) = read_ord(key, TENANT_PREFIX_LEN + 2)?;
723    let (pid, pos) = read_ord(key, pos)?;
724    let elem = read_ord_tail(key, pos)?;
725    Some((tenant, kind, owner, pid, elem))
726}
727
728/// `PRESENCE`: an owner carries `pid` (with the given nullness) under a label.
729#[must_use]
730pub fn presence_key(
731    tenant: TenantId,
732    kind: OwnerKind,
733    label: &str,
734    pid: u64,
735    nullness: u8,
736    owner: u64,
737) -> Vec<u8> {
738    let mut out = presence_nullness_prefix(tenant, kind, label, pid, nullness);
739    push_ord(&mut out, owner);
740    out
741}
742
743/// Prefix of the presence rows of `(kind, label, pid)` with one nullness.
744#[must_use]
745pub fn presence_nullness_prefix(
746    tenant: TenantId,
747    kind: OwnerKind,
748    label: &str,
749    pid: u64,
750    nullness: u8,
751) -> Vec<u8> {
752    let mut out = presence_pid_prefix(tenant, kind, label, pid);
753    out.push(nullness);
754    out
755}
756
757/// Prefix of every presence row of `(kind, label, pid)`.
758#[must_use]
759pub fn presence_pid_prefix(tenant: TenantId, kind: OwnerKind, label: &str, pid: u64) -> Vec<u8> {
760    let mut out = typed(tenant, F_PRESENCE, 1 + 2 + label.len() + 9 + 1 + 9);
761    out.push(kind.as_byte());
762    push_str(&mut out, label);
763    push_ord(&mut out, pid);
764    out
765}
766
767/// Parse a presence key into `(kind, label, pid, nullness, owner)`.
768#[must_use]
769pub fn parse_presence_key(key: &[u8]) -> Option<(OwnerKind, &str, u64, u8, u64)> {
770    if key.get(TENANT_PREFIX_LEN) != Some(&F_PRESENCE) {
771        return None;
772    }
773    let kind = OwnerKind::from_byte(*key.get(TENANT_PREFIX_LEN + 1)?)?;
774    let (label, pos) = read_str(key, TENANT_PREFIX_LEN + 2)?;
775    let (pid, pos) = read_ord(key, pos)?;
776    let nullness = *key.get(pos)?;
777    let owner = read_ord_tail(key, pos + 1)?;
778    Some((kind, label, pid, nullness, owner))
779}
780
781/// `VALUE_DICT`: refcount of one `(tag || payload)` value of `(kind, label, pid)`.
782#[must_use]
783pub fn value_dict_key(
784    tenant: TenantId,
785    kind: OwnerKind,
786    label: &str,
787    pid: u64,
788    value: &[u8],
789) -> Vec<u8> {
790    let mut out = value_dict_prefix(tenant, kind, label, pid);
791    out.extend_from_slice(value);
792    out
793}
794
795/// Prefix of the value dictionary of `(kind, label, pid)`; the remainder of a
796/// key under it is the `(tag || order-preserving payload)` value.
797#[must_use]
798pub fn value_dict_prefix(tenant: TenantId, kind: OwnerKind, label: &str, pid: u64) -> Vec<u8> {
799    let mut out = typed(
800        tenant,
801        F_VALUE_DICT,
802        1 + 2 + label.len() + 9 + DICT_MAX_VALUE_BYTES,
803    );
804    out.push(kind.as_byte());
805    push_str(&mut out, label);
806    push_ord(&mut out, pid);
807    out
808}
809
810fn counter(tenant: TenantId, sub: u8, capacity: usize) -> Vec<u8> {
811    let mut out = typed(tenant, F_COUNTER, 1 + capacity);
812    out.push(sub);
813    out
814}
815
816/// Prefix of one counter sub-family.
817#[must_use]
818pub fn counter_prefix(tenant: TenantId, sub: u8) -> Vec<u8> {
819    counter(tenant, sub, 0)
820}
821
822/// Counter: one tenant-wide usage metric (`U_*`).
823#[must_use]
824pub fn usage_count_key(tenant: TenantId, metric: u8) -> Vec<u8> {
825    let mut out = counter(tenant, C_USAGE, 1);
826    out.push(metric);
827    out
828}
829
830/// The tenant's logical stored bytes row (see [`F_USAGE_BYTES`]).
831#[must_use]
832pub fn usage_bytes_key(tenant: TenantId) -> Vec<u8> {
833    typed(tenant, F_USAGE_BYTES, 0)
834}
835
836/// Counter: live records of a label.
837#[must_use]
838pub fn label_count_key(tenant: TenantId, label: &str) -> Vec<u8> {
839    let mut out = counter(tenant, C_LABEL, 2 + label.len());
840    push_str(&mut out, label);
841    out
842}
843
844/// Counter: owners of `kind` carrying `pid`.
845#[must_use]
846pub fn support_total_key(tenant: TenantId, kind: OwnerKind, pid: u64) -> Vec<u8> {
847    let mut out = counter(tenant, C_SUPPORT_TOTAL, 9);
848    out.push(kind.as_byte());
849    out.extend_from_slice(&u64be(pid));
850    out
851}
852
853/// Counter: owners of `kind` under `label` carrying `pid` (pid-major order).
854#[must_use]
855pub fn support_by_pid_key(tenant: TenantId, kind: OwnerKind, pid: u64, label: &str) -> Vec<u8> {
856    let mut out = support_by_pid_prefix(tenant, kind, pid);
857    push_str(&mut out, label);
858    out
859}
860
861/// Prefix of the per-label support counters of one pid.
862#[must_use]
863pub fn support_by_pid_prefix(tenant: TenantId, kind: OwnerKind, pid: u64) -> Vec<u8> {
864    let mut out = counter(tenant, C_SUPPORT_BY_PID, 9 + 2 + 32);
865    out.push(kind.as_byte());
866    out.extend_from_slice(&u64be(pid));
867    out
868}
869
870/// Counter: owners of `kind` under `label` carrying `pid` (label-major order).
871#[must_use]
872pub fn support_by_label_key(tenant: TenantId, kind: OwnerKind, label: &str, pid: u64) -> Vec<u8> {
873    let mut out = support_by_label_prefix(tenant, kind, label);
874    out.extend_from_slice(&u64be(pid));
875    out
876}
877
878/// Prefix of the per-pid support counters of one label.
879#[must_use]
880pub fn support_by_label_prefix(tenant: TenantId, kind: OwnerKind, label: &str) -> Vec<u8> {
881    let mut out = counter(tenant, C_SUPPORT_BY_LABEL, 1 + 2 + label.len() + 8);
882    out.push(kind.as_byte());
883    push_str(&mut out, label);
884    out
885}
886
887/// Counter: explicit-null occurrences of `(kind, pid)`.
888#[must_use]
889pub fn null_count_key(tenant: TenantId, kind: OwnerKind, pid: u64) -> Vec<u8> {
890    let mut out = counter(tenant, C_NULL, 9);
891    out.push(kind.as_byte());
892    out.extend_from_slice(&u64be(pid));
893    out
894}
895
896/// Counter: degree of `node` in one direction (`true` = outgoing).
897#[must_use]
898pub fn degree_key(tenant: TenantId, node: u64, outgoing: bool) -> Vec<u8> {
899    let mut out = counter(tenant, C_DEGREE, 9);
900    out.extend_from_slice(&u64be(node));
901    out.push(u8::from(outgoing));
902    out
903}
904
905/// Counter: distinct dictionary values of `(kind, label, pid)`.
906#[must_use]
907pub fn distinct_key(tenant: TenantId, kind: OwnerKind, label: &str, pid: u64) -> Vec<u8> {
908    let mut out = counter(tenant, C_DISTINCT, 1 + 2 + label.len() + 8);
909    out.push(kind.as_byte());
910    push_str(&mut out, label);
911    out.extend_from_slice(&u64be(pid));
912    out
913}
914
915/// Counter: occurrences of `(kind, label, pid)` too long for the dictionary.
916#[must_use]
917pub fn long_values_key(tenant: TenantId, kind: OwnerKind, label: &str, pid: u64) -> Vec<u8> {
918    let mut out = counter(tenant, C_LONG_VALUES, 1 + 2 + label.len() + 8);
919    out.push(kind.as_byte());
920    push_str(&mut out, label);
921    out.extend_from_slice(&u64be(pid));
922    out
923}
924
925/// Bytes of a long value's digest in `long_value_key`.
926pub const LONG_DIGEST_BYTES: usize = 16;
927
928/// Digest of a long dictionary entry (`tag || payload`): the first
929/// `LONG_DIGEST_BYTES` of its SHA-256.
930#[must_use]
931pub fn long_value_digest(entry: &[u8]) -> [u8; LONG_DIGEST_BYTES] {
932    use sha2::Digest as _;
933    let full: [u8; 32] = sha2::Sha256::digest(entry).into();
934    let mut out = [0u8; LONG_DIGEST_BYTES];
935    out.copy_from_slice(&full[..LONG_DIGEST_BYTES]);
936    out
937}
938
939/// Prefix of the long-value refcounts of `(kind, label, pid)`; the remainder
940/// of a key under it is a `long_value_digest`.
941#[must_use]
942pub fn long_value_prefix(tenant: TenantId, kind: OwnerKind, label: &str, pid: u64) -> Vec<u8> {
943    let mut out = counter(
944        tenant,
945        C_LONG_VALUE,
946        1 + 2 + label.len() + 8 + LONG_DIGEST_BYTES,
947    );
948    out.push(kind.as_byte());
949    push_str(&mut out, label);
950    out.extend_from_slice(&u64be(pid));
951    out
952}
953
954/// Counter: occurrences of the long value with `digest` of `(kind, label, pid)`.
955#[must_use]
956pub fn long_value_key(
957    tenant: TenantId,
958    kind: OwnerKind,
959    label: &str,
960    pid: u64,
961    digest: &[u8; LONG_DIGEST_BYTES],
962) -> Vec<u8> {
963    let mut out = long_value_prefix(tenant, kind, label, pid);
964    out.extend_from_slice(digest);
965    out
966}
967
968/// Counter: distinct long values of `(kind, label, pid)`.
969#[must_use]
970pub fn long_distinct_key(tenant: TenantId, kind: OwnerKind, label: &str, pid: u64) -> Vec<u8> {
971    let mut out = counter(tenant, C_LONG_DISTINCT, 1 + 2 + label.len() + 8);
972    out.push(kind.as_byte());
973    push_str(&mut out, label);
974    out.extend_from_slice(&u64be(pid));
975    out
976}
977
978/// Counter: relationships of `(type, source label, target label)`.
979#[must_use]
980pub fn rel_catalog_key(tenant: TenantId, rel_type: &str, source: &str, target: &str) -> Vec<u8> {
981    let mut out = counter(
982        tenant,
983        C_REL_CATALOG,
984        6 + rel_type.len() + source.len() + target.len(),
985    );
986    push_str(&mut out, rel_type);
987    push_str(&mut out, source);
988    push_str(&mut out, target);
989    out
990}
991
992/// Counter: relationships of `(type, source label, target label)` that carry
993/// `pid` (creation-time endpoint labels, like [`rel_catalog_key`]).
994#[must_use]
995pub fn rel_shape_prop_key(
996    tenant: TenantId,
997    (rel_type, source, target): (&str, &str, &str),
998    pid: u64,
999) -> Vec<u8> {
1000    let mut out = rel_shape_prop_prefix(tenant, rel_type, source, target);
1001    out.extend_from_slice(&u64be(pid));
1002    out
1003}
1004
1005/// Prefix of the per-pid counters of one relationship shape.
1006#[must_use]
1007pub fn rel_shape_prop_prefix(
1008    tenant: TenantId,
1009    rel_type: &str,
1010    source: &str,
1011    target: &str,
1012) -> Vec<u8> {
1013    let mut out = counter(
1014        tenant,
1015        C_REL_SHAPE_PROP,
1016        6 + rel_type.len() + source.len() + target.len() + 8,
1017    );
1018    push_str(&mut out, rel_type);
1019    push_str(&mut out, source);
1020    push_str(&mut out, target);
1021    out
1022}
1023
1024/// Marker: owners of `kind` under `label` holding `pid` as an array; the
1025/// value is their count (the row is absent at zero).
1026#[must_use]
1027pub fn array_label_marker_key(tenant: TenantId, kind: OwnerKind, pid: u64, label: &str) -> Vec<u8> {
1028    let mut out = array_label_marker_prefix(tenant, kind, pid);
1029    push_str(&mut out, label);
1030    out
1031}
1032
1033/// Prefix of the per-label array markers of one pid.
1034#[must_use]
1035pub fn array_label_marker_prefix(tenant: TenantId, kind: OwnerKind, pid: u64) -> Vec<u8> {
1036    let mut out = typed(tenant, F_MARKER, 10 + 2 + 32);
1037    out.push(M_ARRAY_LABEL);
1038    out.push(kind.as_byte());
1039    out.extend_from_slice(&u64be(pid));
1040    out
1041}
1042
1043/// Decode a big-endian `u64` counter value (absent → 0).
1044///
1045/// # Errors
1046/// Returns `None` from the outer option for a malformed value.
1047#[must_use]
1048pub fn decode_counter(raw: Option<&[u8]>) -> Option<u64> {
1049    match raw {
1050        None => Some(0),
1051        Some(raw) => raw.try_into().ok().map(u64::from_be_bytes),
1052    }
1053}
1054
1055// ---- declared indexes ----------------------------------------------
1056
1057/// Key of an index definition.
1058#[must_use]
1059pub fn index_def_key(tenant: TenantId, id: u32) -> Vec<u8> {
1060    let mut out = typed(tenant, F_INDEX_DEF, 4);
1061    out.extend_from_slice(&id.to_be_bytes());
1062    out
1063}
1064
1065/// Prefix of every index definition of a tenant.
1066#[must_use]
1067pub fn index_def_prefix(tenant: TenantId) -> Vec<u8> {
1068    typed(tenant, F_INDEX_DEF, 0)
1069}
1070
1071/// Key mapping an index name to its id.
1072#[must_use]
1073pub fn index_name_key(tenant: TenantId, name: &str) -> Vec<u8> {
1074    let mut out = typed(tenant, F_INDEX_NAME, name.len());
1075    out.extend_from_slice(name.as_bytes());
1076    out
1077}
1078
1079/// Prefix of one index's entries.
1080#[must_use]
1081pub fn index_entry_prefix(tenant: TenantId, id: u32) -> Vec<u8> {
1082    let mut out = typed(tenant, F_INDEX_ENTRY, 4 + 16 + 8);
1083    out.extend_from_slice(&id.to_be_bytes());
1084    out
1085}
1086
1087/// Key of one ordered entry: `entry` is `tag ‖ order-preserving payload`.
1088#[must_use]
1089pub fn index_entry_key(tenant: TenantId, id: u32, entry: &[u8], owner: u64) -> Vec<u8> {
1090    let mut out = index_entry_prefix(tenant, id);
1091    out.extend_from_slice(entry);
1092    out.extend_from_slice(&u64be(owner));
1093    out
1094}
1095
1096/// `(entry, owner)` of an index entry key under `prefix`
1097/// ([`index_entry_prefix`]).
1098#[must_use]
1099pub fn parse_index_entry_key<'a>(key: &'a [u8], prefix: &[u8]) -> Option<(&'a [u8], u64)> {
1100    let rest = key.strip_prefix(prefix)?;
1101    let split = rest.len().checked_sub(8)?;
1102    let (entry, owner) = rest.split_at(split);
1103    Some((entry, u64::from_be_bytes(owner.try_into().ok()?)))
1104}
1105
1106/// Prefix of one index's pending rows (owners its applier has yet to
1107/// process: text and structural indexes).
1108#[must_use]
1109pub fn index_pending_prefix(tenant: TenantId, index: u32) -> Vec<u8> {
1110    let mut out = typed(tenant, F_INDEX_PENDING, 4 + 8);
1111    out.extend_from_slice(&index.to_be_bytes());
1112    out
1113}
1114
1115/// Pending row of one owner of an applied index.
1116#[must_use]
1117pub fn index_pending_key(tenant: TenantId, index: u32, owner: u64) -> Vec<u8> {
1118    let mut out = index_pending_prefix(tenant, index);
1119    out.extend_from_slice(&u64be(owner));
1120    out
1121}
1122
1123#[cfg(test)]
1124mod ord_tests {
1125    use super::*;
1126
1127    fn edge_values() -> Vec<u64> {
1128        let mut values = vec![0, 1, u64::MAX, u64::MAX - 1];
1129        for shift in (8..64).step_by(8) {
1130            let boundary = 1u64 << shift;
1131            values.extend([boundary - 1, boundary, boundary + 1]);
1132        }
1133        values.extend((0..2_000u64).map(|i| i.wrapping_mul(0x9e37_79b9_7f4a_7c15) >> (i % 64)));
1134        values
1135    }
1136
1137    fn encode(value: u64) -> Vec<u8> {
1138        let mut out = Vec::new();
1139        push_ord(&mut out, value);
1140        out
1141    }
1142
1143    #[test]
1144    fn ord_round_trips_with_minimal_length() {
1145        for value in edge_values() {
1146            let bytes = encode(value);
1147            assert_eq!(read_ord_tail(&bytes, 0), Some(value), "{value}");
1148            let digits = 8 - usize::try_from(value.leading_zeros() / 8).unwrap();
1149            assert_eq!(bytes.len(), 1 + digits, "{value}");
1150        }
1151        assert_eq!(encode(0), vec![0]);
1152        assert_eq!(encode(200_000), vec![3, 0x03, 0x0d, 0x40]);
1153    }
1154
1155    #[test]
1156    fn ord_byte_order_is_numeric_order() {
1157        let mut values = edge_values();
1158        values.sort_unstable();
1159        values.dedup();
1160        for pair in values.windows(2) {
1161            assert!(
1162                encode(pair[0]) < encode(pair[1]),
1163                "{} vs {}",
1164                pair[0],
1165                pair[1]
1166            );
1167        }
1168    }
1169
1170    #[test]
1171    fn non_minimal_or_oversized_encodings_are_rejected() {
1172        assert_eq!(read_ord(&[1, 0], 0), None);
1173        assert_eq!(read_ord(&[2, 0, 5], 0), None);
1174        assert_eq!(read_ord(&[9, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0), None);
1175        assert_eq!(read_ord(&[3, 1, 2], 0), None);
1176        assert_eq!(read_ord_tail(&[1, 5, 0], 0), None);
1177    }
1178
1179    #[test]
1180    fn one_owner_prefix_never_matches_another_owners_keys() {
1181        let tenant = TenantId(7);
1182        for (a, b) in [(1u64, 257u64), (0, 1), (255, 65_535), (1, 256)] {
1183            let prefix = occ_owner_prefix(tenant, OwnerKind::Record, a);
1184            let other = occ_key(tenant, OwnerKind::Record, b, 3, 0);
1185            assert!(!other.starts_with(&prefix), "{a} prefix matched owner {b}");
1186            let own = occ_key(tenant, OwnerKind::Record, a, 3, 0);
1187            assert_eq!(
1188                parse_occ_key(&own),
1189                Some((tenant, OwnerKind::Record, a, 3, 0))
1190            );
1191        }
1192        let presence = presence_key(tenant, OwnerKind::Record, "User", 42, NULLNESS_VALUE, 300);
1193        assert_eq!(
1194            parse_presence_key(&presence),
1195            Some((OwnerKind::Record, "User", 42, NULLNESS_VALUE, 300))
1196        );
1197        let member = label_member_key(tenant, "User", 70_000);
1198        assert_eq!(parse_label_member_key(&member), Some(("User", 70_000)));
1199        assert_eq!(parse_record_hdr_key(&record_hdr_key(tenant, 9)), Some(9));
1200    }
1201
1202    #[test]
1203    fn an_owner_binding_prefix_covers_only_that_owners_bindings() {
1204        let tenant = TenantId(7);
1205        for (kind, owner) in [(OwnerKind::Record, 1u64), (OwnerKind::Relationship, 257)] {
1206            let prefix = emb_owner_bindings_prefix(tenant, 3, kind, owner);
1207            let own = emb_binding_key(tenant, 3, kind, owner, 9, 2);
1208            assert!(own.starts_with(&prefix));
1209            assert!(own.starts_with(&emb_bindings_prefix(tenant, 3)));
1210            assert_eq!(parse_emb_binding_key(&own), Some((3, kind, owner, 9, 2)));
1211            for other in [
1212                emb_binding_key(tenant, 3, kind, owner + 1, 9, 2),
1213                emb_binding_key(tenant, 4, kind, owner, 9, 2),
1214                emb_binding_key(tenant, 3, OwnerKind::Record, owner, 9, 2),
1215            ] {
1216                if other != own {
1217                    assert!(!other.starts_with(&prefix));
1218                }
1219            }
1220        }
1221    }
1222}