rust-rocksdb-zaidoon1/src/prop_name.rs
zaidoon 5543f28ad8 prep work for upgrading to rust 2024
running cargo fix --edition for now and keeping
only the backwards compatible changes
2025-07-23 14:26:44 -04:00

326 lines
8.4 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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` isnt terminated by a nul byte or contains
/// interior nul bytes.
pub(crate) const fn new_unwrap(value: &str) -> &Self {
if let Some((&0, bytes)) = value.as_bytes().split_last() {
let mut idx = 0;
while idx < bytes.len() {
assert!(bytes[idx] != 0, "input contained interior nul byte");
idx += 1;
}
// SAFETY: 1. Weve just verified `value` is a nul-terminated with no
// interior nul bytes and since its `str` its 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());
&*(std::ptr::from_ref::<CStr>(value) as *const Self)
}
} else {
panic!("input was not nul-terminated");
}
}
/// 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 {
unsafe {
// 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 { &*(std::ptr::from_ref::<CStr>(self.0.as_c_str()) 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 {
unsafe {
let bytes = format!("rocksdb.{name}{level}\0").into_bytes();
// SAFETY: Were 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(expected = "input contained interior nul byte")]
fn test_interior_nul() {
PropName::new_unwrap("interior nul\0\0");
}
#[test]
#[should_panic(expected = "input was not nul-terminated")]
fn test_non_nul_terminated() {
PropName::new_unwrap("no nul terminator");
}