Allow taking from pointer lists
This introduces `tc_task_list_take`, supporting taking ownership of an item in a task list. TCTaskList is the only pointer list, but this is a generic and could be used for other types.
This commit is contained in:
@@ -561,6 +561,53 @@ static void test_task_taskmap(void) {
|
|||||||
tc_replica_free(rep);
|
tc_replica_free(rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// taking from a task list behaves correctly
|
||||||
|
static void test_task_list_take(void) {
|
||||||
|
TCReplica *rep = tc_replica_new_in_memory();
|
||||||
|
TEST_ASSERT_NULL(tc_replica_error(rep).ptr);
|
||||||
|
|
||||||
|
TCTask *task1 = tc_replica_new_task(rep, TC_STATUS_PENDING, tc_string_borrow("t"));
|
||||||
|
TEST_ASSERT_NOT_NULL(task1);
|
||||||
|
|
||||||
|
TCTask *task2 = tc_replica_new_task(rep, TC_STATUS_PENDING, tc_string_borrow("t"));
|
||||||
|
TEST_ASSERT_NOT_NULL(task2);
|
||||||
|
tc_task_free(task2);
|
||||||
|
|
||||||
|
TCString desc;
|
||||||
|
TCTaskList tasks = tc_replica_all_tasks(rep);
|
||||||
|
TEST_ASSERT_NOT_NULL(tasks.items);
|
||||||
|
TEST_ASSERT_EQUAL(2, tasks.len);
|
||||||
|
|
||||||
|
task1 = tc_task_list_take(&tasks, 5); // out of bounds
|
||||||
|
TEST_ASSERT_NULL(task1);
|
||||||
|
|
||||||
|
task1 = tc_task_list_take(&tasks, 0);
|
||||||
|
TEST_ASSERT_NOT_NULL(task1);
|
||||||
|
desc = tc_task_get_description(task1);
|
||||||
|
TEST_ASSERT_EQUAL_STRING("t", tc_string_content(&desc));
|
||||||
|
tc_string_free(&desc);
|
||||||
|
|
||||||
|
task2 = tc_task_list_take(&tasks, 1);
|
||||||
|
TEST_ASSERT_NOT_NULL(task2);
|
||||||
|
desc = tc_task_get_description(task2);
|
||||||
|
TEST_ASSERT_EQUAL_STRING("t", tc_string_content(&desc));
|
||||||
|
tc_string_free(&desc);
|
||||||
|
|
||||||
|
tc_task_free(task1);
|
||||||
|
tc_task_free(task2);
|
||||||
|
|
||||||
|
task1 = tc_task_list_take(&tasks, 0); // already taken
|
||||||
|
TEST_ASSERT_NULL(task1);
|
||||||
|
|
||||||
|
task1 = tc_task_list_take(&tasks, 5); // out of bounds
|
||||||
|
TEST_ASSERT_NULL(task1);
|
||||||
|
|
||||||
|
tc_task_list_free(&tasks);
|
||||||
|
TEST_ASSERT_NULL(tasks.items);
|
||||||
|
|
||||||
|
tc_replica_free(rep);
|
||||||
|
}
|
||||||
|
|
||||||
int task_tests(void) {
|
int task_tests(void) {
|
||||||
UNITY_BEGIN();
|
UNITY_BEGIN();
|
||||||
// each test case above should be named here, in order.
|
// each test case above should be named here, in order.
|
||||||
@@ -578,5 +625,6 @@ int task_tests(void) {
|
|||||||
RUN_TEST(test_task_annotations);
|
RUN_TEST(test_task_annotations);
|
||||||
RUN_TEST(test_task_udas);
|
RUN_TEST(test_task_udas);
|
||||||
RUN_TEST(test_task_taskmap);
|
RUN_TEST(test_task_taskmap);
|
||||||
|
RUN_TEST(test_task_list_take);
|
||||||
return UNITY_END();
|
return UNITY_END();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,13 +75,13 @@ pub struct TCAnnotationList {
|
|||||||
|
|
||||||
/// array of annotations. these remain owned by the TCAnnotationList instance and will be freed by
|
/// array of annotations. these remain owned by the TCAnnotationList instance and will be freed by
|
||||||
/// tc_annotation_list_free. This pointer is never NULL for a valid TCAnnotationList.
|
/// tc_annotation_list_free. This pointer is never NULL for a valid TCAnnotationList.
|
||||||
items: *const TCAnnotation,
|
items: *mut TCAnnotation,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CList for TCAnnotationList {
|
impl CList for TCAnnotationList {
|
||||||
type Element = TCAnnotation;
|
type Element = TCAnnotation;
|
||||||
|
|
||||||
unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self {
|
unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
|
||||||
TCAnnotationList {
|
TCAnnotationList {
|
||||||
len,
|
len,
|
||||||
_capacity: cap,
|
_capacity: cap,
|
||||||
@@ -89,7 +89,16 @@ impl CList for TCAnnotationList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_raw_parts(self) -> (*const Self::Element, usize, usize) {
|
fn slice(&mut self) -> &mut [Self::Element] {
|
||||||
|
// SAFETY:
|
||||||
|
// - because we have &mut self, we have read/write access to items[0..len]
|
||||||
|
// - all items are properly initialized Element's
|
||||||
|
// - return value lifetime is equal to &mmut self's, so access is exclusive
|
||||||
|
// - items and len came from Vec, so total size is < isize::MAX
|
||||||
|
unsafe { std::slice::from_raw_parts_mut(self.items, self.len) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) {
|
||||||
(self.items, self.len, self._capacity)
|
(self.items, self.len, self._capacity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,13 +50,13 @@ pub struct TCKVList {
|
|||||||
|
|
||||||
/// array of TCKV's. these remain owned by the TCKVList instance and will be freed by
|
/// array of TCKV's. these remain owned by the TCKVList instance and will be freed by
|
||||||
/// tc_kv_list_free. This pointer is never NULL for a valid TCKVList.
|
/// tc_kv_list_free. This pointer is never NULL for a valid TCKVList.
|
||||||
items: *const TCKV,
|
items: *mut TCKV,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CList for TCKVList {
|
impl CList for TCKVList {
|
||||||
type Element = TCKV;
|
type Element = TCKV;
|
||||||
|
|
||||||
unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self {
|
unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
|
||||||
TCKVList {
|
TCKVList {
|
||||||
len,
|
len,
|
||||||
_capacity: cap,
|
_capacity: cap,
|
||||||
@@ -64,7 +64,16 @@ impl CList for TCKVList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_raw_parts(self) -> (*const Self::Element, usize, usize) {
|
fn slice(&mut self) -> &mut [Self::Element] {
|
||||||
|
// SAFETY:
|
||||||
|
// - because we have &mut self, we have read/write access to items[0..len]
|
||||||
|
// - all items are properly initialized Element's
|
||||||
|
// - return value lifetime is equal to &mmut self's, so access is exclusive
|
||||||
|
// - items and len came from Vec, so total size is < isize::MAX
|
||||||
|
unsafe { std::slice::from_raw_parts_mut(self.items, self.len) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) {
|
||||||
(self.items, self.len, self._capacity)
|
(self.items, self.len, self._capacity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,12 +177,14 @@ pub unsafe extern "C" fn tc_replica_all_tasks(rep: *mut TCReplica) -> TCTaskList
|
|||||||
.all_tasks()?
|
.all_tasks()?
|
||||||
.drain()
|
.drain()
|
||||||
.map(|(_uuid, t)| {
|
.map(|(_uuid, t)| {
|
||||||
NonNull::new(
|
Some(
|
||||||
// SAFETY:
|
NonNull::new(
|
||||||
// - caller promises to free this value (via freeing the list)
|
// SAFETY:
|
||||||
unsafe { TCTask::from(t).return_ptr() },
|
// - caller promises to free this value (via freeing the list)
|
||||||
|
unsafe { TCTask::from(t).return_ptr() },
|
||||||
|
)
|
||||||
|
.expect("TCTask::return_ptr returned NULL"),
|
||||||
)
|
)
|
||||||
.expect("TCTask::return_ptr returned NULL")
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
|
|||||||
@@ -374,13 +374,13 @@ pub struct TCStringList {
|
|||||||
/// TCStringList representing each string. these remain owned by the TCStringList instance and will
|
/// TCStringList representing each string. these remain owned by the TCStringList instance and will
|
||||||
/// be freed by tc_string_list_free. This pointer is never NULL for a valid TCStringList, and the
|
/// be freed by tc_string_list_free. This pointer is never NULL for a valid TCStringList, and the
|
||||||
/// *TCStringList at indexes 0..len-1 are not NULL.
|
/// *TCStringList at indexes 0..len-1 are not NULL.
|
||||||
items: *const TCString,
|
items: *mut TCString,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CList for TCStringList {
|
impl CList for TCStringList {
|
||||||
type Element = TCString;
|
type Element = TCString;
|
||||||
|
|
||||||
unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self {
|
unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
|
||||||
TCStringList {
|
TCStringList {
|
||||||
len,
|
len,
|
||||||
_capacity: cap,
|
_capacity: cap,
|
||||||
@@ -388,7 +388,16 @@ impl CList for TCStringList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_raw_parts(self) -> (*const Self::Element, usize, usize) {
|
fn slice(&mut self) -> &mut [Self::Element] {
|
||||||
|
// SAFETY:
|
||||||
|
// - because we have &mut self, we have read/write access to items[0..len]
|
||||||
|
// - all items are properly initialized Element's
|
||||||
|
// - return value lifetime is equal to &mmut self's, so access is exclusive
|
||||||
|
// - items and len came from Vec, so total size is < isize::MAX
|
||||||
|
unsafe { std::slice::from_raw_parts_mut(self.items, self.len) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) {
|
||||||
(self.items, self.len, self._capacity)
|
(self.items, self.len, self._capacity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,7 +171,10 @@ impl TryFrom<RustString<'static>> for Tag {
|
|||||||
|
|
||||||
/// TCTaskList represents a list of tasks.
|
/// TCTaskList represents a list of tasks.
|
||||||
///
|
///
|
||||||
/// The content of this struct must be treated as read-only.
|
/// The content of this struct must be treated as read-only: no fields or anything they reference
|
||||||
|
/// should be modified directly by C code.
|
||||||
|
///
|
||||||
|
/// When an item is taken from this list, its pointer in `items` is set to NULL.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct TCTaskList {
|
pub struct TCTaskList {
|
||||||
/// number of tasks in items
|
/// number of tasks in items
|
||||||
@@ -183,13 +186,13 @@ pub struct TCTaskList {
|
|||||||
/// array of pointers representing each task. these remain owned by the TCTaskList instance and
|
/// array of pointers representing each task. these remain owned by the TCTaskList instance and
|
||||||
/// will be freed by tc_task_list_free. This pointer is never NULL for a valid TCTaskList,
|
/// will be freed by tc_task_list_free. This pointer is never NULL for a valid TCTaskList,
|
||||||
/// and the *TCTaskList at indexes 0..len-1 are not NULL.
|
/// and the *TCTaskList at indexes 0..len-1 are not NULL.
|
||||||
items: *const NonNull<TCTask>,
|
items: *mut Option<NonNull<TCTask>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CList for TCTaskList {
|
impl CList for TCTaskList {
|
||||||
type Element = NonNull<TCTask>;
|
type Element = Option<NonNull<TCTask>>;
|
||||||
|
|
||||||
unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self {
|
unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
|
||||||
TCTaskList {
|
TCTaskList {
|
||||||
len,
|
len,
|
||||||
_capacity: cap,
|
_capacity: cap,
|
||||||
@@ -197,7 +200,16 @@ impl CList for TCTaskList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_raw_parts(self) -> (*const Self::Element, usize, usize) {
|
fn slice(&mut self) -> &mut [Self::Element] {
|
||||||
|
// SAFETY:
|
||||||
|
// - because we have &mut self, we have read/write access to items[0..len]
|
||||||
|
// - all items are properly initialized Element's
|
||||||
|
// - return value lifetime is equal to &mmut self's, so access is exclusive
|
||||||
|
// - items and len came from Vec, so total size is < isize::MAX
|
||||||
|
unsafe { std::slice::from_raw_parts_mut(self.items, self.len) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) {
|
||||||
(self.items, self.len, self._capacity)
|
(self.items, self.len, self._capacity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -813,17 +825,39 @@ pub unsafe extern "C" fn tc_task_free(task: *mut TCTask) {
|
|||||||
drop(tctask);
|
drop(tctask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Take an item from a TCTaskList. After this call, the indexed item is no longer associated
|
||||||
|
/// with the list and becomes the caller's responsibility, just as if it had been returned from
|
||||||
|
/// `tc_replica_get_task`.
|
||||||
|
///
|
||||||
|
/// The corresponding element in the `items` array will be set to NULL. If that field is already
|
||||||
|
/// NULL (that is, if the item has already been taken), this function will return NULL. If the
|
||||||
|
/// index is out of bounds, this function will also return NULL.
|
||||||
|
///
|
||||||
|
/// The passed TCTaskList remains owned by the caller.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn tc_task_list_take(tasks: *mut TCTaskList, index: usize) -> *mut TCTask {
|
||||||
|
// SAFETY:
|
||||||
|
// - tasks is not NULL and points to a valid TCTaskList (caller is not allowed to
|
||||||
|
// modify the list directly, and tc_task_list_take leaves the list valid)
|
||||||
|
let p = unsafe { take_optional_pointer_list_item(tasks, index) };
|
||||||
|
if let Some(p) = p {
|
||||||
|
p.as_ptr()
|
||||||
|
} else {
|
||||||
|
std::ptr::null_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Free a TCTaskList instance. The instance, and all TCTaskList it contains, must not be used after
|
/// Free a TCTaskList instance. The instance, and all TCTaskList it contains, must not be used after
|
||||||
/// this call.
|
/// this call.
|
||||||
///
|
///
|
||||||
/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCTaskList.
|
/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCTaskList.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn tc_task_list_free(tctasks: *mut TCTaskList) {
|
pub unsafe extern "C" fn tc_task_list_free(tasks: *mut TCTaskList) {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - tctasks is not NULL and points to a valid TCTaskList (caller is not allowed to
|
// - tasks is not NULL and points to a valid TCTaskList (caller is not allowed to
|
||||||
// modify the list)
|
// modify the list directly, and tc_task_list_take leaves the list valid)
|
||||||
// - caller promises not to use the value after return
|
// - caller promises not to use the value after return
|
||||||
unsafe { drop_pointer_list(tctasks) };
|
unsafe { drop_optional_pointer_list(tasks) };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -832,19 +866,19 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_list_has_non_null_pointer() {
|
fn empty_list_has_non_null_pointer() {
|
||||||
let tctasks = unsafe { TCTaskList::return_val(Vec::new()) };
|
let tasks = unsafe { TCTaskList::return_val(Vec::new()) };
|
||||||
assert!(!tctasks.items.is_null());
|
assert!(!tasks.items.is_null());
|
||||||
assert_eq!(tctasks.len, 0);
|
assert_eq!(tasks.len, 0);
|
||||||
assert_eq!(tctasks._capacity, 0);
|
assert_eq!(tasks._capacity, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn free_sets_null_pointer() {
|
fn free_sets_null_pointer() {
|
||||||
let mut tctasks = unsafe { TCTaskList::return_val(Vec::new()) };
|
let mut tasks = unsafe { TCTaskList::return_val(Vec::new()) };
|
||||||
// SAFETY: testing expected behavior
|
// SAFETY: testing expected behavior
|
||||||
unsafe { tc_task_list_free(&mut tctasks) };
|
unsafe { tc_task_list_free(&mut tasks) };
|
||||||
assert!(tctasks.items.is_null());
|
assert!(tasks.items.is_null());
|
||||||
assert_eq!(tctasks.len, 0);
|
assert_eq!(tasks.len, 0);
|
||||||
assert_eq!(tctasks._capacity, 0);
|
assert_eq!(tasks._capacity, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,13 +150,17 @@ pub(crate) trait PassByPointer: Sized {
|
|||||||
/// The PassByValue trait will be implemented automatically, converting between the C type and
|
/// The PassByValue trait will be implemented automatically, converting between the C type and
|
||||||
/// `Vec<Element>`.
|
/// `Vec<Element>`.
|
||||||
///
|
///
|
||||||
/// For most cases, it is only necessary to implement `tc_.._free` that calls either
|
/// The element type can be PassByValue or PassByPointer. If the latter, it should use either
|
||||||
/// drop_value_list (if Element is PassByValue) or drop_pointer_list (if element is PassByPointer).
|
/// `NonNull<T>` or `Option<NonNull<T>>` to represent the element. The latter is an "optional
|
||||||
|
/// pointer list", where elements can be omitted.
|
||||||
|
///
|
||||||
|
/// For most cases, it is only necessary to implement `tc_.._free` that calls one of the
|
||||||
|
/// drop_..._list functions.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The C type must be documented as read-only. None of the fields may be modified, nor anything
|
/// The C type must be documented as read-only. None of the fields may be modified, nor anything
|
||||||
/// accessible via the `items` array.
|
/// accessible via the `items` array. The exception is modification via "taking" elements.
|
||||||
///
|
///
|
||||||
/// This class guarantees that the items pointer is non-NULL for any valid list (even when len=0).
|
/// This class guarantees that the items pointer is non-NULL for any valid list (even when len=0).
|
||||||
pub(crate) trait CList: Sized {
|
pub(crate) trait CList: Sized {
|
||||||
@@ -169,18 +173,21 @@ pub(crate) trait CList: Sized {
|
|||||||
/// The arguments must either:
|
/// The arguments must either:
|
||||||
/// - be NULL, 0, and 0, respectively; or
|
/// - be NULL, 0, and 0, respectively; or
|
||||||
/// - be valid for Vec::from_raw_parts
|
/// - be valid for Vec::from_raw_parts
|
||||||
unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self;
|
unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self;
|
||||||
|
|
||||||
|
/// Return a mutable slice representing the elements in this list.
|
||||||
|
fn slice(&mut self) -> &mut [Self::Element];
|
||||||
|
|
||||||
/// Get the items, len, and capacity (in that order) for this instance. These must be
|
/// Get the items, len, and capacity (in that order) for this instance. These must be
|
||||||
/// precisely the same values passed tearlier to `from_raw_parts`.
|
/// precisely the same values passed tearlier to `from_raw_parts`.
|
||||||
fn into_raw_parts(self) -> (*const Self::Element, usize, usize);
|
fn into_raw_parts(self) -> (*mut Self::Element, usize, usize);
|
||||||
|
|
||||||
/// Generate a NULL value. By default this is a NULL items pointer with zero length and
|
/// Generate a NULL value. By default this is a NULL items pointer with zero length and
|
||||||
/// capacity.
|
/// capacity.
|
||||||
fn null_value() -> Self {
|
fn null_value() -> Self {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - satisfies the first case in from_raw_parts' safety documentation
|
// - satisfies the first case in from_raw_parts' safety documentation
|
||||||
unsafe { Self::from_raw_parts(std::ptr::null(), 0, 0) }
|
unsafe { Self::from_raw_parts(std::ptr::null_mut(), 0, 0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,6 +232,7 @@ where
|
|||||||
/// - List must be non-NULL and point to a valid CL instance
|
/// - List must be non-NULL and point to a valid CL instance
|
||||||
/// - The caller must not use the value array points to after this function, as
|
/// - The caller must not use the value array points to after this function, as
|
||||||
/// it has been freed. It will be replaced with the null value.
|
/// it has been freed. It will be replaced with the null value.
|
||||||
|
#[allow(dead_code)] // this was useful once, and might be again?
|
||||||
pub(crate) unsafe fn drop_pointer_list<CL, T>(list: *mut CL)
|
pub(crate) unsafe fn drop_pointer_list<CL, T>(list: *mut CL)
|
||||||
where
|
where
|
||||||
CL: CList<Element = NonNull<T>>,
|
CL: CList<Element = NonNull<T>>,
|
||||||
@@ -246,6 +254,79 @@ where
|
|||||||
drop(vec);
|
drop(vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given a CList containing optional pointers, drop all of the non-null pointed-to values and the
|
||||||
|
/// list.
|
||||||
|
///
|
||||||
|
/// This is a convenience function for `tc_.._list_free` functions, for lists from which items
|
||||||
|
/// can be taken.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// - List must be non-NULL and point to a valid CL instance
|
||||||
|
/// - The caller must not use the value array points to after this function, as
|
||||||
|
/// it has been freed. It will be replaced with the null value.
|
||||||
|
pub(crate) unsafe fn drop_optional_pointer_list<CL, T>(list: *mut CL)
|
||||||
|
where
|
||||||
|
CL: CList<Element = Option<NonNull<T>>>,
|
||||||
|
T: PassByPointer,
|
||||||
|
{
|
||||||
|
debug_assert!(!list.is_null());
|
||||||
|
// SAFETY:
|
||||||
|
// - *list is a valid CL (promised by caller)
|
||||||
|
let mut vec = unsafe { CL::take_val_from_arg(list, CL::null_value()) };
|
||||||
|
|
||||||
|
// first, drop each of the elements in turn
|
||||||
|
for e in vec.drain(..) {
|
||||||
|
if let Some(e) = e {
|
||||||
|
// SAFETY:
|
||||||
|
// - e is a valid Element (promised by caller)
|
||||||
|
// - e is owned
|
||||||
|
drop(unsafe { PassByPointer::take_from_ptr_arg(e.as_ptr()) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// then drop the vector
|
||||||
|
drop(vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Take a value from an optional pointer list, returning the value and replacing its array
|
||||||
|
/// element with NULL.
|
||||||
|
///
|
||||||
|
/// This is a convenience function for `tc_.._list_take` functions, for lists from which items
|
||||||
|
/// can be taken.
|
||||||
|
///
|
||||||
|
/// The returned value will be None if the element has already been taken, or if the index is
|
||||||
|
/// out of bounds.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// - List must be non-NULL and point to a valid CL instance
|
||||||
|
pub(crate) unsafe fn take_optional_pointer_list_item<CL, T>(
|
||||||
|
list: *mut CL,
|
||||||
|
index: usize,
|
||||||
|
) -> Option<NonNull<T>>
|
||||||
|
where
|
||||||
|
CL: CList<Element = Option<NonNull<T>>>,
|
||||||
|
T: PassByPointer,
|
||||||
|
{
|
||||||
|
debug_assert!(!list.is_null());
|
||||||
|
|
||||||
|
// SAFETy:
|
||||||
|
// - list is properly aligned, dereferencable, and points to an initialized CL, since it is valid
|
||||||
|
// - the lifetime of the resulting reference is limited to this function, during which time
|
||||||
|
// nothing else refers to this memory.
|
||||||
|
let slice = list.as_mut().unwrap().slice();
|
||||||
|
if let Some(elt_ref) = slice.get_mut(index) {
|
||||||
|
let mut rv = None;
|
||||||
|
if let Some(elt) = elt_ref.as_mut() {
|
||||||
|
rv = Some(*elt);
|
||||||
|
*elt_ref = None; // clear out the array element
|
||||||
|
}
|
||||||
|
rv
|
||||||
|
} else {
|
||||||
|
None // index out of bounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<A> PassByValue for A
|
impl<A> PassByValue for A
|
||||||
where
|
where
|
||||||
A: CList,
|
A: CList,
|
||||||
|
|||||||
@@ -72,13 +72,13 @@ pub struct TCUdaList {
|
|||||||
|
|
||||||
/// array of UDAs. These remain owned by the TCUdaList instance and will be freed by
|
/// array of UDAs. These remain owned by the TCUdaList instance and will be freed by
|
||||||
/// tc_uda_list_free. This pointer is never NULL for a valid TCUdaList.
|
/// tc_uda_list_free. This pointer is never NULL for a valid TCUdaList.
|
||||||
items: *const TCUda,
|
items: *mut TCUda,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CList for TCUdaList {
|
impl CList for TCUdaList {
|
||||||
type Element = TCUda;
|
type Element = TCUda;
|
||||||
|
|
||||||
unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self {
|
unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
|
||||||
TCUdaList {
|
TCUdaList {
|
||||||
len,
|
len,
|
||||||
_capacity: cap,
|
_capacity: cap,
|
||||||
@@ -86,7 +86,16 @@ impl CList for TCUdaList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_raw_parts(self) -> (*const Self::Element, usize, usize) {
|
fn slice(&mut self) -> &mut [Self::Element] {
|
||||||
|
// SAFETY:
|
||||||
|
// - because we have &mut self, we have read/write access to items[0..len]
|
||||||
|
// - all items are properly initialized Element's
|
||||||
|
// - return value lifetime is equal to &mmut self's, so access is exclusive
|
||||||
|
// - items and len came from Vec, so total size is < isize::MAX
|
||||||
|
unsafe { std::slice::from_raw_parts_mut(self.items, self.len) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) {
|
||||||
(self.items, self.len, self._capacity)
|
(self.items, self.len, self._capacity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,13 +57,13 @@ pub struct TCUuidList {
|
|||||||
|
|
||||||
/// array of uuids. these remain owned by the TCUuidList instance and will be freed by
|
/// array of uuids. these remain owned by the TCUuidList instance and will be freed by
|
||||||
/// tc_uuid_list_free. This pointer is never NULL for a valid TCUuidList.
|
/// tc_uuid_list_free. This pointer is never NULL for a valid TCUuidList.
|
||||||
items: *const TCUuid,
|
items: *mut TCUuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CList for TCUuidList {
|
impl CList for TCUuidList {
|
||||||
type Element = TCUuid;
|
type Element = TCUuid;
|
||||||
|
|
||||||
unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self {
|
unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
|
||||||
TCUuidList {
|
TCUuidList {
|
||||||
len,
|
len,
|
||||||
_capacity: cap,
|
_capacity: cap,
|
||||||
@@ -71,7 +71,16 @@ impl CList for TCUuidList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_raw_parts(self) -> (*const Self::Element, usize, usize) {
|
fn slice(&mut self) -> &mut [Self::Element] {
|
||||||
|
// SAFETY:
|
||||||
|
// - because we have &mut self, we have read/write access to items[0..len]
|
||||||
|
// - all items are properly initialized Element's
|
||||||
|
// - return value lifetime is equal to &mmut self's, so access is exclusive
|
||||||
|
// - items and len came from Vec, so total size is < isize::MAX
|
||||||
|
unsafe { std::slice::from_raw_parts_mut(self.items, self.len) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) {
|
||||||
(self.items, self.len, self._capacity)
|
(self.items, self.len, self._capacity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,7 +301,7 @@ typedef struct TCAnnotationList {
|
|||||||
* array of annotations. these remain owned by the TCAnnotationList instance and will be freed by
|
* array of annotations. these remain owned by the TCAnnotationList instance and will be freed by
|
||||||
* tc_annotation_list_free. This pointer is never NULL for a valid TCAnnotationList.
|
* tc_annotation_list_free. This pointer is never NULL for a valid TCAnnotationList.
|
||||||
*/
|
*/
|
||||||
const struct TCAnnotation *items;
|
struct TCAnnotation *items;
|
||||||
} TCAnnotationList;
|
} TCAnnotationList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -333,13 +333,16 @@ typedef struct TCKVList {
|
|||||||
* array of TCKV's. these remain owned by the TCKVList instance and will be freed by
|
* array of TCKV's. these remain owned by the TCKVList instance and will be freed by
|
||||||
* tc_kv_list_free. This pointer is never NULL for a valid TCKVList.
|
* tc_kv_list_free. This pointer is never NULL for a valid TCKVList.
|
||||||
*/
|
*/
|
||||||
const struct TCKV *items;
|
struct TCKV *items;
|
||||||
} TCKVList;
|
} TCKVList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TCTaskList represents a list of tasks.
|
* TCTaskList represents a list of tasks.
|
||||||
*
|
*
|
||||||
* The content of this struct must be treated as read-only.
|
* The content of this struct must be treated as read-only: no fields or anything they reference
|
||||||
|
* should be modified directly by C code.
|
||||||
|
*
|
||||||
|
* When an item is taken from this list, its pointer in `items` is set to NULL.
|
||||||
*/
|
*/
|
||||||
typedef struct TCTaskList {
|
typedef struct TCTaskList {
|
||||||
/**
|
/**
|
||||||
@@ -355,7 +358,7 @@ typedef struct TCTaskList {
|
|||||||
* will be freed by tc_task_list_free. This pointer is never NULL for a valid TCTaskList,
|
* will be freed by tc_task_list_free. This pointer is never NULL for a valid TCTaskList,
|
||||||
* and the *TCTaskList at indexes 0..len-1 are not NULL.
|
* and the *TCTaskList at indexes 0..len-1 are not NULL.
|
||||||
*/
|
*/
|
||||||
struct TCTask *const *items;
|
struct TCTask **items;
|
||||||
} TCTaskList;
|
} TCTaskList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -385,7 +388,7 @@ typedef struct TCUuidList {
|
|||||||
* array of uuids. these remain owned by the TCUuidList instance and will be freed by
|
* array of uuids. these remain owned by the TCUuidList instance and will be freed by
|
||||||
* tc_uuid_list_free. This pointer is never NULL for a valid TCUuidList.
|
* tc_uuid_list_free. This pointer is never NULL for a valid TCUuidList.
|
||||||
*/
|
*/
|
||||||
const struct TCUuid *items;
|
struct TCUuid *items;
|
||||||
} TCUuidList;
|
} TCUuidList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -407,7 +410,7 @@ typedef struct TCStringList {
|
|||||||
* be freed by tc_string_list_free. This pointer is never NULL for a valid TCStringList, and the
|
* be freed by tc_string_list_free. This pointer is never NULL for a valid TCStringList, and the
|
||||||
* *TCStringList at indexes 0..len-1 are not NULL.
|
* *TCStringList at indexes 0..len-1 are not NULL.
|
||||||
*/
|
*/
|
||||||
const struct TCString *items;
|
struct TCString *items;
|
||||||
} TCStringList;
|
} TCStringList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -446,7 +449,7 @@ typedef struct TCUdaList {
|
|||||||
* array of UDAs. These remain owned by the TCUdaList instance and will be freed by
|
* array of UDAs. These remain owned by the TCUdaList instance and will be freed by
|
||||||
* tc_uda_list_free. This pointer is never NULL for a valid TCUdaList.
|
* tc_uda_list_free. This pointer is never NULL for a valid TCUdaList.
|
||||||
*/
|
*/
|
||||||
const struct TCUda *items;
|
struct TCUda *items;
|
||||||
} TCUdaList;
|
} TCUdaList;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -920,13 +923,26 @@ struct TCString tc_task_error(struct TCTask *task);
|
|||||||
*/
|
*/
|
||||||
void tc_task_free(struct TCTask *task);
|
void tc_task_free(struct TCTask *task);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take an item from a TCTaskList. After this call, the indexed item is no longer associated
|
||||||
|
* with the list and becomes the caller's responsibility, just as if it had been returned from
|
||||||
|
* `tc_replica_get_task`.
|
||||||
|
*
|
||||||
|
* The corresponding element in the `items` array will be set to NULL. If that field is already
|
||||||
|
* NULL (that is, if the item has already been taken), this function will return NULL. If the
|
||||||
|
* index is out of bounds, this function will also return NULL.
|
||||||
|
*
|
||||||
|
* The passed TCTaskList remains owned by the caller.
|
||||||
|
*/
|
||||||
|
struct TCTask *tc_task_list_take(struct TCTaskList *tasks, size_t index);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free a TCTaskList instance. The instance, and all TCTaskList it contains, must not be used after
|
* Free a TCTaskList instance. The instance, and all TCTaskList it contains, must not be used after
|
||||||
* this call.
|
* this call.
|
||||||
*
|
*
|
||||||
* When this call returns, the `items` pointer will be NULL, signalling an invalid TCTaskList.
|
* When this call returns, the `items` pointer will be NULL, signalling an invalid TCTaskList.
|
||||||
*/
|
*/
|
||||||
void tc_task_list_free(struct TCTaskList *tctasks);
|
void tc_task_list_free(struct TCTaskList *tasks);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free a TCUda instance. The instance, and the TCStrings it contains, must not be used
|
* Free a TCUda instance. The instance, and the TCStrings it contains, must not be used
|
||||||
|
|||||||
Reference in New Issue
Block a user