rusty_leveldb/
types.rs

1//! A collection of fundamental and/or simple types used by other modules
2
3use 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
13/// Represents a sequence number of a single entry.
14pub type SequenceNumber = u64;
15
16pub const MAX_SEQUENCE_NUMBER: SequenceNumber = (1 << 56) - 1;
17
18/// A shared thingy with interior mutability.
19pub 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
31/// Denotes a key range
32pub struct Range<'a> {
33    pub start: &'a [u8],
34    pub limit: &'a [u8],
35}
36
37/// An extension of the standard `Iterator` trait that supports some methods necessary for LevelDB.
38/// This works because the iterators used are stateful and keep the last returned element.
39///
40/// Note: Implementing types are expected to hold `!valid()` before the first call to `advance()`.
41///
42/// test_util::test_iterator_properties() verifies that all properties hold.
43pub trait LdbIterator {
44    /// Advances the position of the iterator by one element (which can be retrieved using
45    /// current(). If no more elements are available, advance() returns false, and the iterator
46    /// becomes invalid (i.e. as if reset() had been called).
47    fn advance(&mut self) -> bool;
48    /// Return the current item (i.e. the item most recently returned by `next()`).
49    fn current(&self) -> Option<(Bytes, Bytes)>;
50    /// Seek the iterator to `key` or the next bigger key. If the seek is invalid (past last
51    /// element, or before first element), the iterator is `reset()` and not valid.
52    fn seek(&mut self, key: &[u8]);
53    /// Resets the iterator to be `!valid()`, i.e. positioned before the first element.
54    fn reset(&mut self);
55    /// Returns true if the iterator is not positioned before the first or after the last element,
56    /// i.e. if `current()` would succeed.
57    fn valid(&self) -> bool;
58    /// Go to the previous item; if the iterator is moved beyond the first element, `prev()`
59    /// returns false and it will be `!valid()`. This is inefficient for most iterator
60    /// implementations.
61    fn prev(&mut self) -> bool;
62
63    // default implementations.
64
65    /// next is like Iterator::next(). It's implemented here because Rust disallows implementing a
66    /// foreign trait for any type, thus we can't do `impl<T: LdbIterator> Iterator<Item=Vec<u8>>
67    /// for T {}`.
68    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    /// seek_to_first seeks to the first element.
80    fn seek_to_first(&mut self) {
81        self.reset();
82        self.advance();
83    }
84}
85
86/// current_key_val is a helper allocating two vectors and filling them with the current key/value
87/// of the specified iterator.
88pub 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
117/// The unique (sequential) number of a file.
118pub type FileNum = u64;
119
120/// Describes a file on disk.
121#[derive(Clone, Debug, Default, PartialEq)]
122pub struct FileMetaData {
123    // default: size / 16384.
124    pub allowed_seeks: usize,
125    pub num: FileNum,
126    pub size: usize,
127    // these are in InternalKey format:
128    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        // 00012345.log 00123.sst ...
164        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}