use crate::ids::{OwnerKind, TenantId};
use std::fmt::Write as _;
#[must_use]
pub fn tenant_hex(tenant: TenantId) -> String {
let mut out = String::with_capacity(8);
for byte in tenant.to_be_bytes() {
let _ = write!(out, "{byte:02x}");
}
out
}
pub const SYSTEM_NS: u8 = 0x00;
pub const TENANT_NS: u8 = 0x01;
pub const TENANT_PREFIX_LEN: usize = 5;
pub const SYS_TENANT_BY_SCOPE: u8 = 0x02;
pub const SYS_TENANT_BY_ORD: u8 = 0x03;
pub const SYS_TENANT_SEQ: u8 = 0x04;
#[must_use]
pub fn tenant_prefix(tenant: TenantId) -> [u8; TENANT_PREFIX_LEN] {
let [a, b, c, d] = tenant.to_be_bytes();
[TENANT_NS, a, b, c, d]
}
#[must_use]
pub fn parse_tenant(key: &[u8]) -> Option<TenantId> {
if key.first() != Some(&TENANT_NS) {
return None;
}
let bytes: [u8; 4] = key.get(1..TENANT_PREFIX_LEN)?.try_into().ok()?;
Some(TenantId(u32::from_be_bytes(bytes)))
}
#[must_use]
pub fn tenant_by_scope_key(scope: &crate::ids::TenantScope) -> Vec<u8> {
let mut out = Vec::with_capacity(34);
out.extend_from_slice(&[SYSTEM_NS, SYS_TENANT_BY_SCOPE]);
out.extend_from_slice(&scope.0);
out
}
#[must_use]
pub fn tenant_by_ord_key(tenant: TenantId) -> Vec<u8> {
let mut out = Vec::with_capacity(6);
out.extend_from_slice(&[SYSTEM_NS, SYS_TENANT_BY_ORD]);
out.extend_from_slice(&tenant.to_be_bytes());
out
}
#[must_use]
pub fn tenant_by_ord_prefix() -> [u8; 2] {
[SYSTEM_NS, SYS_TENANT_BY_ORD]
}
#[must_use]
pub fn tenant_seq_key() -> [u8; 2] {
[SYSTEM_NS, SYS_TENANT_SEQ]
}
#[must_use]
pub fn vector_index_meta_prefix(tenant: TenantId) -> String {
format!("vector/index/{}/", tenant_hex(tenant))
}
#[must_use]
pub fn ann_manifest_meta_prefix(tenant: TenantId) -> String {
format!("vector/ann-manifest/{}/", tenant_hex(tenant))
}
pub const P_META: &[u8] = b"meta";
pub const P_REL: &[u8] = b"relationship";
pub const P_REL_BY_ORD: &[u8] = b"relationship-ordinal";
pub const P_REL_GROUP: &[u8] = b"rel-group";
pub const P_FAMILY_SIG: &[u8] = b"property-family-signature";
pub const P_FAMILY: &[u8] = b"property-family";
pub const P_DEFINITION: &[u8] = b"property-definition";
#[must_use]
pub fn key(tenant: TenantId, segments: &[&[u8]]) -> Vec<u8> {
let mut out =
Vec::with_capacity(TENANT_PREFIX_LEN + segments.iter().map(|s| s.len() + 1).sum::<usize>());
out.extend_from_slice(&tenant_prefix(tenant));
for s in segments {
out.push(b'/');
out.extend_from_slice(s);
}
out
}
#[must_use]
pub fn prefix_range(prefix: &[u8]) -> (Vec<u8>, Vec<u8>) {
let mut end = prefix.to_vec();
loop {
match end.last_mut() {
Some(last) if *last != 0xFF => {
*last += 1;
return (prefix.to_vec(), end);
}
Some(_) => {
end.pop();
}
None => return (prefix.to_vec(), vec![]),
}
}
}
#[must_use]
pub fn u64be(v: u64) -> [u8; 8] {
v.to_be_bytes()
}
#[must_use]
pub fn owner_prefix(kind: OwnerKind) -> &'static [u8] {
match kind {
OwnerKind::Record => b"record",
OwnerKind::Relationship => b"rel",
}
}
pub const P_EMB_SPACE: &[u8] = b"emb-space";
pub const P_EMB_ALIAS: &[u8] = b"emb-alias";
pub const P_EMB_BINDING: &[u8] = b"emb-binding";
pub const P_EMB_VECTOR: &[u8] = b"vec";
#[must_use]
pub fn emb_space_key(tenant: TenantId, sid: u64) -> Vec<u8> {
key(tenant, &[P_EMB_SPACE, &u64be(sid)])
}
#[must_use]
pub fn emb_alias_key(tenant: TenantId, alias: &str) -> Vec<u8> {
key(tenant, &[P_EMB_ALIAS, alias.as_bytes()])
}
#[must_use]
pub fn emb_binding_key(
tenant: TenantId,
space: u64,
kind: OwnerKind,
owner_ord: u64,
pid: u64,
elem: u64,
) -> Vec<u8> {
key(
tenant,
&[
P_EMB_BINDING,
&u64be(space),
&[kind.as_byte()],
&u64be(owner_ord),
&u64be(pid),
&u64be(elem),
],
)
}
#[must_use]
pub fn emb_bindings_prefix(tenant: TenantId, space: u64) -> Vec<u8> {
key(tenant, &[P_EMB_BINDING, &u64be(space)])
}
#[must_use]
pub fn emb_vector_key(tenant: TenantId, space: u64, slot: u64) -> Vec<u8> {
key(tenant, &[P_EMB_VECTOR, &u64be(space), &u64be(slot)])
}
pub const P_EMB_VERSION: &[u8] = b"emb-version";
#[must_use]
pub fn emb_space_version_key(tenant: TenantId, space: u64) -> Vec<u8> {
key(tenant, &[P_EMB_VERSION, &u64be(space)])
}
#[must_use]
pub fn emb_space_version_value(prev_ci: u64, ci: u64) -> Vec<u8> {
let mut out = Vec::with_capacity(16);
out.extend_from_slice(&u64be(prev_ci));
out.extend_from_slice(&u64be(ci));
out
}
#[must_use]
pub fn parse_emb_space_version(raw: &[u8]) -> Option<(u64, u64)> {
let prev = u64::from_be_bytes(raw.get(..8)?.try_into().ok()?);
let ci = u64::from_be_bytes(raw.get(8..16)?.try_into().ok()?);
(raw.len() == 16).then_some((prev, ci))
}
pub const P_EMB_READY: &[u8] = b"emb-ready";
#[must_use]
pub fn emb_ready_count_key(tenant: TenantId, space: u64) -> Vec<u8> {
key(tenant, &[P_EMB_READY, &u64be(space)])
}
pub const P_EMB_RECLAIM: &[u8] = b"emb-reclaim";
#[must_use]
pub fn emb_reclaim_key(tenant: TenantId, after_ci: u64, space: u64, slot: u64) -> Vec<u8> {
key(
tenant,
&[P_EMB_RECLAIM, &u64be(after_ci), &u64be(space), &u64be(slot)],
)
}
#[must_use]
pub fn emb_reclaim_prefix(tenant: TenantId) -> Vec<u8> {
key(tenant, &[P_EMB_RECLAIM])
}
#[must_use]
pub fn emb_reclaim_value(after_ci: u64, space: u64, slot: u64) -> Vec<u8> {
let mut out = Vec::with_capacity(24);
out.extend_from_slice(&u64be(after_ci));
out.extend_from_slice(&u64be(space));
out.extend_from_slice(&u64be(slot));
out
}
#[must_use]
pub fn parse_emb_reclaim_value(value: &[u8]) -> Option<(u64, u64, u64)> {
if value.len() != 24 {
return None;
}
let after_ci = u64::from_be_bytes(value[0..8].try_into().ok()?);
let space = u64::from_be_bytes(value[8..16].try_into().ok()?);
let slot = u64::from_be_bytes(value[16..24].try_into().ok()?);
Some((after_ci, space, slot))
}
pub const SECURITY_GENERATION_KEY: &str = "security/generation";
pub const SECURITY_POLICY_ACTIVE_KEY: &str = "security/policy/active";
#[must_use]
pub fn security_prefix(tenant: TenantId) -> Vec<u8> {
key(tenant, &[b"security", b""])
}
#[must_use]
pub fn security_generation_key(tenant: TenantId) -> Vec<u8> {
key(tenant, &[b"security", b"generation"])
}
#[must_use]
pub fn security_policy_key(tenant: TenantId) -> Vec<u8> {
key(tenant, &[b"security", b"policy", b"active"])
}
pub const F_RECORD_ID: u8 = 0x03;
pub const F_RECORD_HDR: u8 = 0x04;
pub const F_LABEL: u8 = 0x05;
pub const F_COUNTER: u8 = 0x09;
pub const F_VALUE_DICT: u8 = 0x0A;
pub const F_MARKER: u8 = 0x11;
pub const F_OCC: u8 = 0x12;
pub const F_PRESENCE: u8 = 0x13;
pub const F_INDEX_DEF: u8 = 0x14;
pub const F_INDEX_NAME: u8 = 0x15;
pub const F_INDEX_ENTRY: u8 = 0x16;
pub const F_INDEX_PENDING: u8 = 0x17;
pub const F_USAGE_BYTES: u8 = 0x18;
#[must_use]
pub fn family_label(key: &[u8]) -> String {
match key.first() {
Some(&TENANT_NS) if key.len() > TENANT_PREFIX_LEN => {
let rest = &key[TENANT_PREFIX_LEN..];
match rest[0] {
F_RECORD_ID => "record-id".into(),
F_RECORD_HDR => "record-header".into(),
F_LABEL => "label-member".into(),
F_COUNTER => format!("counter/{:02x}", rest.get(1).copied().unwrap_or(0)),
F_VALUE_DICT => "value-dictionary".into(),
F_MARKER => "marker".into(),
F_OCC => "occurrence".into(),
F_PRESENCE => "presence".into(),
F_INDEX_DEF => "index-def".into(),
F_INDEX_NAME => "index-name".into(),
F_INDEX_ENTRY => "index-entry".into(),
F_INDEX_PENDING => "index-pending".into(),
F_USAGE_BYTES => "usage-bytes".into(),
b'/' => {
let segment = rest[1..].split(|byte| *byte == b'/').next().unwrap_or(&[]);
format!("legacy/{}", String::from_utf8_lossy(segment))
}
other => format!("tenant-family/{other:02x}"),
}
}
Some(&SYSTEM_NS) => format!("system/{:02x}", key.get(1).copied().unwrap_or(0)),
_ => {
let segment = key.split(|byte| *byte == b'/').next().unwrap_or(&[]);
format!("global/{}", String::from_utf8_lossy(segment))
}
}
}
#[must_use]
pub fn describe_key(key: &[u8]) -> String {
use std::fmt::Write as _;
let Some(tenant) = parse_tenant(key) else {
return key.escape_ascii().to_string();
};
let rest = &key[TENANT_PREFIX_LEN..];
let family = match rest.first() {
Some(&F_RECORD_ID) => "record-id",
Some(&F_RECORD_HDR) => "record-header",
Some(&F_LABEL) => "label-member",
Some(&F_COUNTER) => "counter",
Some(&F_VALUE_DICT) => "value-dictionary",
Some(&F_MARKER) => "marker",
Some(&F_OCC) => "occurrence",
Some(&F_PRESENCE) => "presence",
Some(&F_INDEX_DEF) => "index-def",
Some(&F_INDEX_NAME) => "index-name",
Some(&F_INDEX_ENTRY) => "index-entry",
Some(&F_INDEX_PENDING) => "index-pending",
Some(&F_USAGE_BYTES) => "usage-bytes",
_ => return format!("tenant {tenant} {}", rest.escape_ascii()),
};
let mut out = format!("tenant {tenant} {family} ");
for byte in &rest[1..] {
let _ = write!(out, "{byte:02x}");
}
out
}
pub const C_LABEL: u8 = 0x01;
pub const C_SUPPORT_TOTAL: u8 = 0x02;
pub const C_SUPPORT_BY_PID: u8 = 0x03;
pub const C_SUPPORT_BY_LABEL: u8 = 0x04;
pub const C_NULL: u8 = 0x05;
pub const C_DEGREE: u8 = 0x06;
pub const C_DISTINCT: u8 = 0x07;
pub const C_REL_CATALOG: u8 = 0x08;
pub const C_LONG_VALUES: u8 = 0x09;
pub const C_REL_SHAPE_PROP: u8 = 0x0A;
pub const C_USAGE: u8 = 0x0B;
pub const C_LONG_VALUE: u8 = 0x0C;
pub const C_LONG_DISTINCT: u8 = 0x0D;
pub const U_RECORDS: u8 = 0x01;
pub const U_RELATIONSHIPS: u8 = 0x02;
pub const U_LABELS: u8 = 0x03;
pub const U_PROPERTIES: u8 = 0x04;
pub const M_ARRAY_LABEL: u8 = 0x02;
pub const NULLNESS_VALUE: u8 = 0;
pub const NULLNESS_NULL: u8 = 1;
pub const DICT_MAX_VALUE_BYTES: usize = 96;
fn typed(tenant: TenantId, family: u8, capacity: usize) -> Vec<u8> {
let mut out = Vec::with_capacity(TENANT_PREFIX_LEN + 1 + capacity);
out.extend_from_slice(&tenant_prefix(tenant));
out.push(family);
out
}
pub fn push_str(out: &mut Vec<u8>, value: &str) {
let len = u16::try_from(value.len()).expect("key string field fits u16");
out.extend_from_slice(&len.to_be_bytes());
out.extend_from_slice(value.as_bytes());
}
#[must_use]
pub fn read_str(key: &[u8], pos: usize) -> Option<(&str, usize)> {
let len = usize::from(u16::from_be_bytes(key.get(pos..pos + 2)?.try_into().ok()?));
let start = pos + 2;
let text = std::str::from_utf8(key.get(start..start + len)?).ok()?;
Some((text, start + len))
}
#[must_use]
pub fn read_u64(key: &[u8], pos: usize) -> Option<u64> {
Some(u64::from_be_bytes(key.get(pos..pos + 8)?.try_into().ok()?))
}
pub fn push_ord(out: &mut Vec<u8>, value: u64) {
let bytes = value.to_be_bytes();
let skip = usize::try_from(value.leading_zeros() / 8).unwrap_or(8);
out.push(u8::try_from(8 - skip).unwrap_or(8));
out.extend_from_slice(&bytes[skip..]);
}
#[must_use]
pub fn read_ord(key: &[u8], pos: usize) -> Option<(u64, usize)> {
let len = usize::from(*key.get(pos)?);
if len > 8 {
return None;
}
let digits = key.get(pos + 1..pos + 1 + len)?;
if digits.first() == Some(&0) {
return None;
}
let mut buf = [0u8; 8];
buf[8 - len..].copy_from_slice(digits);
Some((u64::from_be_bytes(buf), pos + 1 + len))
}
#[must_use]
pub fn read_ord_tail(key: &[u8], pos: usize) -> Option<u64> {
let (value, end) = read_ord(key, pos)?;
(end == key.len()).then_some(value)
}
#[must_use]
pub fn record_id_key(tenant: TenantId, id: &[u8; 16]) -> Vec<u8> {
let mut out = record_id_prefix(tenant);
out.extend_from_slice(id);
out
}
#[must_use]
pub fn record_id_prefix(tenant: TenantId) -> Vec<u8> {
typed(tenant, F_RECORD_ID, 16)
}
#[must_use]
pub fn record_hdr_key(tenant: TenantId, ordinal: u64) -> Vec<u8> {
let mut out = typed(tenant, F_RECORD_HDR, 9);
push_ord(&mut out, ordinal);
out
}
#[must_use]
pub fn parse_record_hdr_key(key: &[u8]) -> Option<u64> {
if key.get(TENANT_PREFIX_LEN) != Some(&F_RECORD_HDR) {
return None;
}
read_ord_tail(key, TENANT_PREFIX_LEN + 1)
}
#[must_use]
pub fn record_hdr_prefix(tenant: TenantId) -> Vec<u8> {
typed(tenant, F_RECORD_HDR, 0)
}
#[must_use]
pub fn encode_record_hdr(id: &[u8; 16], label: &str) -> Vec<u8> {
let mut out = Vec::with_capacity(16 + label.len());
out.extend_from_slice(id);
out.extend_from_slice(label.as_bytes());
out
}
#[must_use]
pub fn decode_record_hdr(raw: &[u8]) -> Option<([u8; 16], &str)> {
let id: [u8; 16] = raw.get(..16)?.try_into().ok()?;
Some((id, std::str::from_utf8(raw.get(16..)?).ok()?))
}
#[must_use]
pub fn label_member_key(tenant: TenantId, label: &str, ordinal: u64) -> Vec<u8> {
let mut out = label_member_prefix(tenant, label);
push_ord(&mut out, ordinal);
out
}
#[must_use]
pub fn label_member_prefix(tenant: TenantId, label: &str) -> Vec<u8> {
let mut out = typed(tenant, F_LABEL, 2 + label.len() + 9);
push_str(&mut out, label);
out
}
#[must_use]
pub fn label_members_prefix(tenant: TenantId) -> Vec<u8> {
typed(tenant, F_LABEL, 0)
}
#[must_use]
pub fn parse_label_member_key(key: &[u8]) -> Option<(&str, u64)> {
if key.get(TENANT_PREFIX_LEN) != Some(&F_LABEL) {
return None;
}
let (label, pos) = read_str(key, TENANT_PREFIX_LEN + 1)?;
Some((label, read_ord_tail(key, pos)?))
}
#[must_use]
pub fn occ_key(tenant: TenantId, kind: OwnerKind, owner: u64, pid: u64, elem: u64) -> Vec<u8> {
let mut out = occ_pid_prefix(tenant, kind, owner, pid);
push_ord(&mut out, elem);
out
}
#[must_use]
pub fn occ_owner_prefix(tenant: TenantId, kind: OwnerKind, owner: u64) -> Vec<u8> {
let mut out = typed(tenant, F_OCC, 1 + 9 + 9 + 9);
out.push(kind.as_byte());
push_ord(&mut out, owner);
out
}
#[must_use]
pub fn occ_pid_prefix(tenant: TenantId, kind: OwnerKind, owner: u64, pid: u64) -> Vec<u8> {
let mut out = occ_owner_prefix(tenant, kind, owner);
push_ord(&mut out, pid);
out
}
#[must_use]
pub fn occ_kind_prefix(tenant: TenantId, kind: OwnerKind) -> Vec<u8> {
let mut out = typed(tenant, F_OCC, 1);
out.push(kind.as_byte());
out
}
#[must_use]
pub fn parse_occ_key(key: &[u8]) -> Option<(TenantId, OwnerKind, u64, u64, u64)> {
let tenant = parse_tenant(key)?;
if key.get(TENANT_PREFIX_LEN) != Some(&F_OCC) {
return None;
}
let kind = OwnerKind::from_byte(*key.get(TENANT_PREFIX_LEN + 1)?)?;
let (owner, pos) = read_ord(key, TENANT_PREFIX_LEN + 2)?;
let (pid, pos) = read_ord(key, pos)?;
let elem = read_ord_tail(key, pos)?;
Some((tenant, kind, owner, pid, elem))
}
#[must_use]
pub fn presence_key(
tenant: TenantId,
kind: OwnerKind,
label: &str,
pid: u64,
nullness: u8,
owner: u64,
) -> Vec<u8> {
let mut out = presence_nullness_prefix(tenant, kind, label, pid, nullness);
push_ord(&mut out, owner);
out
}
#[must_use]
pub fn presence_nullness_prefix(
tenant: TenantId,
kind: OwnerKind,
label: &str,
pid: u64,
nullness: u8,
) -> Vec<u8> {
let mut out = presence_pid_prefix(tenant, kind, label, pid);
out.push(nullness);
out
}
#[must_use]
pub fn presence_pid_prefix(tenant: TenantId, kind: OwnerKind, label: &str, pid: u64) -> Vec<u8> {
let mut out = typed(tenant, F_PRESENCE, 1 + 2 + label.len() + 9 + 1 + 9);
out.push(kind.as_byte());
push_str(&mut out, label);
push_ord(&mut out, pid);
out
}
#[must_use]
pub fn parse_presence_key(key: &[u8]) -> Option<(OwnerKind, &str, u64, u8, u64)> {
if key.get(TENANT_PREFIX_LEN) != Some(&F_PRESENCE) {
return None;
}
let kind = OwnerKind::from_byte(*key.get(TENANT_PREFIX_LEN + 1)?)?;
let (label, pos) = read_str(key, TENANT_PREFIX_LEN + 2)?;
let (pid, pos) = read_ord(key, pos)?;
let nullness = *key.get(pos)?;
let owner = read_ord_tail(key, pos + 1)?;
Some((kind, label, pid, nullness, owner))
}
#[must_use]
pub fn value_dict_key(
tenant: TenantId,
kind: OwnerKind,
label: &str,
pid: u64,
value: &[u8],
) -> Vec<u8> {
let mut out = value_dict_prefix(tenant, kind, label, pid);
out.extend_from_slice(value);
out
}
#[must_use]
pub fn value_dict_prefix(tenant: TenantId, kind: OwnerKind, label: &str, pid: u64) -> Vec<u8> {
let mut out = typed(
tenant,
F_VALUE_DICT,
1 + 2 + label.len() + 9 + DICT_MAX_VALUE_BYTES,
);
out.push(kind.as_byte());
push_str(&mut out, label);
push_ord(&mut out, pid);
out
}
fn counter(tenant: TenantId, sub: u8, capacity: usize) -> Vec<u8> {
let mut out = typed(tenant, F_COUNTER, 1 + capacity);
out.push(sub);
out
}
#[must_use]
pub fn counter_prefix(tenant: TenantId, sub: u8) -> Vec<u8> {
counter(tenant, sub, 0)
}
#[must_use]
pub fn usage_count_key(tenant: TenantId, metric: u8) -> Vec<u8> {
let mut out = counter(tenant, C_USAGE, 1);
out.push(metric);
out
}
#[must_use]
pub fn usage_bytes_key(tenant: TenantId) -> Vec<u8> {
typed(tenant, F_USAGE_BYTES, 0)
}
#[must_use]
pub fn label_count_key(tenant: TenantId, label: &str) -> Vec<u8> {
let mut out = counter(tenant, C_LABEL, 2 + label.len());
push_str(&mut out, label);
out
}
#[must_use]
pub fn support_total_key(tenant: TenantId, kind: OwnerKind, pid: u64) -> Vec<u8> {
let mut out = counter(tenant, C_SUPPORT_TOTAL, 9);
out.push(kind.as_byte());
out.extend_from_slice(&u64be(pid));
out
}
#[must_use]
pub fn support_by_pid_key(tenant: TenantId, kind: OwnerKind, pid: u64, label: &str) -> Vec<u8> {
let mut out = support_by_pid_prefix(tenant, kind, pid);
push_str(&mut out, label);
out
}
#[must_use]
pub fn support_by_pid_prefix(tenant: TenantId, kind: OwnerKind, pid: u64) -> Vec<u8> {
let mut out = counter(tenant, C_SUPPORT_BY_PID, 9 + 2 + 32);
out.push(kind.as_byte());
out.extend_from_slice(&u64be(pid));
out
}
#[must_use]
pub fn support_by_label_key(tenant: TenantId, kind: OwnerKind, label: &str, pid: u64) -> Vec<u8> {
let mut out = support_by_label_prefix(tenant, kind, label);
out.extend_from_slice(&u64be(pid));
out
}
#[must_use]
pub fn support_by_label_prefix(tenant: TenantId, kind: OwnerKind, label: &str) -> Vec<u8> {
let mut out = counter(tenant, C_SUPPORT_BY_LABEL, 1 + 2 + label.len() + 8);
out.push(kind.as_byte());
push_str(&mut out, label);
out
}
#[must_use]
pub fn null_count_key(tenant: TenantId, kind: OwnerKind, pid: u64) -> Vec<u8> {
let mut out = counter(tenant, C_NULL, 9);
out.push(kind.as_byte());
out.extend_from_slice(&u64be(pid));
out
}
#[must_use]
pub fn degree_key(tenant: TenantId, node: u64, outgoing: bool) -> Vec<u8> {
let mut out = counter(tenant, C_DEGREE, 9);
out.extend_from_slice(&u64be(node));
out.push(u8::from(outgoing));
out
}
#[must_use]
pub fn distinct_key(tenant: TenantId, kind: OwnerKind, label: &str, pid: u64) -> Vec<u8> {
let mut out = counter(tenant, C_DISTINCT, 1 + 2 + label.len() + 8);
out.push(kind.as_byte());
push_str(&mut out, label);
out.extend_from_slice(&u64be(pid));
out
}
#[must_use]
pub fn long_values_key(tenant: TenantId, kind: OwnerKind, label: &str, pid: u64) -> Vec<u8> {
let mut out = counter(tenant, C_LONG_VALUES, 1 + 2 + label.len() + 8);
out.push(kind.as_byte());
push_str(&mut out, label);
out.extend_from_slice(&u64be(pid));
out
}
pub const LONG_DIGEST_BYTES: usize = 16;
#[must_use]
pub fn long_value_digest(entry: &[u8]) -> [u8; LONG_DIGEST_BYTES] {
use sha2::Digest as _;
let full: [u8; 32] = sha2::Sha256::digest(entry).into();
let mut out = [0u8; LONG_DIGEST_BYTES];
out.copy_from_slice(&full[..LONG_DIGEST_BYTES]);
out
}
#[must_use]
pub fn long_value_prefix(tenant: TenantId, kind: OwnerKind, label: &str, pid: u64) -> Vec<u8> {
let mut out = counter(
tenant,
C_LONG_VALUE,
1 + 2 + label.len() + 8 + LONG_DIGEST_BYTES,
);
out.push(kind.as_byte());
push_str(&mut out, label);
out.extend_from_slice(&u64be(pid));
out
}
#[must_use]
pub fn long_value_key(
tenant: TenantId,
kind: OwnerKind,
label: &str,
pid: u64,
digest: &[u8; LONG_DIGEST_BYTES],
) -> Vec<u8> {
let mut out = long_value_prefix(tenant, kind, label, pid);
out.extend_from_slice(digest);
out
}
#[must_use]
pub fn long_distinct_key(tenant: TenantId, kind: OwnerKind, label: &str, pid: u64) -> Vec<u8> {
let mut out = counter(tenant, C_LONG_DISTINCT, 1 + 2 + label.len() + 8);
out.push(kind.as_byte());
push_str(&mut out, label);
out.extend_from_slice(&u64be(pid));
out
}
#[must_use]
pub fn rel_catalog_key(tenant: TenantId, rel_type: &str, source: &str, target: &str) -> Vec<u8> {
let mut out = counter(
tenant,
C_REL_CATALOG,
6 + rel_type.len() + source.len() + target.len(),
);
push_str(&mut out, rel_type);
push_str(&mut out, source);
push_str(&mut out, target);
out
}
#[must_use]
pub fn rel_shape_prop_key(
tenant: TenantId,
(rel_type, source, target): (&str, &str, &str),
pid: u64,
) -> Vec<u8> {
let mut out = rel_shape_prop_prefix(tenant, rel_type, source, target);
out.extend_from_slice(&u64be(pid));
out
}
#[must_use]
pub fn rel_shape_prop_prefix(
tenant: TenantId,
rel_type: &str,
source: &str,
target: &str,
) -> Vec<u8> {
let mut out = counter(
tenant,
C_REL_SHAPE_PROP,
6 + rel_type.len() + source.len() + target.len() + 8,
);
push_str(&mut out, rel_type);
push_str(&mut out, source);
push_str(&mut out, target);
out
}
#[must_use]
pub fn array_label_marker_key(tenant: TenantId, kind: OwnerKind, pid: u64, label: &str) -> Vec<u8> {
let mut out = array_label_marker_prefix(tenant, kind, pid);
push_str(&mut out, label);
out
}
#[must_use]
pub fn array_label_marker_prefix(tenant: TenantId, kind: OwnerKind, pid: u64) -> Vec<u8> {
let mut out = typed(tenant, F_MARKER, 10 + 2 + 32);
out.push(M_ARRAY_LABEL);
out.push(kind.as_byte());
out.extend_from_slice(&u64be(pid));
out
}
#[must_use]
pub fn decode_counter(raw: Option<&[u8]>) -> Option<u64> {
match raw {
None => Some(0),
Some(raw) => raw.try_into().ok().map(u64::from_be_bytes),
}
}
#[must_use]
pub fn index_def_key(tenant: TenantId, id: u32) -> Vec<u8> {
let mut out = typed(tenant, F_INDEX_DEF, 4);
out.extend_from_slice(&id.to_be_bytes());
out
}
#[must_use]
pub fn index_def_prefix(tenant: TenantId) -> Vec<u8> {
typed(tenant, F_INDEX_DEF, 0)
}
#[must_use]
pub fn index_name_key(tenant: TenantId, name: &str) -> Vec<u8> {
let mut out = typed(tenant, F_INDEX_NAME, name.len());
out.extend_from_slice(name.as_bytes());
out
}
#[must_use]
pub fn index_entry_prefix(tenant: TenantId, id: u32) -> Vec<u8> {
let mut out = typed(tenant, F_INDEX_ENTRY, 4 + 16 + 8);
out.extend_from_slice(&id.to_be_bytes());
out
}
#[must_use]
pub fn index_entry_key(tenant: TenantId, id: u32, entry: &[u8], owner: u64) -> Vec<u8> {
let mut out = index_entry_prefix(tenant, id);
out.extend_from_slice(entry);
out.extend_from_slice(&u64be(owner));
out
}
#[must_use]
pub fn parse_index_entry_key<'a>(key: &'a [u8], prefix: &[u8]) -> Option<(&'a [u8], u64)> {
let rest = key.strip_prefix(prefix)?;
let split = rest.len().checked_sub(8)?;
let (entry, owner) = rest.split_at(split);
Some((entry, u64::from_be_bytes(owner.try_into().ok()?)))
}
#[must_use]
pub fn index_pending_prefix(tenant: TenantId, index: u32) -> Vec<u8> {
let mut out = typed(tenant, F_INDEX_PENDING, 4 + 8);
out.extend_from_slice(&index.to_be_bytes());
out
}
#[must_use]
pub fn index_pending_key(tenant: TenantId, index: u32, owner: u64) -> Vec<u8> {
let mut out = index_pending_prefix(tenant, index);
out.extend_from_slice(&u64be(owner));
out
}
#[cfg(test)]
mod ord_tests {
use super::*;
fn edge_values() -> Vec<u64> {
let mut values = vec![0, 1, u64::MAX, u64::MAX - 1];
for shift in (8..64).step_by(8) {
let boundary = 1u64 << shift;
values.extend([boundary - 1, boundary, boundary + 1]);
}
values.extend((0..2_000u64).map(|i| i.wrapping_mul(0x9e37_79b9_7f4a_7c15) >> (i % 64)));
values
}
fn encode(value: u64) -> Vec<u8> {
let mut out = Vec::new();
push_ord(&mut out, value);
out
}
#[test]
fn ord_round_trips_with_minimal_length() {
for value in edge_values() {
let bytes = encode(value);
assert_eq!(read_ord_tail(&bytes, 0), Some(value), "{value}");
let digits = 8 - usize::try_from(value.leading_zeros() / 8).unwrap();
assert_eq!(bytes.len(), 1 + digits, "{value}");
}
assert_eq!(encode(0), vec![0]);
assert_eq!(encode(200_000), vec![3, 0x03, 0x0d, 0x40]);
}
#[test]
fn ord_byte_order_is_numeric_order() {
let mut values = edge_values();
values.sort_unstable();
values.dedup();
for pair in values.windows(2) {
assert!(
encode(pair[0]) < encode(pair[1]),
"{} vs {}",
pair[0],
pair[1]
);
}
}
#[test]
fn non_minimal_or_oversized_encodings_are_rejected() {
assert_eq!(read_ord(&[1, 0], 0), None);
assert_eq!(read_ord(&[2, 0, 5], 0), None);
assert_eq!(read_ord(&[9, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0), None);
assert_eq!(read_ord(&[3, 1, 2], 0), None);
assert_eq!(read_ord_tail(&[1, 5, 0], 0), None);
}
#[test]
fn one_owner_prefix_never_matches_another_owners_keys() {
let tenant = TenantId(7);
for (a, b) in [(1u64, 257u64), (0, 1), (255, 65_535), (1, 256)] {
let prefix = occ_owner_prefix(tenant, OwnerKind::Record, a);
let other = occ_key(tenant, OwnerKind::Record, b, 3, 0);
assert!(!other.starts_with(&prefix), "{a} prefix matched owner {b}");
let own = occ_key(tenant, OwnerKind::Record, a, 3, 0);
assert_eq!(
parse_occ_key(&own),
Some((tenant, OwnerKind::Record, a, 3, 0))
);
}
let presence = presence_key(tenant, OwnerKind::Record, "User", 42, NULLNESS_VALUE, 300);
assert_eq!(
parse_presence_key(&presence),
Some((OwnerKind::Record, "User", 42, NULLNESS_VALUE, 300))
);
let member = label_member_key(tenant, "User", 70_000);
assert_eq!(parse_label_member_key(&member), Some(("User", 70_000)));
assert_eq!(parse_record_hdr_key(&record_hdr_key(tenant, 9)), Some(9));
}
}