pub const TAG_CATALOG: u8 = 0x00;
pub const TAG_NODE: u8 = 0x01;
pub const TAG_EDGE: u8 = 0x03;
pub const TAG_REDGE: u8 = 0x04;
pub const TAG_VEC: u8 = 0x05;
pub const TAG_LABEL: u8 = 0x02;
pub const TAG_EXT: u8 = 0x06;
pub const TAG_CEDGE: u8 = 0x08;
pub const TAG_CREDGE: u8 = 0x09;
pub const TAG_PROP: u8 = 0x0A;
pub const TAG_VCODE: u8 = 0x0B;
pub const TAG_TEXT: u8 = 0x0C;
pub const TAG_TEXTNORM: u8 = 0x0D;
pub const TAG_TEXTMETA: u8 = 0x0E;
pub const TAG_SPAT: u8 = 0x0F;
pub const TAG_GEOM: u8 = 0x10;
pub const TAG_NAV: u8 = 0x11;
pub const TAG_SQLMETA: u8 = 0x12;
pub const TAG_FIELDIDX: u8 = 0x13;
pub const TAG_SEARCHSLOT: u8 = 0x14;
pub const CAT_KEY_ORDER: u64 = 4;
pub const CAT_FIELD_AGGREGATE: u64 = 5;
pub fn field_aggregate_key(coll: u64, field: u64) -> Vec<u8> {
k(TAG_CATALOG, &[CAT_FIELD_AGGREGATE, coll, field])
}
pub fn is_field_aggregate_key(key: &[u8]) -> bool {
key.len() == 25 && key[0] == TAG_CATALOG
&& key[1..9] == CAT_FIELD_AGGREGATE.to_be_bytes()
}
fn k(tag: u8, parts: &[u64]) -> Vec<u8> {
let mut v = Vec::with_capacity(1 + parts.len() * 8);
v.push(tag);
for p in parts { v.extend_from_slice(&p.to_be_bytes()); }
v
}
pub fn node(id: u64) -> Vec<u8> { k(TAG_NODE, &[id]) }
pub fn edge(ctx: u64, src: u64, ty: u64, dst: u64) -> Vec<u8> {
if ctx == 0 { k(TAG_EDGE, &[src, ty, dst]) } else { k(TAG_CEDGE, &[ctx, src, ty, dst]) }
}
pub fn redge(ctx: u64, dst: u64, ty: u64, src: u64) -> Vec<u8> {
if ctx == 0 { k(TAG_REDGE, &[dst, ty, src]) } else { k(TAG_CREDGE, &[ctx, dst, ty, src]) }
}
pub fn edge_prefix(ctx: u64, src: u64) -> Vec<u8> {
if ctx == 0 { k(TAG_EDGE, &[src]) } else { k(TAG_CEDGE, &[ctx, src]) }
}
pub fn ctx_prefix(ctx: u64) -> Vec<u8> {
if ctx == 0 { vec![TAG_EDGE] } else { k(TAG_CEDGE, &[ctx]) }
}
pub fn vec_key(field: u64, id: u64) -> Vec<u8> { k(TAG_VEC, &[field, id]) }
pub fn vec_prefix(field: u64) -> Vec<u8> { k(TAG_VEC, &[field]) }
pub fn vcode_key(field: u64, id: u64) -> Vec<u8> { k(TAG_VCODE, &[field, id]) }
pub fn vcode_prefix(field: u64) -> Vec<u8> { k(TAG_VCODE, &[field]) }
pub fn nav_prefix(field: u64) -> Vec<u8> { k(TAG_NAV, &[field]) }
pub fn text_head_key(field: u64, term: &[u8], docid: u64) -> Vec<u8> {
let mut v = Vec::with_capacity(1 + 8 + 4 + term.len() + 1 + 8);
v.push(TAG_TEXT);
v.extend_from_slice(&field.to_be_bytes());
v.extend_from_slice(&0u32.to_be_bytes());
v.extend_from_slice(term);
v.push(0x00);
v.extend_from_slice(&docid.to_be_bytes());
v
}
pub fn text_head_doc_key(field: u64, docid: u64) -> Vec<u8> {
text_head_key(field, b"", docid)
}
pub fn text_seg_key(field: u64, seg: u32, term: &[u8]) -> Vec<u8> {
let mut v = Vec::with_capacity(1 + 8 + 4 + term.len());
v.push(TAG_TEXT);
v.extend_from_slice(&field.to_be_bytes());
v.extend_from_slice(&seg.to_be_bytes());
v.extend_from_slice(term);
v
}
pub fn text_seg_block_prefix(field: u64, seg: u32, term: &[u8]) -> Vec<u8> {
let mut v = text_seg_key(field, seg, term);
v.push(0);
v
}
pub fn text_seg_block_key(field: u64, seg: u32, term: &[u8], first_doc: u64) -> Vec<u8> {
let mut v = text_seg_block_prefix(field, seg, term);
v.extend_from_slice(&first_doc.to_be_bytes());
v
}
pub fn text_seg_doc_prefix(field: u64, seg: u32) -> Vec<u8> {
let mut v = text_meta_key(field, seg);
v.push(0);
v
}
pub fn text_seg_doc_key(field: u64, seg: u32, docid: u64) -> Vec<u8> {
let mut v = text_seg_doc_prefix(field, seg);
v.extend_from_slice(&docid.to_be_bytes());
v
}
pub fn text_norm_key(field: u64, docid: u64) -> Vec<u8> { k(TAG_TEXTNORM, &[field, docid]) }
pub fn text_prefix(field: u64) -> Vec<u8> { k(TAG_TEXT, &[field]) }
pub fn text_norm_prefix(field: u64) -> Vec<u8> { k(TAG_TEXTNORM, &[field]) }
pub fn text_meta_prefix(field: u64) -> Vec<u8> { k(TAG_TEXTMETA, &[field]) }
pub fn spat_key(field: u64, level: u8, cell: u64, id: u64) -> Vec<u8> {
let mut v = Vec::with_capacity(1 + 8 + 1 + 8 + 8);
v.push(TAG_SPAT);
v.extend_from_slice(&field.to_be_bytes());
v.push(level);
v.extend_from_slice(&cell.to_be_bytes());
v.extend_from_slice(&id.to_be_bytes());
v
}
pub fn geom_key(field: u64, id: u64) -> Vec<u8> { k(TAG_GEOM, &[field, id]) }
pub fn geom_prefix(field: u64) -> Vec<u8> { k(TAG_GEOM, &[field]) }
pub fn nav_key(field: u64, id: u64) -> Vec<u8> { k(TAG_NAV, &[field, id]) }
pub fn fieldidx_key(coll: u64, field: u64, value: &[u8], id: u64) -> Vec<u8> {
let mut v = Vec::with_capacity(17 + value.len() + 8);
v.push(TAG_FIELDIDX);
v.extend_from_slice(&coll.to_be_bytes());
v.extend_from_slice(&field.to_be_bytes());
v.extend_from_slice(value);
v.extend_from_slice(&id.to_be_bytes());
v
}
pub fn fieldidx_prefix(coll: u64, field: u64) -> Vec<u8> {
let mut v = Vec::with_capacity(17);
v.push(TAG_FIELDIDX);
v.extend_from_slice(&coll.to_be_bytes());
v.extend_from_slice(&field.to_be_bytes());
v
}
pub fn searchslot_key(idx: u64, kind: u8, id: u64) -> Vec<u8> {
let mut v = Vec::with_capacity(18);
v.push(TAG_SEARCHSLOT);
v.extend_from_slice(&idx.to_be_bytes());
v.push(kind);
v.extend_from_slice(&id.to_be_bytes());
v
}
pub fn searchslot_prefix(idx: u64) -> Vec<u8> {
let mut v = Vec::with_capacity(9);
v.push(TAG_SEARCHSLOT);
v.extend_from_slice(&idx.to_be_bytes());
v
}
pub fn sqlmeta_key(kind: u8, hash: u64) -> Vec<u8> {
let mut v = Vec::with_capacity(10);
v.push(TAG_SQLMETA); v.push(kind); v.extend_from_slice(&hash.to_be_bytes()); v
}
pub fn spat_prefix(field: u64, level: u8) -> Vec<u8> {
let mut v = Vec::with_capacity(1 + 8 + 1);
v.push(TAG_SPAT);
v.extend_from_slice(&field.to_be_bytes());
v.push(level);
v
}
pub fn text_meta_key(field: u64, seg: u32) -> Vec<u8> {
let mut v = Vec::with_capacity(1 + 8 + 4);
v.push(TAG_TEXTMETA);
v.extend_from_slice(&field.to_be_bytes());
v.extend_from_slice(&seg.to_be_bytes());
v
}
pub const TEXT_TERM_STATS_SEG: u32 = u32::MAX - 1;
pub const TEXT_FIELD_META_SEG: u32 = u32::MAX;
pub fn text_field_meta_key(field: u64) -> Vec<u8> {
text_meta_key(field, TEXT_FIELD_META_SEG)
}
pub fn text_term_id_key(field: u64, term_id: u64) -> Vec<u8> {
let mut v = text_meta_key(field, TEXT_TERM_STATS_SEG);
v.push(0);
v.extend_from_slice(&term_id.to_be_bytes());
v
}
pub fn text_term_lex_prefix(field: u64) -> Vec<u8> {
let mut v = text_meta_key(field, TEXT_TERM_STATS_SEG);
v.push(1);
v
}
pub fn text_term_lex_key(field: u64, term: &[u8]) -> Vec<u8> {
let mut v = text_term_lex_prefix(field);
v.extend_from_slice(term);
v
}
pub fn label(l: u64, id: u64) -> Vec<u8> { k(TAG_LABEL, &[l, id]) }
pub fn label_prefix(l: u64) -> Vec<u8> { k(TAG_LABEL, &[l]) }
pub fn edge_type_prefix(ctx: u64, src: u64, ty: u64) -> Vec<u8> {
if ctx == 0 { k(TAG_EDGE, &[src, ty]) } else { k(TAG_CEDGE, &[ctx, src, ty]) }
}
pub fn redge_prefix(ctx: u64, dst: u64) -> Vec<u8> {
if ctx == 0 { k(TAG_REDGE, &[dst]) } else { k(TAG_CREDGE, &[ctx, dst]) }
}
pub fn redge_type_prefix(ctx: u64, dst: u64, ty: u64) -> Vec<u8> {
if ctx == 0 { k(TAG_REDGE, &[dst, ty]) } else { k(TAG_CREDGE, &[ctx, dst, ty]) }
}
pub fn extkey(h: u64) -> Vec<u8> { k(TAG_EXT, &[h]) }
pub fn catalog(field: u64) -> Vec<u8> { k(TAG_CATALOG, &[field]) }
pub fn catalog_field(slot: u64, field: u64) -> Vec<u8> { k(TAG_CATALOG, &[slot, field]) }
pub fn catalog_field_prefix(slot: u64) -> Vec<u8> { k(TAG_CATALOG, &[slot]) }
pub fn catalog_field_item(slot: u64, field: u64, item: u64) -> Vec<u8> {
k(TAG_CATALOG, &[slot, field, item])
}
pub fn key_order_prefix(collection: u64, field: u64) -> Vec<u8> {
k(TAG_CATALOG, &[CAT_KEY_ORDER, collection, field])
}
pub fn key_order_key(collection: u64, field: u64, key: &[u8], id: u64) -> Vec<u8> {
let mut out = key_order_prefix(collection, field);
for &byte in key {
if byte == 0 { out.extend_from_slice(&[0, u8::MAX]); }
else { out.push(byte); }
}
out.extend_from_slice(&[0, 0]);
out.extend_from_slice(&id.to_be_bytes());
out
}
pub fn ext_hash(b: &[u8]) -> u64 {
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for x in b { h ^= *x as u64; h = h.wrapping_mul(0x1000_0000_01b3); }
h
}
pub fn prop(prop_id: u64, value: u64, id: u64) -> Vec<u8> { k(TAG_PROP, &[prop_id, value, id]) }
pub fn prop_prefix(prop_id: u64) -> Vec<u8> { k(TAG_PROP, &[prop_id]) }
pub fn prop_value_prefix(prop_id: u64, value: u64) -> Vec<u8> { k(TAG_PROP, &[prop_id, value]) }
pub fn enc_i64(v: i64) -> u64 { (v as u64) ^ (1 << 63) }
pub fn enc_f64(v: f64) -> u64 {
let b = v.to_bits();
if b >> 63 == 1 { !b } else { b ^ (1 << 63) }
}
pub fn enc_f64_desc(v: f64) -> u64 { !enc_f64(v) }
pub fn enc_i64_desc(v: i64) -> u64 { !enc_i64(v) }
pub fn u64_at(k: &[u8], off: usize) -> u64 {
let mut w = [0u8; 8]; w.copy_from_slice(&k[off..off+8]); u64::from_be_bytes(w)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn byte_order_is_numeric_order_and_tags_never_interleave() {
let mut ks: Vec<Vec<u8>> = (0..64u64).map(|i| edge(0, i * 7919, 1, i)).collect();
let want = ks.clone();
ks.sort();
assert_eq!(ks, want, "big-endian keys must already be sorted");
assert!(node(u64::MAX) < edge(0, 0, 0, 0), "node space must sort before edge space");
assert!(edge(0, u64::MAX, u64::MAX, u64::MAX) < redge(0, 0, 0, 0));
assert!(redge(0, u64::MAX, 0, 0) < edge(1, 0, 0, 0), "ctx spaces sort after base");
assert!(edge(1, u64::MAX, 0, 0) < redge(1, 0, 0, 0));
}
#[test]
fn adjacency_is_a_contiguous_prefix() {
let pre = edge_prefix(0, 42);
for d in 0..16u64 {
assert!(edge(0, 42, 7, d).starts_with(&pre));
assert!(!edge(0, 43, 7, d).starts_with(&pre));
assert!(!edge(1, 42, 7, d).starts_with(&pre), "another ctx leaked in");
}
let kg = ctx_prefix(5);
assert!(edge(5, 0, 0, 0).starts_with(&kg) && edge(5, u64::MAX, 1, 1).starts_with(&kg));
assert!(!edge(6, 0, 0, 0).starts_with(&kg));
}
}