From 2fdf020645f441b03c52689a38e087cbef045b8a Mon Sep 17 00:00:00 2001 From: Felix Krull Date: Mon, 2 Sep 2019 15:56:52 +0200 Subject: [PATCH] checksum: implement conversion from string and to base64 --- rust-bindings/rust/src/checksum.rs | 92 +++++++++++++++++++++--- rust-bindings/rust/src/collection_ref.rs | 2 - 2 files changed, 83 insertions(+), 11 deletions(-) diff --git a/rust-bindings/rust/src/checksum.rs b/rust-bindings/rust/src/checksum.rs index b2f48307..2dc25031 100644 --- a/rust-bindings/rust/src/checksum.rs +++ b/rust-bindings/rust/src/checksum.rs @@ -1,21 +1,64 @@ -use glib::translate::{from_glib_full, FromGlibPtrFull}; +use glib::translate::{from_glib_full, FromGlibPtrFull, ToGlibPtr}; use glib::GString; use glib_sys::{g_free, gpointer}; use std::fmt; +const BYTES_BUF_SIZE: usize = ostree_sys::OSTREE_SHA256_DIGEST_LEN as usize; +const HEX_BUF_SIZE: usize = (ostree_sys::OSTREE_SHA256_STRING_LEN + 1) as usize; +const B64_BUF_SIZE: usize = 44; + +/// A binary SHA256 checksum. +#[derive(Debug)] pub struct Checksum { - bytes: *mut [u8; 32], + bytes: *mut [u8; BYTES_BUF_SIZE], } impl Checksum { - pub(crate) unsafe fn new(bytes: *mut [u8; 32]) -> Checksum { + /// Create a `Checksum` value, taking ownership of the given memory location. + /// + /// # Safety + /// `bytes` must point to a fully initialized 32-byte memory location that is freeable with + /// `g_free` (this is e.g. the case if the memory was allocated with `g_malloc`). The value + /// takes ownership of the memory, i.e. the memory is freed when the value is dropped. The + /// memory must not be freed by other code. + unsafe fn new(bytes: *mut [u8; BYTES_BUF_SIZE]) -> Checksum { assert!(!bytes.is_null()); Checksum { bytes } } + /// Create a `Checksum` from a hexadecimal SHA256 string. + /// + /// Unfortunately, the underlying libostree function has no way to report parsing errors. If the + /// string is not a valid SHA256 string, the program will abort! + // TODO: implement by hand to avoid stupid assertions? + pub fn from_string(checksum: &str) -> Checksum { + unsafe { + from_glib_full(ostree_sys::ostree_checksum_to_bytes( + checksum.to_glib_none().0, + )) + } + } + + /// Return checksum as a hex digest string. fn to_gstring(&self) -> GString { unsafe { from_glib_full(ostree_sys::ostree_checksum_from_bytes(self.bytes)) } } + + /// Convert checksum to base64. + pub fn to_base64(&self) -> String { + let mut buf: Vec = Vec::with_capacity(B64_BUF_SIZE); + unsafe { + ostree_sys::ostree_checksum_b64_inplace_from_bytes( + self.bytes, + buf.as_mut_ptr() as *mut i8, + ); + let len = libc::strlen(buf.as_mut_ptr() as *const i8); + // Assumption: (len + 1) valid bytes are in the buffer. + buf.set_len(len); + // Assumption: all characters are ASCII, ergo valid UTF-8. + String::from_utf8_unchecked(buf) + } + } } impl Drop for Checksum { @@ -26,21 +69,21 @@ impl Drop for Checksum { } } -impl FromGlibPtrFull<*mut [u8; 32]> for Checksum { - unsafe fn from_glib_full(ptr: *mut [u8; 32]) -> Self { +impl FromGlibPtrFull<*mut [u8; BYTES_BUF_SIZE]> for Checksum { + unsafe fn from_glib_full(ptr: *mut [u8; BYTES_BUF_SIZE]) -> Self { Checksum::new(ptr) } } -impl FromGlibPtrFull<*mut [*mut u8; 32]> for Checksum { - unsafe fn from_glib_full(ptr: *mut [*mut u8; 32]) -> Self { - Checksum::new(ptr as *mut u8 as *mut [u8; 32]) +impl FromGlibPtrFull<*mut [*mut u8; BYTES_BUF_SIZE]> for Checksum { + unsafe fn from_glib_full(ptr: *mut [*mut u8; BYTES_BUF_SIZE]) -> Self { + Checksum::new(ptr as *mut u8 as *mut [u8; BYTES_BUF_SIZE]) } } impl FromGlibPtrFull<*mut u8> for Checksum { unsafe fn from_glib_full(ptr: *mut u8) -> Self { - Checksum::new(ptr as *mut [u8; 32]) + Checksum::new(ptr as *mut [u8; BYTES_BUF_SIZE]) } } @@ -49,3 +92,34 @@ impl fmt::Display for Checksum { write!(f, "{}", self.to_gstring()) } } + +#[cfg(test)] +mod tests { + use super::*; + use glib_sys::g_malloc0; + + const CHECKSUM_STRING: &str = + "bf875306783efdc5bcab37ea10b6ca4e9b6aea8b94580d0ca94af120565c0e8a"; + + #[test] + fn should_create_checksum_from_bytes() { + let bytes = unsafe { g_malloc0(BYTES_BUF_SIZE) } as *mut u8; + let checksum: Checksum = unsafe { from_glib_full(bytes) }; + assert_eq!(checksum.to_string(), "00".repeat(BYTES_BUF_SIZE)); + } + + #[test] + fn should_parse_checksum_string_to_bytes() { + let csum = Checksum::from_string(CHECKSUM_STRING); + assert_eq!(csum.to_string(), CHECKSUM_STRING); + } + + #[test] + fn should_convert_checksum_to_base64() { + let csum = Checksum::from_string(CHECKSUM_STRING); + assert_eq!( + csum.to_base64(), + "v4dTBng+_cW8qzfqELbKTptq6ouUWA0MqUrxIFZcDoo" + ); + } +} diff --git a/rust-bindings/rust/src/collection_ref.rs b/rust-bindings/rust/src/collection_ref.rs index 428c0b56..b85a3f4c 100644 --- a/rust-bindings/rust/src/collection_ref.rs +++ b/rust-bindings/rust/src/collection_ref.rs @@ -19,8 +19,6 @@ impl AsNonnullPtr for *mut T { } } -// TODO: these methods are unsound if you change the underlying OstreeCollectionRef from C. Is that -// a problem? impl CollectionRef { /// Get the collection ID from this `CollectionRef`. ///