use crate::registry::{DatabaseStorePrefixes, SEPARATOR};
use smallvec::SmallVec;
use std::fmt::{Debug, Display};
#[derive(Clone)]
pub struct DbKey {
path: SmallVec<[u8; 36]>, prefix_len: usize,
}
impl DbKey {
pub fn new<TKey>(prefix: &[u8], key: TKey) -> Self
where
TKey: Clone + AsRef<[u8]>,
{
Self { path: prefix.iter().chain(key.as_ref().iter()).copied().collect(), prefix_len: prefix.len() }
}
pub fn new_with_bucket<TKey, TBucket>(prefix: &[u8], bucket: TBucket, key: TKey) -> Self
where
TKey: Clone + AsRef<[u8]>,
TBucket: Copy + AsRef<[u8]>,
{
let mut db_key = Self::prefix_only(prefix);
db_key.add_bucket(bucket);
db_key.add_key(key);
db_key
}
pub fn new_with_suffix<TKey, TSuffix>(prefix: &[u8], key: TKey, suffix: TSuffix) -> Self
where
TKey: Clone + AsRef<[u8]>,
TSuffix: AsRef<[u8]>,
{
let mut db_key = Self::new(prefix, key);
db_key.add_suffix(suffix);
db_key
}
pub fn prefix_only(prefix: &[u8]) -> Self {
Self::new(prefix, [])
}
pub fn add_bucket<TBucket>(&mut self, bucket: TBucket)
where
TBucket: Copy + AsRef<[u8]>,
{
self.path.extend(bucket.as_ref().iter().copied());
self.prefix_len += bucket.as_ref().len();
}
pub fn add_key<TKey>(&mut self, key: TKey)
where
TKey: Clone + AsRef<[u8]>,
{
self.path.extend(key.as_ref().iter().copied());
self.prefix_len += key.as_ref().len();
}
pub fn add_suffix<TSuffix>(&mut self, suffix: TSuffix)
where
TSuffix: AsRef<[u8]>,
{
self.path.extend(suffix.as_ref().iter().copied());
}
pub fn prefix_len(&self) -> usize {
self.prefix_len
}
}
impl AsRef<[u8]> for DbKey {
fn as_ref(&self) -> &[u8] {
&self.path
}
}
impl Display for DbKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use DatabaseStorePrefixes::*;
let mut pos = 0;
if self.prefix_len > 0
&& let Ok(prefix) = DatabaseStorePrefixes::try_from(self.path[0])
{
prefix.fmt(f)?;
f.write_str("/")?;
pos += 1;
if self.prefix_len > 1 {
match prefix {
Ghostdag
| GhostdagCompact
| TempGhostdag
| TempGhostdagCompact
| RelationsParents
| RelationsChildren
| Reachability
| ReachabilityTreeChildren
| ReachabilityFutureCoveringSet => {
if self.path[1] != SEPARATOR {
Display::fmt(&self.path[1], f)?;
f.write_str("/")?;
}
pos += 1;
}
ReachabilityRelations => {
if let Ok(next_prefix) = DatabaseStorePrefixes::try_from(self.path[1]) {
next_prefix.fmt(f)?;
f.write_str("/")?;
pos += 1;
}
}
_ => {}
}
}
}
f.write_str(&faster_hex::hex_string(&self.path[pos..]))
}
}
impl Debug for DbKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self, f)
}
}
#[cfg(test)]
mod tests {
use super::*;
use DatabaseStorePrefixes::*;
use kaspa_hashes::{HASH_SIZE, Hash};
#[test]
fn test_key_display() {
let level = 37;
let key1 = DbKey::new(&[ReachabilityRelations.into(), RelationsParents.into()], Hash::from_u64_word(34567890));
let key2 = DbKey::new(&[Reachability.into(), Separator.into()], Hash::from_u64_word(345690));
let key3 = DbKey::new(&[Reachability.into(), level], Hash::from_u64_word(345690));
let key4 = DbKey::new(&[RelationsParents.into(), level], Hash::from_u64_word(345690));
assert!(key1.to_string().starts_with(&format!("{:?}/{:?}/00", ReachabilityRelations, RelationsParents)));
assert!(key2.to_string().starts_with(&format!("{:?}/00", Reachability)));
assert!(key3.to_string().starts_with(&format!("{:?}/{}/00", Reachability, level)));
assert!(key4.to_string().starts_with(&format!("{:?}/{}/00", RelationsParents, level)));
let key5 = DbKey::new(b"human/readable", Hash::from_bytes([SEPARATOR; HASH_SIZE]));
let key6 = DbKey::prefix_only(&[0xC0, 0xC1, 0xF5, 0xF6]);
let key7 = DbKey::prefix_only(b"direct-prefix");
let _ = key5.to_string();
let _ = key6.to_string();
let _ = key7.to_string();
}
#[test]
fn test_add_suffix_and_new_with_suffix() {
let prefix: [u8; 1] = [42];
let key = [0x01u8; 4];
let suffix = [0xFFu8];
let db_key = DbKey::new_with_suffix(&prefix, key, suffix);
assert_eq!(db_key.as_ref(), &[42, 0x01, 0x01, 0x01, 0x01, 0xFF]);
assert_eq!(db_key.prefix_len(), prefix.len(), "suffix must not grow prefix_len");
let mut extended = DbKey::new(&prefix, key);
let prefix_len_before = extended.prefix_len();
extended.add_suffix([0x77u8, 0x88u8]);
assert_eq!(extended.as_ref(), &[42, 0x01, 0x01, 0x01, 0x01, 0x77, 0x88]);
assert_eq!(extended.prefix_len(), prefix_len_before);
}
}