1use crate::ids::{OwnerKind, TenantId};
2use std::fmt::Write as _;
3
4#[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
16pub const SYSTEM_NS: u8 = 0x00;
18pub const TENANT_NS: u8 = 0x01;
20pub const TENANT_PREFIX_LEN: usize = 5;
22
23pub const SYS_TENANT_BY_SCOPE: u8 = 0x02;
25pub const SYS_TENANT_BY_ORD: u8 = 0x03;
27pub const SYS_TENANT_SEQ: u8 = 0x04;
29
30#[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#[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#[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#[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#[must_use]
67pub fn tenant_by_ord_prefix() -> [u8; 2] {
68 [SYSTEM_NS, SYS_TENANT_BY_ORD]
69}
70
71#[must_use]
73pub fn tenant_seq_key() -> [u8; 2] {
74 [SYSTEM_NS, SYS_TENANT_SEQ]
75}
76
77#[must_use]
79pub fn vector_index_meta_prefix(tenant: TenantId) -> String {
80 format!("vector/index/{}/", tenant_hex(tenant))
81}
82
83#[must_use]
85pub fn ann_manifest_meta_prefix(tenant: TenantId) -> String {
86 format!("vector/ann-manifest/{}/", tenant_hex(tenant))
87}
88
89pub const P_META: &[u8] = b"meta";
91pub const P_REL: &[u8] = b"relationship";
93pub const P_REL_BY_ORD: &[u8] = b"relationship-ordinal";
95pub const P_REL_GROUP: &[u8] = b"rel-group";
97pub const P_FAMILY_SIG: &[u8] = b"property-family-signature";
99pub const P_FAMILY: &[u8] = b"property-family";
101pub const P_DEFINITION: &[u8] = b"property-definition";
103#[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#[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#[must_use]
147pub fn u64be(v: u64) -> [u8; 8] {
148 v.to_be_bytes()
149}
150
151#[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
160pub const P_EMB_SPACE: &[u8] = b"emb-space";
162pub const P_EMB_ALIAS: &[u8] = b"emb-alias";
164pub const P_EMB_BINDING: &[u8] = b"emb-binding";
166pub const P_EMB_VECTOR: &[u8] = b"vec";
168
169#[must_use]
171pub fn emb_space_key(tenant: TenantId, sid: u64) -> Vec<u8> {
172 key(tenant, &[P_EMB_SPACE, &u64be(sid)])
173}
174
175#[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#[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#[must_use]
206pub fn emb_bindings_prefix(tenant: TenantId, space: u64) -> Vec<u8> {
207 key(tenant, &[P_EMB_BINDING, &u64be(space)])
208}
209
210#[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#[must_use]
235pub fn parse_emb_binding_key(key: &[u8]) -> Option<(u64, OwnerKind, u64, u64, u64)> {
236 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#[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
254pub const P_EMB_VERSION: &[u8] = b"emb-version";
256
257#[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#[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#[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
284pub const P_EMB_READY: &[u8] = b"emb-ready";
286
287#[must_use]
291pub fn emb_ready_count_key(tenant: TenantId, space: u64) -> Vec<u8> {
292 key(tenant, &[P_EMB_READY, &u64be(space)])
293}
294
295pub const P_EMB_RECLAIM: &[u8] = b"emb-reclaim";
297
298#[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#[must_use]
312pub fn emb_reclaim_prefix(tenant: TenantId) -> Vec<u8> {
313 key(tenant, &[P_EMB_RECLAIM])
314}
315
316#[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#[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
339pub const SECURITY_GENERATION_KEY: &str = "security/generation";
342
343pub const SECURITY_POLICY_ACTIVE_KEY: &str = "security/policy/active";
345
346#[must_use]
348pub fn security_prefix(tenant: TenantId) -> Vec<u8> {
349 key(tenant, &[b"security", b""])
350}
351
352#[must_use]
354pub fn security_generation_key(tenant: TenantId) -> Vec<u8> {
355 key(tenant, &[b"security", b"generation"])
356}
357
358#[must_use]
360pub fn security_policy_key(tenant: TenantId) -> Vec<u8> {
361 key(tenant, &[b"security", b"policy", b"active"])
362}
363
364pub const F_RECORD_ID: u8 = 0x03;
373pub const F_RECORD_HDR: u8 = 0x04;
375pub const F_LABEL: u8 = 0x05;
377pub const F_COUNTER: u8 = 0x09;
379pub const F_VALUE_DICT: u8 = 0x0A;
381pub const F_MARKER: u8 = 0x11;
383pub const F_OCC: u8 = 0x12;
385pub const F_PRESENCE: u8 = 0x13;
387pub const F_INDEX_DEF: u8 = 0x14;
389pub const F_INDEX_NAME: u8 = 0x15;
391pub const F_INDEX_ENTRY: u8 = 0x16;
393pub const F_INDEX_PENDING: u8 = 0x17;
396pub const F_USAGE_BYTES: u8 = 0x18;
400
401#[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#[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
472pub const C_LABEL: u8 = 0x01;
474pub const C_SUPPORT_TOTAL: u8 = 0x02;
476pub const C_SUPPORT_BY_PID: u8 = 0x03;
478pub const C_SUPPORT_BY_LABEL: u8 = 0x04;
480pub const C_NULL: u8 = 0x05;
482pub const C_DEGREE: u8 = 0x06;
484pub const C_DISTINCT: u8 = 0x07;
486pub const C_REL_CATALOG: u8 = 0x08;
488pub const C_LONG_VALUES: u8 = 0x09;
490pub const C_REL_SHAPE_PROP: u8 = 0x0A;
493pub const C_USAGE: u8 = 0x0B;
495pub const C_LONG_VALUE: u8 = 0x0C;
499pub const C_LONG_DISTINCT: u8 = 0x0D;
502
503pub const U_RECORDS: u8 = 0x01;
505pub const U_RELATIONSHIPS: u8 = 0x02;
507pub const U_LABELS: u8 = 0x03;
509pub const U_PROPERTIES: u8 = 0x04;
511
512pub const M_ARRAY_LABEL: u8 = 0x02;
514
515pub const NULLNESS_VALUE: u8 = 0;
517pub const NULLNESS_NULL: u8 = 1;
519
520pub 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
531pub 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#[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#[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
558pub 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#[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#[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#[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#[must_use]
606pub fn record_id_prefix(tenant: TenantId) -> Vec<u8> {
607 typed(tenant, F_RECORD_ID, 16)
608}
609
610#[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#[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#[must_use]
629pub fn record_hdr_prefix(tenant: TenantId) -> Vec<u8> {
630 typed(tenant, F_RECORD_HDR, 0)
631}
632
633#[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#[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#[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#[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#[must_use]
667pub fn label_members_prefix(tenant: TenantId) -> Vec<u8> {
668 typed(tenant, F_LABEL, 0)
669}
670
671#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[must_use]
818pub fn counter_prefix(tenant: TenantId, sub: u8) -> Vec<u8> {
819 counter(tenant, sub, 0)
820}
821
822#[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#[must_use]
832pub fn usage_bytes_key(tenant: TenantId) -> Vec<u8> {
833 typed(tenant, F_USAGE_BYTES, 0)
834}
835
836#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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
925pub const LONG_DIGEST_BYTES: usize = 16;
927
928#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[must_use]
1067pub fn index_def_prefix(tenant: TenantId) -> Vec<u8> {
1068 typed(tenant, F_INDEX_DEF, 0)
1069}
1070
1071#[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#[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#[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#[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#[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#[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}