use std::sync::Arc;
use crate::sync::Mutex;
pub(crate) const META_CF_ID: u32 = 0;
pub(crate) const DEFAULT_CF_ID: u32 = 1;
pub(crate) const MAX_CF_ID: u32 = u32::MAX - 1;
pub const DEFAULT_CF_NAME: &str = "default";
#[derive(Debug, Clone)]
pub struct ColumnFamilyHandle {
pub(crate) name: Arc<String>,
pub(crate) id: u32,
}
impl ColumnFamilyHandle {
pub fn name(&self) -> &str {
&self.name
}
pub(crate) fn id(&self) -> u32 {
self.id
}
}
impl PartialEq for ColumnFamilyHandle {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for ColumnFamilyHandle {}
pub(crate) fn prefix_key(cf_id: u32, key: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(4 + key.len());
out.extend_from_slice(&cf_id.to_be_bytes());
out.extend_from_slice(key);
out
}
pub(crate) fn cf_upper_bound(cf_id: u32) -> Vec<u8> {
cf_id.saturating_add(1).to_be_bytes().to_vec()
}
pub(crate) fn cf_lower_bound(cf_id: u32) -> Vec<u8> {
cf_id.to_be_bytes().to_vec()
}
pub(crate) mod meta {
use super::META_CF_ID;
pub(crate) fn next_id_key() -> Vec<u8> {
let mut k = META_CF_ID.to_be_bytes().to_vec();
k.extend_from_slice(b"next_id");
k
}
pub(crate) fn name_key(name: &str) -> Vec<u8> {
let mut k = META_CF_ID.to_be_bytes().to_vec();
k.extend_from_slice(b"name:");
k.extend_from_slice(name.as_bytes());
k
}
pub(crate) fn name_scan_prefix() -> Vec<u8> {
let mut k = META_CF_ID.to_be_bytes().to_vec();
k.extend_from_slice(b"name:");
k
}
pub(crate) fn name_scan_upper() -> Vec<u8> {
let mut k = META_CF_ID.to_be_bytes().to_vec();
k.extend_from_slice(b"name;");
k
}
pub(crate) fn name_from_key(key: &[u8]) -> Option<&str> {
let prefix = name_scan_prefix();
key.strip_prefix(prefix.as_slice())
.and_then(|bytes| std::str::from_utf8(bytes).ok())
}
}
pub(crate) struct CfRegistry {
inner: Mutex<CfRegistryInner>,
}
struct CfRegistryInner {
next_id: u32,
by_name: std::collections::HashMap<String, u32>,
by_id: std::collections::HashMap<u32, String>,
}
impl CfRegistry {
pub(crate) fn new() -> Self {
Self {
inner: Mutex::new(CfRegistryInner {
next_id: DEFAULT_CF_ID + 1,
by_name: std::collections::HashMap::new(),
by_id: std::collections::HashMap::new(),
}),
}
}
pub(crate) fn load(&self, entries: impl IntoIterator<Item = (String, u32)>, next_id: u32) {
let mut inner = self.inner.lock();
inner.next_id = next_id;
inner.by_name.clear();
inner.by_id.clear();
for (name, id) in entries {
inner.by_name.insert(name.clone(), id);
inner.by_id.insert(id, name);
}
}
pub(crate) fn get(&self, name: &str) -> Option<ColumnFamilyHandle> {
let inner = self.inner.lock();
inner.by_name.get(name).map(|&id| ColumnFamilyHandle {
name: Arc::new(name.to_string()),
id,
})
}
pub(crate) fn contains_id(&self, id: u32) -> bool {
self.inner.lock().by_id.contains_key(&id)
}
pub(crate) fn is_live_handle(&self, cf: &ColumnFamilyHandle) -> bool {
let inner = self.inner.lock();
inner
.by_name
.get(cf.name())
.is_some_and(|&id| id == cf.id())
&& inner
.by_id
.get(&cf.id())
.is_some_and(|name| name == cf.name())
}
pub(crate) fn allocate(&self, name: &str) -> Option<(ColumnFamilyHandle, u32)> {
let mut inner = self.inner.lock();
let id = inner.next_id;
if id > MAX_CF_ID {
return None;
}
inner.next_id = id + 1;
inner.by_name.insert(name.to_string(), id);
inner.by_id.insert(id, name.to_string());
Some((
ColumnFamilyHandle {
name: Arc::new(name.to_string()),
id,
},
inner.next_id,
))
}
pub(crate) fn remove(&self, name: &str) {
let mut inner = self.inner.lock();
if let Some(id) = inner.by_name.remove(name) {
inner.by_id.remove(&id);
}
}
pub(crate) fn names(&self) -> Vec<String> {
self.inner.lock().by_name.keys().cloned().collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn prefix_key_contains_cf_id() {
let k = prefix_key(5, b"hello");
assert_eq!(&k[0..4], &[0, 0, 0, 5]);
assert_eq!(&k[4..], b"hello");
}
#[test]
fn cf_bounds_are_adjacent() {
let lo = cf_lower_bound(7);
let hi = cf_upper_bound(7);
assert_eq!(lo, vec![0, 0, 0, 7]);
assert_eq!(hi, vec![0, 0, 0, 8]);
assert!(lo < hi);
}
#[test]
fn meta_keys_live_under_reserved_cf() {
let next = meta::next_id_key();
assert_eq!(&next[0..4], &[0, 0, 0, 0]);
let name = meta::name_key("foo");
assert_eq!(&name[0..4], &[0, 0, 0, 0]);
assert!(name.ends_with(b"foo"));
}
#[test]
fn meta_name_from_key_parses() {
let key = meta::name_key("widgets");
assert_eq!(meta::name_from_key(&key), Some("widgets"));
}
#[test]
fn meta_scan_range_is_tight() {
let lo = meta::name_scan_prefix();
let hi = meta::name_scan_upper();
assert!(lo < hi);
let foo = meta::name_key("foo");
assert!(lo <= foo);
assert!(foo < hi);
}
#[test]
fn registry_refuses_to_mint_the_last_id() {
let r = CfRegistry::new();
r.load(std::iter::empty(), MAX_CF_ID);
let (last, next) = r.allocate("last").expect("one id left");
assert_eq!(last.id, MAX_CF_ID);
assert_eq!(next, MAX_CF_ID + 1);
assert!(r.allocate("one too many").is_none());
assert!(cf_lower_bound(last.id) < cf_upper_bound(last.id));
}
#[test]
fn registry_allocate_is_monotonic() {
let r = CfRegistry::new();
let (a, _) = r.allocate("alpha").expect("id space");
let (b, _) = r.allocate("beta").expect("id space");
assert!(a.id < b.id);
assert_eq!(r.get("alpha").unwrap(), a);
assert_eq!(r.get("beta").unwrap(), b);
}
}