automerge/rust/automerge-c/src/change.rs
2022-11-27 23:52:47 -08:00

356 lines
9.8 KiB
Rust

use automerge as am;
use std::cell::RefCell;
use crate::byte_span::AMbyteSpan;
use crate::change_hashes::AMchangeHashes;
use crate::result::{to_result, AMresult};
macro_rules! to_change {
($handle:expr) => {{
let handle = $handle.as_ref();
match handle {
Some(b) => b,
None => return AMresult::err("Invalid AMchange pointer").into(),
}
}};
}
/// \struct AMchange
/// \installed_headerfile
/// \brief A group of operations performed by an actor.
#[derive(Eq, PartialEq)]
pub struct AMchange {
body: *mut am::Change,
changehash: RefCell<Option<am::ChangeHash>>,
}
impl AMchange {
pub fn new(change: &mut am::Change) -> Self {
Self {
body: change,
changehash: Default::default(),
}
}
pub fn message(&self) -> AMbyteSpan {
if let Some(message) = unsafe { (*self.body).message() } {
return message.as_str().as_bytes().into();
}
Default::default()
}
pub fn hash(&self) -> AMbyteSpan {
let mut changehash = self.changehash.borrow_mut();
if let Some(changehash) = changehash.as_ref() {
changehash.into()
} else {
let hash = unsafe { (*self.body).hash() };
let ptr = changehash.insert(hash);
AMbyteSpan {
src: ptr.0.as_ptr(),
count: hash.as_ref().len(),
}
}
}
}
impl AsMut<am::Change> for AMchange {
fn as_mut(&mut self) -> &mut am::Change {
unsafe { &mut *self.body }
}
}
impl AsRef<am::Change> for AMchange {
fn as_ref(&self) -> &am::Change {
unsafe { &*self.body }
}
}
/// \memberof AMchange
/// \brief Gets the first referenced actor identifier in a change.
///
/// \param[in] change A pointer to an `AMchange` struct.
/// \pre \p change `!= NULL`.
/// \return A pointer to an `AMresult` struct containing a pointer to an
/// `AMactorId` struct.
/// \warning The returned `AMresult` struct must be deallocated with `AMfree()`
/// in order to prevent a memory leak.
/// \internal
///
/// # Safety
/// change must be a valid pointer to an AMchange
#[no_mangle]
pub unsafe extern "C" fn AMchangeActorId(change: *const AMchange) -> *mut AMresult {
let change = to_change!(change);
to_result(Ok::<am::ActorId, am::AutomergeError>(
change.as_ref().actor_id().clone(),
))
}
/// \memberof AMchange
/// \brief Compresses the raw bytes of a change.
///
/// \param[in,out] change A pointer to an `AMchange` struct.
/// \pre \p change `!= NULL`.
/// \internal
///
/// # Safety
/// change must be a valid pointer to an AMchange
#[no_mangle]
pub unsafe extern "C" fn AMchangeCompress(change: *mut AMchange) {
if let Some(change) = change.as_mut() {
let _ = change.as_mut().bytes();
};
}
/// \memberof AMchange
/// \brief Gets the dependencies of a change.
///
/// \param[in] change A pointer to an `AMchange` struct.
/// \return A pointer to an `AMchangeHashes` struct or `NULL`.
/// \pre \p change `!= NULL`.
/// \internal
///
/// # Safety
/// change must be a valid pointer to an AMchange
#[no_mangle]
pub unsafe extern "C" fn AMchangeDeps(change: *const AMchange) -> AMchangeHashes {
match change.as_ref() {
Some(change) => AMchangeHashes::new(change.as_ref().deps()),
None => Default::default(),
}
}
/// \memberof AMchange
/// \brief Gets the extra bytes of a change.
///
/// \param[in] change A pointer to an `AMchange` struct.
/// \return An `AMbyteSpan` struct.
/// \pre \p change `!= NULL`.
/// \internal
///
/// # Safety
/// change must be a valid pointer to an AMchange
#[no_mangle]
pub unsafe extern "C" fn AMchangeExtraBytes(change: *const AMchange) -> AMbyteSpan {
if let Some(change) = change.as_ref() {
change.as_ref().extra_bytes().into()
} else {
Default::default()
}
}
/// \memberof AMchange
/// \brief Loads a sequence of bytes into a change.
///
/// \param[in] src A pointer to an array of bytes.
/// \param[in] count The number of bytes in \p src to load.
/// \return A pointer to an `AMresult` struct containing an `AMchange` struct.
/// \pre \p src `!= NULL`.
/// \pre `0 <` \p count `<= sizeof(`\p src`)`.
/// \warning The returned `AMresult` struct must be deallocated with `AMfree()`
/// in order to prevent a memory leak.
/// \internal
///
/// # Safety
/// src must be a byte array of size `>= count`
#[no_mangle]
pub unsafe extern "C" fn AMchangeFromBytes(src: *const u8, count: usize) -> *mut AMresult {
let mut data = Vec::new();
data.extend_from_slice(std::slice::from_raw_parts(src, count));
to_result(am::Change::from_bytes(data))
}
/// \memberof AMchange
/// \brief Gets the hash of a change.
///
/// \param[in] change A pointer to an `AMchange` struct.
/// \return A change hash as an `AMbyteSpan` struct.
/// \pre \p change `!= NULL`.
/// \internal
///
/// # Safety
/// change must be a valid pointer to an AMchange
#[no_mangle]
pub unsafe extern "C" fn AMchangeHash(change: *const AMchange) -> AMbyteSpan {
match change.as_ref() {
Some(change) => change.hash(),
None => Default::default(),
}
}
/// \memberof AMchange
/// \brief Tests the emptiness of a change.
///
/// \param[in] change A pointer to an `AMchange` struct.
/// \return A boolean.
/// \pre \p change `!= NULL`.
/// \internal
///
/// # Safety
/// change must be a valid pointer to an AMchange
#[no_mangle]
pub unsafe extern "C" fn AMchangeIsEmpty(change: *const AMchange) -> bool {
if let Some(change) = change.as_ref() {
change.as_ref().is_empty()
} else {
true
}
}
/// \memberof AMchange
/// \brief Gets the maximum operation index of a change.
///
/// \param[in] change A pointer to an `AMchange` struct.
/// \return A 64-bit unsigned integer.
/// \pre \p change `!= NULL`.
/// \internal
///
/// # Safety
/// change must be a valid pointer to an AMchange
#[no_mangle]
pub unsafe extern "C" fn AMchangeMaxOp(change: *const AMchange) -> u64 {
if let Some(change) = change.as_ref() {
change.as_ref().max_op()
} else {
u64::MAX
}
}
/// \memberof AMchange
/// \brief Gets the message of a change.
///
/// \param[in] change A pointer to an `AMchange` struct.
/// \return A UTF-8 string view as an `AMbyteSpan` struct.
/// \pre \p change `!= NULL`.
/// \internal
///
/// # Safety
/// change must be a valid pointer to an AMchange
#[no_mangle]
pub unsafe extern "C" fn AMchangeMessage(change: *const AMchange) -> AMbyteSpan {
if let Some(change) = change.as_ref() {
return change.message();
};
Default::default()
}
/// \memberof AMchange
/// \brief Gets the index of a change in the changes from an actor.
///
/// \param[in] change A pointer to an `AMchange` struct.
/// \return A 64-bit unsigned integer.
/// \pre \p change `!= NULL`.
/// \internal
///
/// # Safety
/// change must be a valid pointer to an AMchange
#[no_mangle]
pub unsafe extern "C" fn AMchangeSeq(change: *const AMchange) -> u64 {
if let Some(change) = change.as_ref() {
change.as_ref().seq()
} else {
u64::MAX
}
}
/// \memberof AMchange
/// \brief Gets the size of a change.
///
/// \param[in] change A pointer to an `AMchange` struct.
/// \return A 64-bit unsigned integer.
/// \pre \p change `!= NULL`.
/// \internal
///
/// # Safety
/// change must be a valid pointer to an AMchange
#[no_mangle]
pub unsafe extern "C" fn AMchangeSize(change: *const AMchange) -> usize {
if let Some(change) = change.as_ref() {
change.as_ref().len()
} else {
0
}
}
/// \memberof AMchange
/// \brief Gets the start operation index of a change.
///
/// \param[in] change A pointer to an `AMchange` struct.
/// \return A 64-bit unsigned integer.
/// \pre \p change `!= NULL`.
/// \internal
///
/// # Safety
/// change must be a valid pointer to an AMchange
#[no_mangle]
pub unsafe extern "C" fn AMchangeStartOp(change: *const AMchange) -> u64 {
if let Some(change) = change.as_ref() {
u64::from(change.as_ref().start_op())
} else {
u64::MAX
}
}
/// \memberof AMchange
/// \brief Gets the commit time of a change.
///
/// \param[in] change A pointer to an `AMchange` struct.
/// \return A 64-bit signed integer.
/// \pre \p change `!= NULL`.
/// \internal
///
/// # Safety
/// change must be a valid pointer to an AMchange
#[no_mangle]
pub unsafe extern "C" fn AMchangeTime(change: *const AMchange) -> i64 {
if let Some(change) = change.as_ref() {
change.as_ref().timestamp()
} else {
i64::MAX
}
}
/// \memberof AMchange
/// \brief Gets the raw bytes of a change.
///
/// \param[in] change A pointer to an `AMchange` struct.
/// \return An `AMbyteSpan` struct.
/// \pre \p change `!= NULL`.
/// \internal
///
/// # Safety
/// change must be a valid pointer to an AMchange
#[no_mangle]
pub unsafe extern "C" fn AMchangeRawBytes(change: *const AMchange) -> AMbyteSpan {
if let Some(change) = change.as_ref() {
change.as_ref().raw_bytes().into()
} else {
Default::default()
}
}
/// \memberof AMchange
/// \brief Loads a document into a sequence of changes.
///
/// \param[in] src A pointer to an array of bytes.
/// \param[in] count The number of bytes in \p src to load.
/// \return A pointer to an `AMresult` struct containing a sequence of
/// `AMchange` structs.
/// \pre \p src `!= NULL`.
/// \pre `0 <` \p count `<= sizeof(`\p src`)`.
/// \warning The returned `AMresult` struct must be deallocated with `AMfree()`
/// in order to prevent a memory leak.
/// \internal
///
/// # Safety
/// src must be a byte array of size `>= count`
#[no_mangle]
pub unsafe extern "C" fn AMchangeLoadDocument(src: *const u8, count: usize) -> *mut AMresult {
let mut data = Vec::new();
data.extend_from_slice(std::slice::from_raw_parts(src, count));
to_result::<Result<Vec<am::Change>, _>>(
am::Automerge::load(&data)
.and_then(|d| d.get_changes(&[]).map(|c| c.into_iter().cloned().collect())),
)
}