automerge/rust/automerge-c/src/byte_span.rs
Conrad Irwin b05c9e83a4
Use AMbyteSpan for AM{list,map}PutBytes (#464)
* Use AMbyteSpan for byte values

Before this change there was an inconsistency between AMmapPutString
(which took an AMbyteSpan) and AMmapPutBytes (which took a pointer +
length).

Either is fine, but we should do the same in both places. I chose this
path to make it clear that the value passed in was an automerge value,
and to be symmetric with AMvalue.bytes when you do an AMmapGet().

I did not update other APIs (like load) that take a pointer + length, as
that is idiomatic usage for C, and these functions are not operating on
byte values stored in automerge.
2022-12-09 16:11:23 +00:00

141 lines
3.5 KiB
Rust

use automerge as am;
use libc::strlen;
use std::convert::TryFrom;
use std::os::raw::c_char;
macro_rules! to_str {
($span:expr) => {{
let result: Result<&str, am::AutomergeError> = (&$span).try_into();
match result {
Ok(s) => s,
Err(e) => return AMresult::err(&e.to_string()).into(),
}
}};
}
pub(crate) use to_str;
/// \struct AMbyteSpan
/// \installed_headerfile
/// \brief A view onto a contiguous sequence of bytes.
#[repr(C)]
pub struct AMbyteSpan {
/// A pointer to an array of bytes.
/// \attention <b>NEVER CALL `free()` ON \p src!</b>
/// \warning \p src is only valid until the `AMfree()` function is called
/// on the `AMresult` struct that stores the array of bytes to
/// which it points.
pub src: *const u8,
/// The number of bytes in the array.
pub count: usize,
}
impl AMbyteSpan {
pub fn is_null(&self) -> bool {
self.src.is_null()
}
}
impl Default for AMbyteSpan {
fn default() -> Self {
Self {
src: std::ptr::null(),
count: 0,
}
}
}
impl PartialEq for AMbyteSpan {
fn eq(&self, other: &Self) -> bool {
if self.count != other.count {
return false;
} else if self.src == other.src {
return true;
}
let slice = unsafe { std::slice::from_raw_parts(self.src, self.count) };
let other_slice = unsafe { std::slice::from_raw_parts(other.src, other.count) };
slice == other_slice
}
}
impl Eq for AMbyteSpan {}
impl From<&am::ActorId> for AMbyteSpan {
fn from(actor: &am::ActorId) -> Self {
let slice = actor.to_bytes();
Self {
src: slice.as_ptr(),
count: slice.len(),
}
}
}
impl From<&mut am::ActorId> for AMbyteSpan {
fn from(actor: &mut am::ActorId) -> Self {
let slice = actor.to_bytes();
Self {
src: slice.as_ptr(),
count: slice.len(),
}
}
}
impl From<*const c_char> for AMbyteSpan {
fn from(cs: *const c_char) -> Self {
if !cs.is_null() {
Self {
src: cs as *const u8,
count: unsafe { strlen(cs) },
}
} else {
Self::default()
}
}
}
impl From<&am::ChangeHash> for AMbyteSpan {
fn from(change_hash: &am::ChangeHash) -> Self {
Self {
src: change_hash.0.as_ptr(),
count: change_hash.0.len(),
}
}
}
impl From<&[u8]> for AMbyteSpan {
fn from(slice: &[u8]) -> Self {
Self {
src: slice.as_ptr(),
count: slice.len(),
}
}
}
impl TryFrom<&AMbyteSpan> for &str {
type Error = am::AutomergeError;
fn try_from(span: &AMbyteSpan) -> Result<Self, Self::Error> {
use am::AutomergeError::InvalidCharacter;
let slice = unsafe { std::slice::from_raw_parts(span.src, span.count) };
match std::str::from_utf8(slice) {
Ok(str_) => Ok(str_),
Err(e) => Err(InvalidCharacter(e.valid_up_to())),
}
}
}
/// \brief Creates an AMbyteSpan from a pointer + length
///
/// \param[in] src A pointer to a span of bytes
/// \param[in] count The number of bytes in the span
/// \return An `AMbyteSpan` struct
/// \internal
///
/// #Safety
/// AMbytes does not retain the underlying storage, so you must discard the
/// return value before freeing the bytes.
#[no_mangle]
pub unsafe extern "C" fn AMbytes(src: *const u8, count: usize) -> AMbyteSpan {
AMbyteSpan { src, count }
}