Fix memory leak when reading properties and avoid memory allocation (#622)

master
Michal Nazarewicz 2 years ago committed by GitHub
parent 59434f93ed
commit ef87246798
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 123
      src/db.rs

@ -1558,13 +1558,21 @@ impl<T: ThreadMode> DBWithThreadMode<T> {
Ok(()) Ok(())
} }
/// Retrieves a RocksDB property by name. /// Implementation for property_value et al methods.
/// ///
/// Full list of properties could be find /// `name` is the name of the property. It will be converted into a CString
/// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L428-L634). /// and passed to `get_property` as argument. `get_property` reads the
pub fn property_value(&self, name: &str) -> Result<Option<String>, Error> { /// specified property and either returns NULL or a pointer to a C allocated
let prop_name = match CString::new(name) { /// string; this method takes ownership of that string and will free it at
Ok(c) => c, /// the end. That string is parsed using `parse` callback which produces
/// the returned result.
fn property_value_impl<R>(
name: &str,
get_property: impl FnOnce(*const c_char) -> *mut c_char,
parse: impl FnOnce(&str) -> Result<R, Error>,
) -> Result<Option<R>, Error> {
let value = match CString::new(name) {
Ok(prop_name) => get_property(prop_name.as_ptr()),
Err(e) => { Err(e) => {
return Err(Error::new(format!( return Err(Error::new(format!(
"Failed to convert property name to CString: {}", "Failed to convert property name to CString: {}",
@ -1572,26 +1580,32 @@ impl<T: ThreadMode> DBWithThreadMode<T> {
))); )));
} }
}; };
unsafe {
let value = ffi::rocksdb_property_value(self.inner, prop_name.as_ptr());
if value.is_null() { if value.is_null() {
return Ok(None); return Ok(None);
} }
let result = match unsafe { CStr::from_ptr(value) }.to_str() {
let str_value = match CStr::from_ptr(value).to_str() { Ok(s) => parse(s).map(|value| Some(value)),
Ok(s) => s.to_owned(), Err(e) => Err(Error::new(format!(
Err(e) => {
return Err(Error::new(format!(
"Failed to convert property value to string: {}", "Failed to convert property value to string: {}",
e e
))); ))),
}
}; };
unsafe {
libc::free(value as *mut c_void); libc::free(value as *mut c_void);
Ok(Some(str_value))
} }
result
}
/// Retrieves a RocksDB property by name.
///
/// Full list of properties could be find
/// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L428-L634).
pub fn property_value(&self, name: &str) -> Result<Option<String>, Error> {
Self::property_value_impl(
name,
|prop_name| unsafe { ffi::rocksdb_property_value(self.inner, prop_name) },
|str_value| Ok(str_value.to_owned()),
)
} }
/// Retrieves a RocksDB property by name, for a specific column family. /// Retrieves a RocksDB property by name, for a specific column family.
@ -1603,35 +1617,22 @@ impl<T: ThreadMode> DBWithThreadMode<T> {
cf: &impl AsColumnFamilyRef, cf: &impl AsColumnFamilyRef,
name: &str, name: &str,
) -> Result<Option<String>, Error> { ) -> Result<Option<String>, Error> {
let prop_name = match CString::new(name) { Self::property_value_impl(
Ok(c) => c, name,
Err(e) => { |prop_name| unsafe {
return Err(Error::new(format!( ffi::rocksdb_property_value_cf(self.inner, cf.inner(), prop_name)
"Failed to convert property name to CString: {}", },
e |str_value| Ok(str_value.to_owned()),
))); )
}
};
unsafe {
let value = ffi::rocksdb_property_value_cf(self.inner, cf.inner(), prop_name.as_ptr());
if value.is_null() {
return Ok(None);
}
let str_value = match CStr::from_ptr(value).to_str() {
Ok(s) => s.to_owned(),
Err(e) => {
return Err(Error::new(format!(
"Failed to convert property value to string: {}",
e
)));
} }
};
libc::free(value as *mut c_void); fn parse_property_int_value(value: &str) -> Result<u64, Error> {
Ok(Some(str_value)) value.parse::<u64>().map_err(|err| {
} Error::new(format!(
"Failed to convert property value {} to int: {}",
value, err
))
})
} }
/// Retrieves a RocksDB property and casts it to an integer. /// Retrieves a RocksDB property and casts it to an integer.
@ -1639,17 +1640,11 @@ impl<T: ThreadMode> DBWithThreadMode<T> {
/// Full list of properties that return int values could be find /// Full list of properties that return int values could be find
/// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L654-L689). /// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L654-L689).
pub fn property_int_value(&self, name: &str) -> Result<Option<u64>, Error> { pub fn property_int_value(&self, name: &str) -> Result<Option<u64>, Error> {
match self.property_value(name) { Self::property_value_impl(
Ok(Some(value)) => match value.parse::<u64>() { name,
Ok(int_value) => Ok(Some(int_value)), |prop_name| unsafe { ffi::rocksdb_property_value(self.inner, prop_name) },
Err(e) => Err(Error::new(format!( Self::parse_property_int_value,
"Failed to convert property value to int: {}", )
e
))),
},
Ok(None) => Ok(None),
Err(e) => Err(e),
}
} }
/// Retrieves a RocksDB property for a specific column family and casts it to an integer. /// Retrieves a RocksDB property for a specific column family and casts it to an integer.
@ -1661,17 +1656,13 @@ impl<T: ThreadMode> DBWithThreadMode<T> {
cf: &impl AsColumnFamilyRef, cf: &impl AsColumnFamilyRef,
name: &str, name: &str,
) -> Result<Option<u64>, Error> { ) -> Result<Option<u64>, Error> {
match self.property_value_cf(cf, name) { Self::property_value_impl(
Ok(Some(value)) => match value.parse::<u64>() { name,
Ok(int_value) => Ok(Some(int_value)), |prop_name| unsafe {
Err(e) => Err(Error::new(format!( ffi::rocksdb_property_value_cf(self.inner, cf.inner(), prop_name)
"Failed to convert property value to int: {}",
e
))),
}, },
Ok(None) => Ok(None), Self::parse_property_int_value,
Err(e) => Err(e), )
}
} }
/// The sequence number of the most recent transaction. /// The sequence number of the most recent transaction.

Loading…
Cancel
Save