diff options
author | Michal Nazarewicz <mina86@mina86.com> | 2023-08-07 17:56:06 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-07 17:56:06 +0200 |
commit | 41512c03c2ad5f745cda08e2079db93cc340282c (patch) | |
tree | b000d37fafedae02496816f2378c3d7e8bc28674 | |
parent | f8906c39ac26d51b4f5873370c8e214ff86fa02c (diff) |
Wrap prop names into a PropName type offering free conversion to str (#780)
-rw-r--r-- | .github/workflows/rust.yml | 2 | ||||
-rw-r--r-- | CHANGELOG.md | 18 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | README.md | 22 | ||||
-rw-r--r-- | librocksdb-sys/Cargo.toml | 2 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/prop_name.rs | 324 | ||||
-rw-r--r-- | src/properties.rs | 125 | ||||
-rw-r--r-- | tests/test_property.rs | 4 |
9 files changed, 402 insertions, 98 deletions
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 25ad816..ac9a3d3 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,7 +2,7 @@ name: RocksDB CI on: [push, pull_request] env: - RUST_VERSION: 1.60.0 + RUST_VERSION: 1.63.0 jobs: fmt: diff --git a/CHANGELOG.md b/CHANGELOG.md index c6c487d..0d91541 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +* Bump MSRV to 1.63.0 (mina86) +* Convert properties to `&PropName` which can be converted at no cost to `&CStr` + and `&str` (mina86) + ## 0.21.0 (2023-05-09) * Add doc-check to CI with fix warnings in docs (YuraKotov) @@ -109,10 +113,10 @@ * Bump `librocksdb-sys` up to 6.20.3 (olegnn, akrylysov) * Add `DB::key_may_exist_cf_opt` method (stanislav-tkach) * Add `Options::set_zstd_max_train_bytes` method (stanislav-tkach) -* Mark Cache and Env as Send and Sync (akrylysov) +* Mark Cache and Env as Send and Sync (akrylysov) * Allow cloning the Cache and Env (duarten) -* Make SSE inclusion conditional for target features (mbargull) -* Use Self where possible (adamnemecek) +* Make SSE inclusion conditional for target features (mbargull) +* Use Self where possible (adamnemecek) * Don't leak dropped column families (ryoqun) ## 0.16.0 (2021-04-18) @@ -169,23 +173,23 @@ * Add `set_max_total_wal_size` to the `Options` (wqfish) * Simplify conversion on iterator item (zhangsoledad) * Add `flush_cf` method to the `DB` (wqfish) -* Fix potential segfault when calling `next` on the `DBIterator` that is at the end of the range (wqfish) +* Fix potential segfault when calling `next` on the `DBIterator` that is at the end of the range (wqfish) * Move to Rust 2018 (wqfish) * Fix doc for `WriteBatch::delete` (wqfish) * Bump `uuid` and `bindgen` dependencies (jonhoo) * Change APIs that never return error to not return `Result` (wqfish) * Fix lifetime parameter for iterators (wqfish) -* Add a doc for `optimize_level_style_compaction` method (NikVolf) +* Add a doc for `optimize_level_style_compaction` method (NikVolf) * Make `DBPath` use `tempfile` (jder) * Refactor `db.rs` and `lib.rs` into smaller pieces (jder) * Check if we're on a big endian system and act upon it (knarz) * Bump internal snappy version up to 1.1.8 (aleksuss) * Bump rocksdb version up to 6.7.3 (aleksuss) -* Atomic flush option (mappum) +* Atomic flush option (mappum) * Make `set_iterate_upper_bound` method safe (wqfish) * Add support for data block hash index (dvdplm) * Add some extra config options (casualjim) -* Add support for range delete APIs (wqfish) +* Add support for range delete APIs (wqfish) * Improve building `librocksdb-sys` with system libraries (basvandijk) * Add support for `open_for_read_only` APIs (wqfish) * Fix doc for `DBRawIterator::prev` and `next` methods (wqfish) @@ -3,7 +3,7 @@ name = "rocksdb" description = "Rust wrapper for Facebook's RocksDB embeddable database" version = "0.21.0" edition = "2018" -rust-version = "1.60" +rust-version = "1.63" authors = ["Tyler Neely <t@jujit.su>", "David Greenberg <dsg123456789@gmail.com>"] repository = "https://github.com/rust-rocksdb/rust-rocksdb" license = "Apache-2.0" @@ -5,7 +5,7 @@ rust-rocksdb [![documentation](https://docs.rs/rocksdb/badge.svg)](https://docs.rs/rocksdb) [![license](https://img.shields.io/crates/l/rocksdb.svg)](https://github.com/rust-rocksdb/rust-rocksdb/blob/master/LICENSE) [![Gitter chat](https://badges.gitter.im/rust-rocksdb/gitter.png)](https://gitter.im/rust-rocksdb/lobby) -![rust 1.60.0 required](https://img.shields.io/badge/rust-1.60.0-blue.svg?label=MSRV) +![rust 1.63.0 required](https://img.shields.io/badge/rust-1.63.0-blue.svg?label=MSRV) ![GitHub commits (since latest release)](https://img.shields.io/github/commits-since/rust-rocksdb/rust-rocksdb/latest.svg) @@ -16,25 +16,25 @@ rust-rocksdb ## Contributing -Feedback and pull requests welcome! If a particular feature of RocksDB is -important to you, please let me know by opening an issue, and I'll +Feedback and pull requests welcome! If a particular feature of RocksDB is +important to you, please let me know by opening an issue, and I'll prioritize it. ## Usage -This binding is statically linked with a specific version of RocksDB. If you -want to build it yourself, make sure you've also cloned the RocksDB and +This binding is statically linked with a specific version of RocksDB. If you +want to build it yourself, make sure you've also cloned the RocksDB and compression submodules: git submodule update --init --recursive ## Compression Support -By default, support for the [Snappy](https://github.com/google/snappy), -[LZ4](https://github.com/lz4/lz4), [Zstd](https://github.com/facebook/zstd), -[Zlib](https://zlib.net), and [Bzip2](http://www.bzip.org) compression -is enabled through crate features. If support for all of these compression -algorithms is not needed, default features can be disabled and specific -compression algorithms can be enabled. For example, to enable only LZ4 +By default, support for the [Snappy](https://github.com/google/snappy), +[LZ4](https://github.com/lz4/lz4), [Zstd](https://github.com/facebook/zstd), +[Zlib](https://zlib.net), and [Bzip2](http://www.bzip.org) compression +is enabled through crate features. If support for all of these compression +algorithms is not needed, default features can be disabled and specific +compression algorithms can be enabled. For example, to enable only LZ4 compression support, make these changes to your Cargo.toml: ``` diff --git a/librocksdb-sys/Cargo.toml b/librocksdb-sys/Cargo.toml index 57f6eb2..5a2bfc9 100644 --- a/librocksdb-sys/Cargo.toml +++ b/librocksdb-sys/Cargo.toml @@ -2,7 +2,7 @@ name = "librocksdb-sys" version = "0.11.0+8.3.2" edition = "2018" -rust-version = "1.60" +rust-version = "1.63" authors = ["Karl Hobley <karlhobley10@gmail.com>", "Arkadiy Paronyan <arkadiy@ethcore.io>"] license = "MIT/Apache-2.0/BSD-3-Clause" description = "Native bindings to librocksdb" @@ -89,6 +89,7 @@ mod env; mod iter_range; pub mod merge_operator; pub mod perf; +mod prop_name; pub mod properties; mod slice_transform; mod snapshot; diff --git a/src/prop_name.rs b/src/prop_name.rs new file mode 100644 index 0000000..0c8f717 --- /dev/null +++ b/src/prop_name.rs @@ -0,0 +1,324 @@ +use crate::ffi_util::CStrLike; + +use std::ffi::{CStr, CString}; + +/// A borrowed name of a RocksDB property. +/// +/// The value is guaranteed to be a nul-terminated UTF-8 string. This means it +/// can be converted to [`CStr`] and [`str`] at zero cost. +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct PropName(CStr); + +impl PropName { + /// Creates a new object from a nul-terminated string with no internal nul + /// bytes. + /// + /// Panics if the `value` isn’t terminated by a nul byte or contains + /// interior nul bytes. + pub(crate) const fn new_unwrap(value: &str) -> &Self { + let bytes = if let Some((&0, bytes)) = value.as_bytes().split_last() { + bytes + } else { + panic!("input was not nul-terminated"); + }; + + let mut idx = 0; + while idx < bytes.len() { + assert!(bytes[idx] != 0, "input contained interior nul byte"); + idx += 1; + } + + // SAFETY: 1. We’ve just verified `value` is a nul-terminated with no + // interior nul bytes and since its `str` it’s also valid UTF-8. + // 2. Self and CStr have the same representation so casting is sound. + unsafe { + let value = CStr::from_bytes_with_nul_unchecked(value.as_bytes()); + &*(value as *const CStr as *const Self) + } + } + + /// Converts the value into a C string slice. + #[inline] + pub fn as_c_str(&self) -> &CStr { + &self.0 + } + + /// Converts the value into a string slice. + /// + /// Nul byte terminating the underlying C string is not included in the + /// returned slice. + #[inline] + pub fn as_str(&self) -> &str { + // SAFETY: self.0 is guaranteed to be valid ASCII string. + unsafe { std::str::from_utf8_unchecked(self.0.to_bytes()) } + } +} + +impl core::ops::Deref for PropName { + type Target = CStr; + + #[inline] + fn deref(&self) -> &Self::Target { + self.as_c_str() + } +} + +impl core::convert::AsRef<CStr> for PropName { + #[inline] + fn as_ref(&self) -> &CStr { + self.as_c_str() + } +} + +impl core::convert::AsRef<str> for PropName { + #[inline] + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl std::borrow::ToOwned for PropName { + type Owned = PropertyName; + + #[inline] + fn to_owned(&self) -> Self::Owned { + PropertyName(self.0.to_owned()) + } + + #[inline] + fn clone_into(&self, target: &mut Self::Owned) { + self.0.clone_into(&mut target.0); + } +} + +impl core::fmt::Display for PropName { + #[inline] + fn fmt(&self, fmtr: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.as_str().fmt(fmtr) + } +} + +impl core::fmt::Debug for PropName { + #[inline] + fn fmt(&self, fmtr: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.as_str().fmt(fmtr) + } +} + +impl core::cmp::PartialEq<CStr> for PropName { + #[inline] + fn eq(&self, other: &CStr) -> bool { + self.as_c_str().eq(other) + } +} + +impl core::cmp::PartialEq<str> for PropName { + #[inline] + fn eq(&self, other: &str) -> bool { + self.as_str().eq(other) + } +} + +impl core::cmp::PartialEq<PropName> for CStr { + #[inline] + fn eq(&self, other: &PropName) -> bool { + self.eq(other.as_c_str()) + } +} + +impl core::cmp::PartialEq<PropName> for str { + #[inline] + fn eq(&self, other: &PropName) -> bool { + self.eq(other.as_str()) + } +} + +impl<'a> CStrLike for &'a PropName { + type Baked = &'a CStr; + type Error = std::convert::Infallible; + + #[inline] + fn bake(self) -> Result<Self::Baked, Self::Error> { + Ok(&self.0) + } + + #[inline] + fn into_c_string(self) -> Result<CString, Self::Error> { + Ok(self.0.to_owned()) + } +} + +/// An owned name of a RocksDB property. +/// +/// The value is guaranteed to be a nul-terminated UTF-8 string. This means it +/// can be converted to [`CString`] and [`String`] at zero cost. +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct PropertyName(CString); + +impl PropertyName { + /// Creates a new object from valid nul-terminated UTF-8 string. The string + /// must not contain interior nul bytes. + #[inline] + unsafe fn from_vec_with_nul_unchecked(inner: Vec<u8>) -> Self { + // SAFETY: Caller promises inner is nul-terminated and valid UTF-8. + Self(CString::from_vec_with_nul_unchecked(inner)) + } + + /// Converts the value into a C string. + #[inline] + pub fn into_c_string(self) -> CString { + self.0 + } + + /// Converts the property name into a string. + /// + /// Nul byte terminating the underlying C string is not included in the + /// returned value. + #[inline] + pub fn into_string(self) -> String { + // SAFETY: self.0 is guaranteed to be valid UTF-8. + unsafe { String::from_utf8_unchecked(self.0.into_bytes()) } + } +} + +impl std::ops::Deref for PropertyName { + type Target = PropName; + + #[inline] + fn deref(&self) -> &Self::Target { + // SAFETY: 1. PropName and CStr have the same representation so casting + // is safe. 2. self.0 is guaranteed to be valid nul-terminated UTF-8 + // string. + unsafe { &*(self.0.as_c_str() as *const CStr as *const PropName) } + } +} + +impl core::convert::AsRef<CStr> for PropertyName { + #[inline] + fn as_ref(&self) -> &CStr { + self.as_c_str() + } +} + +impl core::convert::AsRef<str> for PropertyName { + #[inline] + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl std::borrow::Borrow<PropName> for PropertyName { + #[inline] + fn borrow(&self) -> &PropName { + self + } +} + +impl core::fmt::Display for PropertyName { + #[inline] + fn fmt(&self, fmtr: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.as_str().fmt(fmtr) + } +} + +impl core::fmt::Debug for PropertyName { + #[inline] + fn fmt(&self, fmtr: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.as_str().fmt(fmtr) + } +} + +impl core::cmp::PartialEq<CString> for PropertyName { + #[inline] + fn eq(&self, other: &CString) -> bool { + self.as_c_str().eq(other.as_c_str()) + } +} + +impl core::cmp::PartialEq<String> for PropertyName { + #[inline] + fn eq(&self, other: &String) -> bool { + self.as_str().eq(other.as_str()) + } +} + +impl core::cmp::PartialEq<PropertyName> for CString { + #[inline] + fn eq(&self, other: &PropertyName) -> bool { + self.as_c_str().eq(other.as_c_str()) + } +} + +impl core::cmp::PartialEq<PropertyName> for String { + #[inline] + fn eq(&self, other: &PropertyName) -> bool { + self.as_str().eq(other.as_str()) + } +} + +impl CStrLike for PropertyName { + type Baked = CString; + type Error = std::convert::Infallible; + + #[inline] + fn bake(self) -> Result<Self::Baked, Self::Error> { + Ok(self.0) + } + + #[inline] + fn into_c_string(self) -> Result<CString, Self::Error> { + Ok(self.0) + } +} + +impl<'a> CStrLike for &'a PropertyName { + type Baked = &'a CStr; + type Error = std::convert::Infallible; + + #[inline] + fn bake(self) -> Result<Self::Baked, Self::Error> { + Ok(self.as_c_str()) + } + + #[inline] + fn into_c_string(self) -> Result<CString, Self::Error> { + Ok(self.0.clone()) + } +} + +/// Constructs a property name for an ‘at level’ property. +/// +/// `name` is the infix of the property name (e.g. `"num-files-at-level"`) and +/// `level` is level to get statistics of. The property name is constructed as +/// `"rocksdb.<name><level>"`. +/// +/// Expects `name` not to contain any interior nul bytes. +pub(crate) unsafe fn level_property(name: &str, level: usize) -> PropertyName { + let bytes = format!("rocksdb.{name}{level}\0").into_bytes(); + // SAFETY: We’re appending terminating nul and caller promises `name` has no + // interior nul bytes. + PropertyName::from_vec_with_nul_unchecked(bytes) +} + +#[test] +fn sanity_checks() { + let want = "rocksdb.cfstats-no-file-histogram"; + assert_eq!(want, crate::properties::CFSTATS_NO_FILE_HISTOGRAM); + + let want = "rocksdb.num-files-at-level5"; + assert_eq!(want, &*crate::properties::num_files_at_level(5)); +} + +#[test] +#[should_panic] +fn test_interior_nul() { + PropName::new_unwrap("interior nul\0\0"); +} + +#[test] +#[should_panic] +fn test_non_nul_terminated() { + PropName::new_unwrap("no nul terminator"); +} diff --git a/src/properties.rs b/src/properties.rs index 0f70123..f80ca2d 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -3,22 +3,19 @@ //! Full list of valid properties and descriptions pulled from //! [here](https:///github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L428-L634). -use std::ffi::{CStr, CString}; +use crate::prop_name::level_property; +pub use crate::prop_name::{PropName, PropertyName}; macro_rules! property { ($suffix: literal) => { - // SAFETY: We’re appending terminating NUL byte and this macro is always - // called with values without interior NUL bytes. - unsafe { - CStr::from_bytes_with_nul_unchecked(concat!("rocksdb.", $suffix, "\0").as_bytes()) - } + PropName::new_unwrap(concat!("rocksdb.", $suffix, "\0")) }; } /// "rocksdb.num-files-at-level<N>" - returns string containing the number /// of files at level <N>, where <N> is an ASCII representation of a /// level number (e.g., "0"). -pub fn num_files_at_level(level: usize) -> CString { +pub fn num_files_at_level(level: usize) -> PropertyName { unsafe { level_property("num-files-at-level", level) } } @@ -27,22 +24,22 @@ pub fn num_files_at_level(level: usize) -> CString { /// representation of a level number (e.g., "0"). Here, compression /// ratio is defined as uncompressed data size / compressed file size. /// Returns "-1.0" if no open files at level <N>. -pub fn compression_ratio_at_level(level: usize) -> CString { +pub fn compression_ratio_at_level(level: usize) -> PropertyName { unsafe { level_property("compression-ratio-at-level", level) } } /// "rocksdb.stats" - returns a multi-line string containing the data /// described by kCFStats followed by the data described by kDBStats. -pub const STATS: &CStr = property!("stats"); +pub const STATS: &PropName = property!("stats"); /// "rocksdb.sstables" - returns a multi-line string summarizing current /// SST files. -pub const SSTABLES: &CStr = property!("sstables"); +pub const SSTABLES: &PropName = property!("sstables"); /// "rocksdb.cfstats" - Both of "rocksdb.cfstats-no-file-histogram" and /// "rocksdb.cf-file-histogram" together. See below for description /// of the two. -pub const CFSTATS: &CStr = property!("CFSTATS"); +pub const CFSTATS: &PropName = property!("CFSTATS"); /// "rocksdb.cfstats-no-file-histogram" - returns a multi-line string with /// general columm family stats per-level over db's lifetime ("L<n>"), @@ -52,200 +49,178 @@ pub const CFSTATS: &CStr = property!("CFSTATS"); /// In this case there will a pair of string to array of double for /// each level as well as for "Sum". "Int" stats will not be affected /// when this form of stats are retrieved. -pub const CFSTATS_NO_FILE_HISTOGRAM: &CStr = property!("cfstats-no-file-histogram"); +pub const CFSTATS_NO_FILE_HISTOGRAM: &PropName = property!("cfstats-no-file-histogram"); /// "rocksdb.cf-file-histogram" - print out how many file reads to every /// level, as well as the histogram of latency of single requests. -pub const CF_FILE_HISTOGRAM: &CStr = property!("cf-file-histogram"); +pub const CF_FILE_HISTOGRAM: &PropName = property!("cf-file-histogram"); /// "rocksdb.dbstats" - returns a multi-line string with general database /// stats, both cumulative (over the db's lifetime) and interval (since /// the last retrieval of kDBStats). -pub const DBSTATS: &CStr = property!("dbstats"); +pub const DBSTATS: &PropName = property!("dbstats"); /// "rocksdb.levelstats" - returns multi-line string containing the number /// of files per level and total size of each level (MB). -pub const LEVELSTATS: &CStr = property!("levelstats"); +pub const LEVELSTATS: &PropName = property!("levelstats"); /// "rocksdb.num-immutable-mem-table" - returns number of immutable /// memtables that have not yet been flushed. -pub const NUM_IMMUTABLE_MEM_TABLE: &CStr = property!("num-immutable-mem-table"); +pub const NUM_IMMUTABLE_MEM_TABLE: &PropName = property!("num-immutable-mem-table"); /// "rocksdb.num-immutable-mem-table-flushed" - returns number of immutable /// memtables that have already been flushed. -pub const NUM_IMMUTABLE_MEM_TABLE_FLUSHED: &CStr = property!("num-immutable-mem-table-flushed"); +pub const NUM_IMMUTABLE_MEM_TABLE_FLUSHED: &PropName = property!("num-immutable-mem-table-flushed"); /// "rocksdb.mem-table-flush-pending" - returns 1 if a memtable flush is /// pending; otherwise, returns 0. -pub const MEM_TABLE_FLUSH_PENDING: &CStr = property!("mem-table-flush-pending"); +pub const MEM_TABLE_FLUSH_PENDING: &PropName = property!("mem-table-flush-pending"); /// "rocksdb.num-running-flushes" - returns the number of currently running /// flushes. -pub const NUM_RUNNING_FLUSHES: &CStr = property!("num-running-flushes"); +pub const NUM_RUNNING_FLUSHES: &PropName = property!("num-running-flushes"); /// "rocksdb.compaction-pending" - returns 1 if at least one compaction is /// pending; otherwise, returns 0. -pub const COMPACTION_PENDING: &CStr = property!("compaction-pending"); +pub const COMPACTION_PENDING: &PropName = property!("compaction-pending"); /// "rocksdb.num-running-compactions" - returns the number of currently /// running compactions. -pub const NUM_RUNNING_COMPACTIONS: &CStr = property!("num-running-compactions"); +pub const NUM_RUNNING_COMPACTIONS: &PropName = property!("num-running-compactions"); /// "rocksdb.background-errors" - returns accumulated number of background /// errors. -pub const BACKGROUND_ERRORS: &CStr = property!("background-errors"); +pub const BACKGROUND_ERRORS: &PropName = property!("background-errors"); /// "rocksdb.cur-size-active-mem-table" - returns approximate size of active /// memtable (bytes). -pub const CUR_SIZE_ACTIVE_MEM_TABLE: &CStr = property!("cur-size-active-mem-table"); +pub const CUR_SIZE_ACTIVE_MEM_TABLE: &PropName = property!("cur-size-active-mem-table"); /// "rocksdb.cur-size-all-mem-tables" - returns approximate size of active /// and unflushed immutable memtables (bytes). -pub const CUR_SIZE_ALL_MEM_TABLES: &CStr = property!("cur-size-all-mem-tables"); +pub const CUR_SIZE_ALL_MEM_TABLES: &PropName = property!("cur-size-all-mem-tables"); /// "rocksdb.size-all-mem-tables" - returns approximate size of active, /// unflushed immutable, and pinned immutable memtables (bytes). -pub const SIZE_ALL_MEM_TABLES: &CStr = property!("size-all-mem-tables"); +pub const SIZE_ALL_MEM_TABLES: &PropName = property!("size-all-mem-tables"); /// "rocksdb.num-entries-active-mem-table" - returns total number of entries /// in the active memtable. -pub const NUM_ENTRIES_ACTIVE_MEM_TABLE: &CStr = property!("num-entries-active-mem-table"); +pub const NUM_ENTRIES_ACTIVE_MEM_TABLE: &PropName = property!("num-entries-active-mem-table"); /// "rocksdb.num-entries-imm-mem-tables" - returns total number of entries /// in the unflushed immutable memtables. -pub const NUM_ENTRIES_IMM_MEM_TABLES: &CStr = property!("num-entries-imm-mem-tables"); +pub const NUM_ENTRIES_IMM_MEM_TABLES: &PropName = property!("num-entries-imm-mem-tables"); /// "rocksdb.num-deletes-active-mem-table" - returns total number of delete /// entries in the active memtable. -pub const NUM_DELETES_ACTIVE_MEM_TABLE: &CStr = property!("num-deletes-active-mem-table"); +pub const NUM_DELETES_ACTIVE_MEM_TABLE: &PropName = property!("num-deletes-active-mem-table"); /// "rocksdb.num-deletes-imm-mem-tables" - returns total number of delete /// entries in the unflushed immutable memtables. -pub const NUM_DELETES_IMM_MEM_TABLES: &CStr = property!("num-deletes-imm-mem-tables"); +pub const NUM_DELETES_IMM_MEM_TABLES: &PropName = property!("num-deletes-imm-mem-tables"); /// "rocksdb.estimate-num-keys" - returns estimated number of total keys in /// the active and unflushed immutable memtables and storage. -pub const ESTIMATE_NUM_KEYS: &CStr = property!("estimate-num-keys"); +pub const ESTIMATE_NUM_KEYS: &PropName = property!("estimate-num-keys"); /// "rocksdb.estimate-table-readers-mem" - returns estimated memory used for /// reading SST tables, excluding memory used in block cache (e.g., /// filter and index blocks). -pub const ESTIMATE_TABLE_READERS_MEM: &CStr = property!("estimate-table-readers-mem"); +pub const ESTIMATE_TABLE_READERS_MEM: &PropName = property!("estimate-table-readers-mem"); /// "rocksdb.is-file-deletions-enabled" - returns 0 if deletion of obsolete /// files is enabled; otherwise, returns a non-zero number. -pub const IS_FILE_DELETIONS_ENABLED: &CStr = property!("is-file-deletions-enabled"); +pub const IS_FILE_DELETIONS_ENABLED: &PropName = property!("is-file-deletions-enabled"); /// "rocksdb.num-snapshots" - returns number of unreleased snapshots of the /// database. -pub const NUM_SNAPSHOTS: &CStr = property!("num-snapshots"); +pub const NUM_SNAPSHOTS: &PropName = property!("num-snapshots"); /// "rocksdb.oldest-snapshot-time" - returns number representing unix /// timestamp of oldest unreleased snapshot. -pub const OLDEST_SNAPSHOT_TIME: &CStr = property!("oldest-snapshot-time"); +pub const OLDEST_SNAPSHOT_TIME: &PropName = property!("oldest-snapshot-time"); /// "rocksdb.num-live-versions" - returns number of live versions. `Version` /// is an internal data structure. See version_set.h for details. More /// live versions often mean more SST files are held from being deleted, /// by iterators or unfinished compactions. -pub const NUM_LIVE_VERSIONS: &CStr = property!("num-live-versions"); +pub const NUM_LIVE_VERSIONS: &PropName = property!("num-live-versions"); /// "rocksdb.current-super-version-number" - returns number of current LSM /// version. It is a uint64_t integer number, incremented after there is /// any change to the LSM tree. The number is not preserved after restarting /// the DB. After DB restart, it will start from 0 again. -pub const CURRENT_SUPER_VERSION_NUMBER: &CStr = property!("current-super-version-number"); +pub const CURRENT_SUPER_VERSION_NUMBER: &PropName = property!("current-super-version-number"); /// "rocksdb.estimate-live-data-size" - returns an estimate of the amount of /// live data in bytes. -pub const ESTIMATE_LIVE_DATA_SIZE: &CStr = property!("estimate-live-data-size"); +pub const ESTIMATE_LIVE_DATA_SIZE: &PropName = property!("estimate-live-data-size"); /// "rocksdb.min-log-number-to-keep" - return the minimum log number of the /// log files that should be kept. -pub const MIN_LOG_NUMBER_TO_KEEP: &CStr = property!("min-log-number-to-keep"); +pub const MIN_LOG_NUMBER_TO_KEEP: &PropName = property!("min-log-number-to-keep"); /// "rocksdb.min-obsolete-sst-number-to-keep" - return the minimum file /// number for an obsolete SST to be kept. The max value of `uint64_t` /// will be returned if all obsolete files can be deleted. -pub const MIN_OBSOLETE_SST_NUMBER_TO_KEEP: &CStr = property!("min-obsolete-sst-number-to-keep"); +pub const MIN_OBSOLETE_SST_NUMBER_TO_KEEP: &PropName = property!("min-obsolete-sst-number-to-keep"); /// "rocksdb.total-sst-files-size" - returns total size (bytes) of all SST /// files. /// WARNING: may slow down online queries if there are too many files. -pub const TOTAL_SST_FILES_SIZE: &CStr = property!("total-sst-files-size"); +pub const TOTAL_SST_FILES_SIZE: &PropName = property!("total-sst-files-size"); /// "rocksdb.live-sst-files-size" - returns total size (bytes) of all SST /// files belong to the latest LSM tree. -pub const LIVE_SST_FILES_SIZE: &CStr = property!("live-sst-files-size"); +pub const LIVE_SST_FILES_SIZE: &PropName = property!("live-sst-files-size"); /// "rocksdb.base-level" - returns number of level to which L0 data will be /// compacted. -pub const BASE_LEVEL: &CStr = property!("base-level"); +pub const BASE_LEVEL: &PropName = property!("base-level"); /// "rocksdb.estimate-pending-compaction-bytes" - returns estimated total /// number of bytes compaction needs to rewrite to get all levels down /// to under target size. Not valid for other compactions than level- /// based. -pub const ESTIMATE_PENDING_COMPACTION_BYTES: &CStr = property!("estimate-pending-compaction-bytes"); +pub const ESTIMATE_PENDING_COMPACTION_BYTES: &PropName = + property!("estimate-pending-compaction-bytes"); /// "rocksdb.aggregated-table-properties" - returns a string representation /// of the aggregated table properties of the target column family. -pub const AGGREGATED_TABLE_PROPERTIES: &CStr = property!("aggregated-table-properties"); +pub const AGGREGATED_TABLE_PROPERTIES: &PropName = property!("aggregated-table-properties"); /// "rocksdb.aggregated-table-properties-at-level<N>", same as the previous /// one but only returns the aggregated table properties of the /// specified level "N" at the target column family. -pub fn aggregated_table_properties_at_level(level: usize) -> CString { +pub fn aggregated_table_properties_at_level(level: usize) -> PropertyName { unsafe { level_property("aggregated-table-properties-at-level", level) } } /// "rocksdb.actual-delayed-write-rate" - returns the current actual delayed /// write rate. 0 means no delay. -pub const ACTUAL_DELAYED_WRITE_RATE: &CStr = property!("actual-delayed-write-rate"); +pub const ACTUAL_DELAYED_WRITE_RATE: &PropName = property!("actual-delayed-write-rate"); /// "rocksdb.is-write-stopped" - Return 1 if write has been stopped. -pub const IS_WRITE_STOPPED: &CStr = property!("is-write-stopped"); +pub const IS_WRITE_STOPPED: &PropName = property!("is-write-stopped"); /// "rocksdb.estimate-oldest-key-time" - returns an estimation of /// oldest key timestamp in the DB. Currently only available for /// FIFO compaction with /// compaction_options_fifo.allow_compaction = false. -pub const ESTIMATE_OLDEST_KEY_TIME: &CStr = property!("estimate-oldest-key-time"); +pub const ESTIMATE_OLDEST_KEY_TIME: &PropName = property!("estimate-oldest-key-time"); /// "rocksdb.block-cache-capacity" - returns block cache capacity. -pub const BLOCK_CACHE_CAPACITY: &CStr = property!("block-cache-capacity"); +pub const BLOCK_CACHE_CAPACITY: &PropName = property!("block-cache-capacity"); /// "rocksdb.block-cache-usage" - returns the memory size for the entries /// residing in block cache. -pub const BLOCK_CACHE_USAGE: &CStr = property!("block-cache-usage"); +pub const BLOCK_CACHE_USAGE: &PropName = property!("block-cache-usage"); /// "rocksdb.block-cache-pinned-usage" - returns the memory size for the /// entries being pinned. -pub const BLOCK_CACHE_PINNED_USAGE: &CStr = property!("block-cache-pinned-usage"); +pub const BLOCK_CACHE_PINNED_USAGE: &PropName = property!("block-cache-pinned-usage"); /// "rocksdb.options-statistics" - returns multi-line string /// of options.statistics -pub const OPTIONS_STATISTICS: &CStr = property!("options-statistics"); - -/// Constructs a property name for an ‘at level’ property. -/// -/// `name` is the infix of the property name (e.g. `"num-files-at-level"`) and -/// `level` is level to get statistics of. The property name is constructed as -/// `"rocksdb.<name><level>"`. -/// -/// Expects `name` not to contain any interior NUL bytes. -unsafe fn level_property(name: &str, level: usize) -> CString { - let bytes = format!("rocksdb.{name}{level}\0").into_bytes(); - // SAFETY: We’re appending terminating NUL and all our call sites pass - // a string without interior NUL bytes. - CString::from_vec_with_nul_unchecked(bytes) -} - -#[test] -fn sanity_checks() { - let want = CString::new("rocksdb.cfstats-no-file-histogram".to_string()).unwrap(); - assert_eq!(want.as_c_str(), CFSTATS_NO_FILE_HISTOGRAM); - - let want = CString::new("rocksdb.num-files-at-level5".to_string()).unwrap(); - assert_eq!(want, num_files_at_level(5)); -} +pub const OPTIONS_STATISTICS: &PropName = property!("options-statistics"); diff --git a/tests/test_property.rs b/tests/test_property.rs index 5225305..9782bd2 100644 --- a/tests/test_property.rs +++ b/tests/test_property.rs @@ -31,14 +31,14 @@ fn property_test() { { let db = DB::open_default(&n).unwrap(); - let prop_name: std::ffi::CString = properties::STATS.to_owned(); + let prop_name: properties::PropertyName = properties::STATS.to_owned(); let value = db.property_value(&prop_name).unwrap().unwrap(); assert!(value.contains("Stats")); } { let db = DB::open_default(&n).unwrap(); - let prop_name: String = properties::STATS.to_owned().into_string().unwrap(); + let prop_name: String = properties::STATS.to_owned().into_string(); let value = db.property_value(&prop_name).unwrap().unwrap(); assert!(value.contains("Stats")); } |