use automerge as am; use automerge::transaction::Transactable; use crate::byte_span::{to_str, AMbyteSpan}; use crate::change_hashes::AMchangeHashes; use crate::doc::{to_doc, to_doc_mut, to_obj_id, AMdoc}; use crate::obj::{to_obj_type, AMobjId, AMobjType}; use crate::result::{to_result, AMresult}; pub mod item; pub mod items; macro_rules! adjust { ($index:expr, $insert:expr, $len:expr) => {{ // An empty object can only be inserted into. let insert = $insert || $len == 0; let end = if insert { $len } else { $len - 1 }; if $index > end && $index != usize::MAX { return AMresult::err(&format!("Invalid index {}", $index)).into(); } (std::cmp::min($index, end), insert) }}; } macro_rules! to_range { ($begin:expr, $end:expr) => {{ if $begin > $end { return AMresult::err(&format!("Invalid range [{}-{})", $begin, $end)).into(); }; ($begin..$end) }}; } /// \memberof AMdoc /// \brief Deletes an index in a list object. /// /// \param[in,out] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] index An index in the list object identified by \p obj_id or /// `SIZE_MAX` to indicate its last index. /// \return A pointer to an `AMresult` struct containing a void. /// \pre \p doc `!= NULL`. /// \pre `0 <=` \p index `<= AMobjSize(`\p obj_id`)` or \p index `== SIZE_MAX`. /// \warning The returned `AMresult` struct must be deallocated with `AMfree()` /// in order to prevent a memory leak. /// \internal /// /// # Safety /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() #[no_mangle] pub unsafe extern "C" fn AMlistDelete( doc: *mut AMdoc, obj_id: *const AMobjId, index: usize, ) -> *mut AMresult { let doc = to_doc_mut!(doc); let obj_id = to_obj_id!(obj_id); let (index, _) = adjust!(index, false, doc.length(obj_id)); to_result(doc.delete(obj_id, index)) } /// \memberof AMdoc /// \brief Gets the current or historical value at an index in a list object. /// /// \param[in] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] index An index in the list object identified by \p obj_id or /// `SIZE_MAX` to indicate its last index. /// \param[in] heads A pointer to an `AMchangeHashes` struct for a historical /// value or `NULL` for the current value. /// \return A pointer to an `AMresult` struct that doesn't contain a void. /// \pre \p doc `!= NULL`. /// \pre `0 <=` \p index `<= AMobjSize(`\p obj_id`)` or \p index `== SIZE_MAX`. /// \warning The returned `AMresult` struct must be deallocated with `AMfree()` /// in order to prevent a memory leak. /// \internal /// /// # Safety /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() /// heads must be a valid pointer to an AMchangeHashes or std::ptr::null() #[no_mangle] pub unsafe extern "C" fn AMlistGet( doc: *const AMdoc, obj_id: *const AMobjId, index: usize, heads: *const AMchangeHashes, ) -> *mut AMresult { let doc = to_doc!(doc); let obj_id = to_obj_id!(obj_id); let (index, _) = adjust!(index, false, doc.length(obj_id)); to_result(match heads.as_ref() { None => doc.get(obj_id, index), Some(heads) => doc.get_at(obj_id, index, heads.as_ref()), }) } /// \memberof AMdoc /// \brief Gets all of the historical values at an index in a list object until /// its current one or a specific one. /// /// \param[in] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] index An index in the list object identified by \p obj_id or /// `SIZE_MAX` to indicate its last index. /// \param[in] heads A pointer to an `AMchangeHashes` struct for a historical /// last value or `NULL` for the current last value. /// \return A pointer to an `AMresult` struct containing an `AMobjItems` struct. /// \pre \p doc `!= NULL`. /// \pre `0 <=` \p index `<= AMobjSize(`\p obj_id`)` or \p index `== SIZE_MAX`. /// \warning The returned `AMresult` struct must be deallocated with `AMfree()` /// in order to prevent a memory leak. /// \internal /// /// # Safety /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() /// heads must be a valid pointer to an AMchangeHashes or std::ptr::null() #[no_mangle] pub unsafe extern "C" fn AMlistGetAll( doc: *const AMdoc, obj_id: *const AMobjId, index: usize, heads: *const AMchangeHashes, ) -> *mut AMresult { let doc = to_doc!(doc); let obj_id = to_obj_id!(obj_id); let (index, _) = adjust!(index, false, doc.length(obj_id)); match heads.as_ref() { None => to_result(doc.get_all(obj_id, index)), Some(heads) => to_result(doc.get_all_at(obj_id, index, heads.as_ref())), } } /// \memberof AMdoc /// \brief Increments a counter at an index in a list object by the given /// value. /// /// \param[in,out] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] index An index in the list object identified by \p obj_id or /// `SIZE_MAX` to indicate its last index. /// \param[in] value A 64-bit signed integer. /// \return A pointer to an `AMresult` struct containing a void. /// \pre \p doc `!= NULL`. /// \pre `0 <=` \p index `<= AMobjSize(`\p obj_id`)` or \p index `== SIZE_MAX`. /// \warning The returned `AMresult` struct must be deallocated with `AMfree()` /// in order to prevent a memory leak. /// \internal /// /// # Safety /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() #[no_mangle] pub unsafe extern "C" fn AMlistIncrement( doc: *mut AMdoc, obj_id: *const AMobjId, index: usize, value: i64, ) -> *mut AMresult { let doc = to_doc_mut!(doc); let obj_id = to_obj_id!(obj_id); let (index, _) = adjust!(index, false, doc.length(obj_id)); to_result(doc.increment(obj_id, index, value)) } /// \memberof AMdoc /// \brief Puts a boolean as the value at an index in a list object. /// /// \param[in,out] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] index An index in the list object identified by \p obj_id or /// `SIZE_MAX` to indicate its last index if \p insert /// `== false` or one past its last index if \p insert /// `== true`. /// \param[in] insert A flag to insert \p value before \p index instead of /// writing \p value over \p index. /// \param[in] value A boolean. /// \return A pointer to an `AMresult` struct containing a void. /// \pre \p doc `!= NULL`. /// \pre `0 <=` \p index `<= AMobjSize(`\p obj_id`)` or \p index `== SIZE_MAX`. /// \warning The returned `AMresult` struct must be deallocated with `AMfree()` /// in order to prevent a memory leak. /// \internal /// /// # Safety /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() #[no_mangle] pub unsafe extern "C" fn AMlistPutBool( doc: *mut AMdoc, obj_id: *const AMobjId, index: usize, insert: bool, value: bool, ) -> *mut AMresult { let doc = to_doc_mut!(doc); let obj_id = to_obj_id!(obj_id); let (index, insert) = adjust!(index, insert, doc.length(obj_id)); let value = am::ScalarValue::Boolean(value); to_result(if insert { doc.insert(obj_id, index, value) } else { doc.put(obj_id, index, value) }) } /// \memberof AMdoc /// \brief Puts a sequence of bytes as the value at an index in a list object. /// /// \param[in,out] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] index An index in the list object identified by \p obj_id or /// `SIZE_MAX` to indicate its last index if \p insert /// `== false` or one past its last index if \p insert /// `== true`. /// \param[in] insert A flag to insert \p src before \p index instead of /// writing \p src over \p index. /// \param[in] src A pointer to an array of bytes. /// \param[in] count The number of bytes to copy from \p src. /// \return A pointer to an `AMresult` struct containing a void. /// \pre \p doc `!= NULL`. /// \pre `0 <=` \p index `<= AMobjSize(`\p obj_id`)` or \p index `== SIZE_MAX`. /// \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 /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() /// src must be a byte array of size `>= count` #[no_mangle] pub unsafe extern "C" fn AMlistPutBytes( doc: *mut AMdoc, obj_id: *const AMobjId, index: usize, insert: bool, val: AMbyteSpan, ) -> *mut AMresult { let doc = to_doc_mut!(doc); let obj_id = to_obj_id!(obj_id); let (index, insert) = adjust!(index, insert, doc.length(obj_id)); let mut value = Vec::new(); value.extend_from_slice(std::slice::from_raw_parts(val.src, val.count)); to_result(if insert { doc.insert(obj_id, index, value) } else { doc.put(obj_id, index, value) }) } /// \memberof AMdoc /// \brief Puts a CRDT counter as the value at an index in a list object. /// /// \param[in,out] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] index An index in the list object identified by \p obj_id or /// `SIZE_MAX` to indicate its last index if \p insert /// `== false` or one past its last index if \p insert /// `== true`. /// \param[in] insert A flag to insert \p value before \p index instead of /// writing \p value over \p index. /// \param[in] value A 64-bit signed integer. /// \return A pointer to an `AMresult` struct containing a void. /// \pre \p doc `!= NULL`. /// \pre `0 <=` \p index `<= AMobjSize(`\p obj_id`)` or \p index `== SIZE_MAX`. /// \warning The returned `AMresult` struct must be deallocated with `AMfree()` /// in order to prevent a memory leak. /// \internal /// /// # Safety /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() #[no_mangle] pub unsafe extern "C" fn AMlistPutCounter( doc: *mut AMdoc, obj_id: *const AMobjId, index: usize, insert: bool, value: i64, ) -> *mut AMresult { let doc = to_doc_mut!(doc); let obj_id = to_obj_id!(obj_id); let (index, insert) = adjust!(index, insert, doc.length(obj_id)); let value = am::ScalarValue::Counter(value.into()); to_result(if insert { doc.insert(obj_id, index, value) } else { doc.put(obj_id, index, value) }) } /// \memberof AMdoc /// \brief Puts a float as the value at an index in a list object. /// /// \param[in,out] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] index An index in the list object identified by \p obj_id or /// `SIZE_MAX` to indicate its last index if \p insert /// `== false` or one past its last index if \p insert /// `== true`. /// \param[in] insert A flag to insert \p value before \p index instead of /// writing \p value over \p index. /// \param[in] value A 64-bit float. /// \return A pointer to an `AMresult` struct containing a void. /// \pre \p doc `!= NULL`. /// \pre `0 <=` \p index `<= AMobjSize(`\p obj_id`)` or \p index `== SIZE_MAX`. /// \warning The returned `AMresult` struct must be deallocated with `AMfree()` /// in order to prevent a memory leak. /// \internal /// /// # Safety /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() #[no_mangle] pub unsafe extern "C" fn AMlistPutF64( doc: *mut AMdoc, obj_id: *const AMobjId, index: usize, insert: bool, value: f64, ) -> *mut AMresult { let doc = to_doc_mut!(doc); let obj_id = to_obj_id!(obj_id); let (index, insert) = adjust!(index, insert, doc.length(obj_id)); to_result(if insert { doc.insert(obj_id, index, value) } else { doc.put(obj_id, index, value) }) } /// \memberof AMdoc /// \brief Puts a signed integer as the value at an index in a list object. /// /// \param[in,out] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] index An index in the list object identified by \p obj_id or /// `SIZE_MAX` to indicate its last index if \p insert /// `== false` or one past its last index if \p insert /// `== true`. /// \param[in] insert A flag to insert \p value before \p index instead of /// writing \p value over \p index. /// \param[in] value A 64-bit signed integer. /// \return A pointer to an `AMresult` struct containing a void. /// \pre \p doc `!= NULL`. /// \pre `0 <=` \p index `<= AMobjSize(`\p obj_id`)` or \p index `== SIZE_MAX`. /// \warning The returned `AMresult` struct must be deallocated with `AMfree()` /// in order to prevent a memory leak. /// \internal /// /// # Safety /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() #[no_mangle] pub unsafe extern "C" fn AMlistPutInt( doc: *mut AMdoc, obj_id: *const AMobjId, index: usize, insert: bool, value: i64, ) -> *mut AMresult { let doc = to_doc_mut!(doc); let obj_id = to_obj_id!(obj_id); let (index, insert) = adjust!(index, insert, doc.length(obj_id)); to_result(if insert { doc.insert(obj_id, index, value) } else { doc.put(obj_id, index, value) }) } /// \memberof AMdoc /// \brief Puts null as the value at an index in a list object. /// /// \param[in,out] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] index An index in the list object identified by \p obj_id or /// `SIZE_MAX` to indicate its last index if \p insert /// `== false` or one past its last index if \p insert /// `== true`. /// \param[in] insert A flag to insert \p value before \p index instead of /// writing \p value over \p index. /// \return A pointer to an `AMresult` struct containing a void. /// \pre \p doc `!= NULL`. /// \pre `0 <=` \p index `<= AMobjSize(`\p obj_id`)` or \p index `== SIZE_MAX`. /// \warning The returned `AMresult` struct must be deallocated with `AMfree()` /// in order to prevent a memory leak. /// \internal /// /// # Safety /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() #[no_mangle] pub unsafe extern "C" fn AMlistPutNull( doc: *mut AMdoc, obj_id: *const AMobjId, index: usize, insert: bool, ) -> *mut AMresult { let doc = to_doc_mut!(doc); let obj_id = to_obj_id!(obj_id); let (index, insert) = adjust!(index, insert, doc.length(obj_id)); to_result(if insert { doc.insert(obj_id, index, ()) } else { doc.put(obj_id, index, ()) }) } /// \memberof AMdoc /// \brief Puts an empty object as the value at an index in a list object. /// /// \param[in,out] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] index An index in the list object identified by \p obj_id or /// `SIZE_MAX` to indicate its last index if \p insert /// `== false` or one past its last index if \p insert /// `== true`. /// \param[in] insert A flag to insert \p value before \p index instead of /// writing \p value over \p index. /// \param[in] obj_type An `AMobjIdType` enum tag. /// \return A pointer to an `AMresult` struct containing a pointer to an /// `AMobjId` struct. /// \pre \p doc `!= NULL`. /// \pre `0 <=` \p index `<= AMobjSize(`\p obj_id`)` or \p index `== SIZE_MAX`. /// \pre \p obj_type != `AM_OBJ_TYPE_VOID`. /// \warning The returned `AMresult` struct must be deallocated with `AMfree()` /// in order to prevent a memory leak. /// \internal /// /// # Safety /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() #[no_mangle] pub unsafe extern "C" fn AMlistPutObject( doc: *mut AMdoc, obj_id: *const AMobjId, index: usize, insert: bool, obj_type: AMobjType, ) -> *mut AMresult { let doc = to_doc_mut!(doc); let obj_id = to_obj_id!(obj_id); let (index, insert) = adjust!(index, insert, doc.length(obj_id)); let object = to_obj_type!(obj_type); to_result(if insert { doc.insert_object(obj_id, index, object) } else { doc.put_object(obj_id, index, object) }) } /// \memberof AMdoc /// \brief Puts a UTF-8 string as the value at an index in a list object. /// /// \param[in,out] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] index An index in the list object identified by \p obj_id or /// `SIZE_MAX` to indicate its last index if \p insert /// `== false` or one past its last index if \p insert /// `== true`. /// \param[in] insert A flag to insert \p value before \p index instead of /// writing \p value over \p index. /// \param[in] value A UTF-8 string view as an `AMbyteSpan` struct. /// \return A pointer to an `AMresult` struct containing a void. /// \pre \p doc `!= NULL`. /// \pre `0 <=` \p index `<= AMobjSize(`\p obj_id`)` or \p index `== SIZE_MAX`. /// \pre \p value `!= NULL`. /// \warning The returned `AMresult` struct must be deallocated with `AMfree()` /// in order to prevent a memory leak. /// \internal /// /// # Safety /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() /// value must be a null-terminated array of `c_char` #[no_mangle] pub unsafe extern "C" fn AMlistPutStr( doc: *mut AMdoc, obj_id: *const AMobjId, index: usize, insert: bool, value: AMbyteSpan, ) -> *mut AMresult { let doc = to_doc_mut!(doc); let obj_id = to_obj_id!(obj_id); let (index, insert) = adjust!(index, insert, doc.length(obj_id)); let value = to_str!(value); to_result(if insert { doc.insert(obj_id, index, value) } else { doc.put(obj_id, index, value) }) } /// \memberof AMdoc /// \brief Puts a *nix timestamp (milliseconds) as the value at an index in a /// list object. /// /// \param[in,out] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] index An index in the list object identified by \p obj_id or /// `SIZE_MAX` to indicate its last index if \p insert /// `== false` or one past its last index if \p insert /// `== true`. /// \param[in] insert A flag to insert \p value before \p index instead of /// writing \p value over \p index. /// \param[in] value A 64-bit signed integer. /// \return A pointer to an `AMresult` struct containing a void. /// \pre \p doc `!= NULL`. /// \pre `0 <=` \p index `<= AMobjSize(`\p obj_id`)` or \p index `== SIZE_MAX`. /// \warning The returned `AMresult` struct must be deallocated with `AMfree()` /// in order to prevent a memory leak. /// \internal /// /// # Safety /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() #[no_mangle] pub unsafe extern "C" fn AMlistPutTimestamp( doc: *mut AMdoc, obj_id: *const AMobjId, index: usize, insert: bool, value: i64, ) -> *mut AMresult { let doc = to_doc_mut!(doc); let obj_id = to_obj_id!(obj_id); let (index, insert) = adjust!(index, insert, doc.length(obj_id)); let value = am::ScalarValue::Timestamp(value); to_result(if insert { doc.insert(obj_id, index, value) } else { doc.put(obj_id, index, value) }) } /// \memberof AMdoc /// \brief Puts an unsigned integer as the value at an index in a list object. /// /// \param[in,out] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] index An index in the list object identified by \p obj_id or /// `SIZE_MAX` to indicate its last index if \p insert /// `== false` or one past its last index if \p insert /// `== true`. /// \param[in] insert A flag to insert \p value before \p index instead of /// writing \p value over \p index. /// \param[in] value A 64-bit unsigned integer. /// \return A pointer to an `AMresult` struct containing a void. /// \pre \p doc `!= NULL`. /// \pre `0 <=` \p index `<= AMobjSize(`\p obj_id`)` or \p index `== SIZE_MAX`. /// \warning The returned `AMresult` struct must be deallocated with `AMfree()` /// in order to prevent a memory leak. /// \internal /// /// # Safety /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() #[no_mangle] pub unsafe extern "C" fn AMlistPutUint( doc: *mut AMdoc, obj_id: *const AMobjId, index: usize, insert: bool, value: u64, ) -> *mut AMresult { let doc = to_doc_mut!(doc); let obj_id = to_obj_id!(obj_id); let (index, insert) = adjust!(index, insert, doc.length(obj_id)); to_result(if insert { doc.insert(obj_id, index, value) } else { doc.put(obj_id, index, value) }) } /// \memberof AMdoc /// \brief Gets the current or historical indices and values of the list object /// within the given range. /// /// \param[in] doc A pointer to an `AMdoc` struct. /// \param[in] obj_id A pointer to an `AMobjId` struct or `AM_ROOT`. /// \param[in] begin The first index in a range of indices. /// \param[in] end At least one past the last index in a range of indices. /// \param[in] heads A pointer to an `AMchangeHashes` struct for historical /// indices and values or `NULL` for current indices and /// values. /// \return A pointer to an `AMresult` struct containing an `AMlistItems` /// struct. /// \pre \p doc `!= NULL`. /// \pre \p begin `<=` \p end `<= SIZE_MAX`. /// \warning The returned `AMresult` struct must be deallocated with `AMfree()` /// in order to prevent a memory leak. /// \internal /// /// # Safety /// doc must be a valid pointer to an AMdoc /// obj_id must be a valid pointer to an AMobjId or std::ptr::null() /// heads must be a valid pointer to an AMchangeHashes or std::ptr::null() #[no_mangle] pub unsafe extern "C" fn AMlistRange( doc: *const AMdoc, obj_id: *const AMobjId, begin: usize, end: usize, heads: *const AMchangeHashes, ) -> *mut AMresult { let doc = to_doc!(doc); let obj_id = to_obj_id!(obj_id); let range = to_range!(begin, end); match heads.as_ref() { None => to_result(doc.list_range(obj_id, range)), Some(heads) => to_result(doc.list_range_at(obj_id, range, heads.as_ref())), } }