1use crate::registry::{DatabaseStorePrefixes, SEPARATOR};
2use smallvec::SmallVec;
3use std::fmt::{Debug, Display};
4
5#[derive(Clone)]
6pub struct DbKey {
7 path: SmallVec<[u8; 36]>, prefix_len: usize,
9}
10
11impl DbKey {
12 pub fn new<TKey>(prefix: &[u8], key: TKey) -> Self
13 where
14 TKey: Clone + AsRef<[u8]>,
15 {
16 Self { path: prefix.iter().chain(key.as_ref().iter()).copied().collect(), prefix_len: prefix.len() }
17 }
18
19 pub fn new_with_bucket<TKey, TBucket>(prefix: &[u8], bucket: TBucket, key: TKey) -> Self
20 where
21 TKey: Clone + AsRef<[u8]>,
22 TBucket: Copy + AsRef<[u8]>,
23 {
24 let mut db_key = Self::prefix_only(prefix);
25 db_key.add_bucket(bucket);
26 db_key.add_key(key);
27 db_key
28 }
29
30 pub fn prefix_only(prefix: &[u8]) -> Self {
31 Self::new(prefix, [])
32 }
33
34 pub fn add_bucket<TBucket>(&mut self, bucket: TBucket)
36 where
37 TBucket: Copy + AsRef<[u8]>,
38 {
39 self.path.extend(bucket.as_ref().iter().copied());
40 self.prefix_len += bucket.as_ref().len();
41 }
42
43 pub fn add_key<TKey>(&mut self, key: TKey)
44 where
45 TKey: Clone + AsRef<[u8]>,
46 {
47 self.path.extend(key.as_ref().iter().copied());
48 self.prefix_len += key.as_ref().len();
49 }
50
51 pub fn prefix_len(&self) -> usize {
52 self.prefix_len
53 }
54}
55
56impl AsRef<[u8]> for DbKey {
57 fn as_ref(&self) -> &[u8] {
58 &self.path
59 }
60}
61
62impl Display for DbKey {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 use DatabaseStorePrefixes::*;
65 let mut pos = 0;
66
67 if self.prefix_len > 0 {
68 if let Ok(prefix) = DatabaseStorePrefixes::try_from(self.path[0]) {
69 prefix.fmt(f)?;
70 f.write_str("/")?;
71 pos += 1;
72 if self.prefix_len > 1 {
73 match prefix {
74 Ghostdag
75 | GhostdagCompact
76 | RelationsParents
77 | RelationsChildren
78 | Reachability
79 | ReachabilityTreeChildren
80 | ReachabilityFutureCoveringSet => {
81 if self.path[1] != SEPARATOR {
82 Display::fmt(&self.path[1], f)?;
84 f.write_str("/")?;
85 }
86 pos += 1;
87 }
88 ReachabilityRelations => {
89 if let Ok(next_prefix) = DatabaseStorePrefixes::try_from(self.path[1]) {
90 next_prefix.fmt(f)?;
91 f.write_str("/")?;
92 pos += 1;
93 }
94 }
95 _ => {}
96 }
97 }
98 }
99 }
100
101 f.write_str(&faster_hex::hex_string(&self.path[pos..]))
103 }
104}
105
106impl Debug for DbKey {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 Display::fmt(&self, f)
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use kaspa_hashes::{Hash, HASH_SIZE};
116 use DatabaseStorePrefixes::*;
117
118 #[test]
119 fn test_key_display() {
120 let level = 37;
121 let key1 = DbKey::new(&[ReachabilityRelations.into(), RelationsParents.into()], Hash::from_u64_word(34567890));
122 let key2 = DbKey::new(&[Reachability.into(), Separator.into()], Hash::from_u64_word(345690));
123 let key3 = DbKey::new(&[Reachability.into(), level], Hash::from_u64_word(345690));
124 let key4 = DbKey::new(&[RelationsParents.into(), level], Hash::from_u64_word(345690));
125
126 assert!(key1.to_string().starts_with(&format!("{:?}/{:?}/00", ReachabilityRelations, RelationsParents)));
127 assert!(key2.to_string().starts_with(&format!("{:?}/00", Reachability)));
128 assert!(key3.to_string().starts_with(&format!("{:?}/{}/00", Reachability, level)));
129 assert!(key4.to_string().starts_with(&format!("{:?}/{}/00", RelationsParents, level)));
130
131 let key5 = DbKey::new(b"human/readable", Hash::from_bytes([SEPARATOR; HASH_SIZE]));
132 let key6 = DbKey::prefix_only(&[0xC0, 0xC1, 0xF5, 0xF6]);
133 let key7 = DbKey::prefix_only(b"direct-prefix");
134
135 let _ = key5.to_string();
137 let _ = key6.to_string();
138 let _ = key7.to_string();
139 }
140}