1use crate::error::{err, Result, StatusCode};
4
5use std::cell::RefCell;
6use std::path::Path;
7use std::rc::Rc;
8
9use bytes::Bytes;
10
11pub const NUM_LEVELS: usize = 7;
12
13pub type SequenceNumber = u64;
15
16pub const MAX_SEQUENCE_NUMBER: SequenceNumber = (1 << 56) - 1;
17
18pub type Shared<T> = Rc<RefCell<T>>;
20
21pub fn share<T>(t: T) -> Rc<RefCell<T>> {
22 Rc::new(RefCell::new(t))
23}
24
25#[derive(PartialEq)]
26pub enum Direction {
27 Forward,
28 Reverse,
29}
30
31pub struct Range<'a> {
33 pub start: &'a [u8],
34 pub limit: &'a [u8],
35}
36
37pub trait LdbIterator {
44 fn advance(&mut self) -> bool;
48 fn current(&self) -> Option<(Bytes, Bytes)>;
50 fn seek(&mut self, key: &[u8]);
53 fn reset(&mut self);
55 fn valid(&self) -> bool;
58 fn prev(&mut self) -> bool;
62
63 fn next(&mut self) -> Option<(Vec<u8>, Vec<u8>)> {
69 if !self.advance() {
70 return None;
71 }
72 if let Some((key, val)) = self.current() {
73 Some((key.to_vec(), val.to_vec()))
74 } else {
75 None
76 }
77 }
78
79 fn seek_to_first(&mut self) {
81 self.reset();
82 self.advance();
83 }
84}
85
86pub fn current_key_val<It: LdbIterator + ?Sized>(it: &It) -> Option<(Vec<u8>, Vec<u8>)> {
89 if let Some((k, v)) = it.current() {
90 Some((k.to_vec(), v.to_vec()))
91 } else {
92 None
93 }
94}
95
96impl LdbIterator for Box<dyn LdbIterator> {
97 fn advance(&mut self) -> bool {
98 self.as_mut().advance()
99 }
100 fn current(&self) -> Option<(Bytes, Bytes)> {
101 self.as_ref().current()
102 }
103 fn seek(&mut self, key: &[u8]) {
104 self.as_mut().seek(key)
105 }
106 fn reset(&mut self) {
107 self.as_mut().reset()
108 }
109 fn valid(&self) -> bool {
110 self.as_ref().valid()
111 }
112 fn prev(&mut self) -> bool {
113 self.as_mut().prev()
114 }
115}
116
117pub type FileNum = u64;
119
120#[derive(Clone, Debug, Default, PartialEq)]
122pub struct FileMetaData {
123 pub allowed_seeks: usize,
125 pub num: FileNum,
126 pub size: usize,
127 pub smallest: Bytes,
129 pub largest: Bytes,
130}
131
132#[derive(Debug, Clone, PartialEq)]
133pub enum FileType {
134 Log,
135 DBLock,
136 Table,
137 Descriptor,
138 Current,
139 Temp,
140 InfoLog,
141}
142
143pub fn parse_file_name<P: AsRef<Path>>(ff: P) -> Result<(FileNum, FileType)> {
144 let f = ff.as_ref().to_str().unwrap();
145 if f == "CURRENT" {
146 return Ok((0, FileType::Current));
147 } else if f == "LOCK" {
148 return Ok((0, FileType::DBLock));
149 } else if f == "LOG" || f == "LOG.old" {
150 return Ok((0, FileType::InfoLog));
151 } else if f.starts_with("MANIFEST-") {
152 if let Some(ix) = f.find('-') {
153 if let Ok(num) = FileNum::from_str_radix(&f[ix + 1..], 10) {
154 return Ok((num, FileType::Descriptor));
155 }
156 return err(
157 StatusCode::InvalidArgument,
158 "manifest file number is invalid",
159 );
160 }
161 return err(StatusCode::InvalidArgument, "manifest file has no dash");
162 } else if let Some(ix) = f.find('.') {
163 if let Ok(num) = FileNum::from_str_radix(&f[0..ix], 10) {
165 let typ = match &f[ix + 1..] {
166 "log" => FileType::Log,
167 "sst" | "ldb" => FileType::Table,
168 "dbtmp" => FileType::Temp,
169 _ => {
170 return err(
171 StatusCode::InvalidArgument,
172 "unknown numbered file extension",
173 )
174 }
175 };
176 return Ok((num, typ));
177 }
178 return err(
179 StatusCode::InvalidArgument,
180 "invalid file number for table or temp file",
181 );
182 }
183 err(StatusCode::InvalidArgument, "unknown file type")
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn test_types_parse_file_name() {
192 for c in &[
193 ("CURRENT", (0, FileType::Current)),
194 ("LOCK", (0, FileType::DBLock)),
195 ("LOG", (0, FileType::InfoLog)),
196 ("LOG.old", (0, FileType::InfoLog)),
197 ("MANIFEST-01234", (1234, FileType::Descriptor)),
198 ("001122.sst", (1122, FileType::Table)),
199 ("001122.ldb", (1122, FileType::Table)),
200 ("001122.dbtmp", (1122, FileType::Temp)),
201 ] {
202 assert_eq!(parse_file_name(c.0).unwrap(), c.1);
203 }
204 assert!(parse_file_name("xyz.LOCK").is_err());
205 assert!(parse_file_name("01a.sst").is_err());
206 assert!(parse_file_name("0011.abc").is_err());
207 assert!(parse_file_name("MANIFEST-trolol").is_err());
208 }
209}