1use crate::StorageType;
5use bincode::Options;
6use serde::Serialize;
7use std::ops::{Bound, RangeBounds};
8use std::path::Path;
9
10#[inline]
11pub fn be_fix_int_ser<S>(t: &S) -> Vec<u8>
12where
13 S: ?Sized + serde::Serialize,
14{
15 bincode::DefaultOptions::new()
16 .with_big_endian()
17 .with_fixint_encoding()
18 .serialize(t)
19 .expect("failed to serialize via be_fix_int_ser method")
20}
21
22#[inline]
25pub fn be_fix_int_ser_into<S>(buf: &mut Vec<u8>, t: &S) -> usize
26where
27 S: ?Sized + serde::Serialize,
28{
29 let before = buf.len();
30 bincode::DefaultOptions::new()
31 .with_big_endian()
32 .with_fixint_encoding()
33 .serialize_into(&mut *buf, t)
34 .expect("failed to serialize via be_fix_int_ser_into method");
35 buf.len() - before
36}
37
38pub(crate) fn iterator_bounds<K>(
39 lower_bound: Option<K>,
40 upper_bound: Option<K>,
41) -> (Option<Vec<u8>>, Option<Vec<u8>>)
42where
43 K: Serialize,
44{
45 (
46 lower_bound.map(|b| be_fix_int_ser(&b)),
47 upper_bound.map(|b| be_fix_int_ser(&b)),
48 )
49}
50
51pub(crate) fn iterator_bounds_with_range<K>(
52 range: impl RangeBounds<K>,
53) -> (Option<Vec<u8>>, Option<Vec<u8>>)
54where
55 K: Serialize,
56{
57 let iterator_lower_bound = match range.start_bound() {
58 Bound::Included(lower_bound) => {
59 Some(be_fix_int_ser(&lower_bound))
61 }
63 Bound::Excluded(lower_bound) => {
64 let mut key_buf = be_fix_int_ser(&lower_bound);
65
66 big_endian_saturating_add_one(&mut key_buf);
68 Some(key_buf)
69 }
71 Bound::Unbounded => None,
72 };
73 let iterator_upper_bound = match range.end_bound() {
74 Bound::Included(upper_bound) => {
75 let mut key_buf = be_fix_int_ser(&upper_bound);
76
77 if !is_max(&key_buf) {
79 big_endian_saturating_add_one(&mut key_buf);
81 }
83 Some(key_buf)
84 }
85 Bound::Excluded(upper_bound) => {
86 Some(be_fix_int_ser(&upper_bound))
88 }
90 Bound::Unbounded => None,
91 };
92 (iterator_lower_bound, iterator_upper_bound)
93}
94
95fn big_endian_saturating_add_one(v: &mut [u8]) {
99 if is_max(v) {
100 return;
101 }
102 for i in (0..v.len()).rev() {
103 if v[i] == u8::MAX {
104 v[i] = 0;
105 } else {
106 v[i] += 1;
107 break;
108 }
109 }
110}
111
112fn is_max(v: &[u8]) -> bool {
114 v.iter().all(|&x| x == u8::MAX)
115}
116
117pub(crate) fn ensure_database_type<P: AsRef<Path>>(
118 path: P,
119 storage_type: StorageType,
120) -> std::io::Result<()> {
121 if !path.as_ref().exists() {
122 return Ok(());
123 }
124 for entry in std::fs::read_dir(path)? {
125 let filepath = entry?.path();
126 if filepath.extension().is_some_and(|ext| ext == "sst")
127 && storage_type != StorageType::Rocks
128 {
129 panic!(
130 "DB type mismatch: expected {:?}, found RocksDB",
131 storage_type
132 );
133 }
134 if filepath
135 .file_name()
136 .is_some_and(|n| n.to_string_lossy().starts_with("wal_"))
137 && storage_type != StorageType::TideHunter
138 {
139 panic!(
140 "DB type mismatch: expected {:?}, found TideHunter",
141 storage_type
142 );
143 }
144 }
145 Ok(())
146}
147
148#[allow(clippy::assign_op_pattern, clippy::manual_div_ceil)]
149#[test]
150fn test_helpers() {
151 let v = vec![];
152 assert!(is_max(&v));
153
154 fn check_add(v: Vec<u8>) {
155 let mut v = v;
156 let num = Num32::from_big_endian(&v);
157 big_endian_saturating_add_one(&mut v);
158 assert!(num + 1 == Num32::from_big_endian(&v));
159 }
160
161 uint::construct_uint! {
162 struct Num32(4);
164 }
165
166 let mut v = vec![255; 32];
167 big_endian_saturating_add_one(&mut v);
168 assert!(Num32::MAX == Num32::from_big_endian(&v));
169
170 check_add(vec![1; 32]);
171 check_add(vec![6; 32]);
172 check_add(vec![254; 32]);
173
174 }