const TABLE_PREFIX_MARKER: &str = "\u{001e}actordb.table.v1:";
const TABLE_STORAGE_MARKER: &str = "\u{001d}actordb.table.store.v1:";
fn hex(bytes: &[u8]) -> String {
const DIGITS: &[u8; 16] = b"0123456789abcdef";
let mut output = String::with_capacity(bytes.len().saturating_mul(2));
for byte in bytes {
output.push(DIGITS[(byte >> 4) as usize] as char);
output.push(DIGITS[(byte & 0x0f) as usize] as char);
}
output
}
fn decode_hex(encoded: &str) -> Option<Vec<u8>> {
let bytes = encoded.as_bytes();
if !bytes.len().is_multiple_of(2) {
return None;
}
bytes
.as_chunks::<2>()
.0
.iter()
.map(|pair| {
let high = hex_digit(pair[0])?;
let low = hex_digit(pair[1])?;
Some((high << 4) | low)
})
.collect()
}
fn hex_digit(byte: u8) -> Option<u8> {
match byte {
b'0'..=b'9' => Some(byte - b'0'),
b'a'..=b'f' => Some(byte - b'a' + 10),
b'A'..=b'F' => Some(byte - b'A' + 10),
_ => None,
}
}
pub(crate) fn table_prefix(name: &str) -> String {
format!("{TABLE_PREFIX_MARKER}{}:", hex(name.as_bytes()))
}
pub(crate) fn table_storage_prefix(name: &str) -> Vec<u8> {
format!("{TABLE_STORAGE_MARKER}{}:", hex(name.as_bytes())).into_bytes()
}
pub(crate) fn encode_table_key(name: &str, key: &str) -> String {
format!("{}{}", table_prefix(name), hex(key.as_bytes()))
}
pub(crate) fn encode_table_prefix(name: &str, prefix: &[u8]) -> Vec<u8> {
format!("{}{}", table_prefix(name), hex(prefix)).into_bytes()
}
pub(crate) fn decode_table_key(name: &str, key: &str) -> Option<String> {
let encoded = key.strip_prefix(&table_prefix(name))?;
String::from_utf8(decode_hex(encoded)?).ok()
}
pub(crate) fn decode_table_prefix(name: &str, prefix: &[u8]) -> Option<Vec<u8>> {
let namespace = table_prefix(name);
let encoded = std::str::from_utf8(prefix).ok()?.strip_prefix(&namespace)?;
decode_hex(encoded)
}
pub(crate) fn table_name(key: &str) -> Option<String> {
let encoded = key.strip_prefix(TABLE_PREFIX_MARKER)?;
let (table_hex, _) = encoded.split_once(':')?;
String::from_utf8(decode_hex(table_hex)?).ok()
}
pub(crate) fn strip_storage_prefix(key: &[u8]) -> Option<Vec<u8>> {
if !key.starts_with(TABLE_STORAGE_MARKER.as_bytes()) {
return None;
}
let remainder = &key[TABLE_STORAGE_MARKER.len()..];
let colon = remainder.iter().position(|byte| *byte == b':')?;
let table_hex = std::str::from_utf8(&remainder[..colon]).ok()?;
decode_hex(table_hex)?;
Some(remainder[colon + 1..].to_vec())
}
pub(crate) fn logical_record_key(physical: &str) -> Result<&str, &'static str> {
let Some(remainder) = physical.strip_prefix(TABLE_STORAGE_MARKER) else {
return Ok(physical);
};
let (stored_table, logical) = remainder
.split_once(':')
.ok_or("invalid physical table namespace")?;
let remainder = logical
.strip_prefix(TABLE_PREFIX_MARKER)
.ok_or("physical table record lacks logical namespace")?;
let (logical_table, user_key) = remainder
.split_once(':')
.ok_or("invalid logical table namespace")?;
if stored_table.is_empty()
|| stored_table != logical_table
|| !valid_hex_utf8(stored_table, true)
|| !valid_hex_utf8(user_key, false)
{
return Err("physical/logical table identities or encoded key disagree");
}
Ok(logical)
}
fn valid_hex_utf8(encoded: &str, canonical: bool) -> bool {
let encoded = encoded.as_bytes();
if !encoded.len().is_multiple_of(2) {
return false;
}
let mut position = 0;
while position < encoded.len() {
let Some(first) = decoded_pair(&encoded[position..position + 2], canonical) else {
return false;
};
let width = match first {
0..=0x7f => 1,
0xc2..=0xdf => 2,
0xe0..=0xef => 3,
0xf0..=0xf4 => 4,
_ => return false,
};
let Some(end) = position
.checked_add(width * 2)
.filter(|end| *end <= encoded.len())
else {
return false;
};
let mut bytes = [0; 4];
for (index, pair) in encoded[position..end].as_chunks::<2>().0.iter().enumerate() {
let Some(byte) = decoded_pair(pair, canonical) else {
return false;
};
bytes[index] = byte;
}
if std::str::from_utf8(&bytes[..width]).is_err() {
return false;
}
position = end;
}
true
}
fn decoded_pair(pair: &[u8], canonical: bool) -> Option<u8> {
if canonical
&& pair
.iter()
.any(|byte| !byte.is_ascii_digit() && !(b'a'..=b'f').contains(byte))
{
return None;
}
Some(hex_digit(pair[0])? << 4 | hex_digit(pair[1])?)
}
pub(crate) fn routing_hash(key: &str) -> u64 {
let key = key
.strip_prefix(TABLE_STORAGE_MARKER)
.and_then(|remainder| remainder.split_once(':').map(|(_, logical)| logical))
.unwrap_or(key);
let bytes = if let Some(name) = table_name(key) {
let prefix = table_prefix(&name);
key.strip_prefix(&prefix)
.and_then(decode_hex)
.unwrap_or_else(|| key.as_bytes().to_vec())
} else {
key.as_bytes().to_vec()
};
seahash::hash(&bytes)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn table_name_round_trips_encoded_key() {
let key = encode_table_key("KV", "ephemeral");
assert_eq!(table_name(&key).as_deref(), Some("KV"));
}
#[test]
fn durable_table_key_routes_like_its_logical_key() {
let logical = encode_table_key("KV", "durable");
let physical = format!(
"{}{}",
String::from_utf8(table_storage_prefix("KV")).unwrap(),
logical
);
assert_eq!(routing_hash(&logical), routing_hash(&physical));
}
#[test]
fn physical_namespace_validation_borrows_matching_logical_key_without_relaxing_identity() {
for (table, key) in [("KV", ""), ("USERS", "snow:雪\0"), ("é", "😀")] {
let logical = encode_table_key(table, key);
let physical = format!(
"{}{}",
String::from_utf8(table_storage_prefix(table)).unwrap(),
logical
);
assert_eq!(logical_record_key(&physical).unwrap(), logical);
}
assert_eq!(logical_record_key("plain key").unwrap(), "plain key");
for physical in [
format!(
"{}{}",
String::from_utf8(table_storage_prefix("A")).unwrap(),
encode_table_key("B", "k")
),
format!(
"{}plain",
String::from_utf8(table_storage_prefix("A")).unwrap()
),
format!(
"{}{}ff",
String::from_utf8(table_storage_prefix("A")).unwrap(),
table_prefix("A")
),
format!(
"{}{}f",
String::from_utf8(table_storage_prefix("A")).unwrap(),
table_prefix("A")
),
format!("{TABLE_STORAGE_MARKER}4B56:{}", encode_table_key("KV", "k")),
] {
assert!(logical_record_key(&physical).is_err(), "{physical:?}");
}
assert!(valid_hex_utf8("e99baa", true));
assert!(valid_hex_utf8("E99BAA", false));
for hex in ["c080", "eda080", "f4908080", "ff", "e9", "0g"] {
assert!(!valid_hex_utf8(hex, false), "{hex}");
}
}
}