added vector_take_at() (and take_back()) which removes like pop() without destructor calling, and copies into supplied pointer - ownership transfer

This commit is contained in:
2026-08-19 22:02:43 +02:00
parent 4785b84227
commit b8d45ad32a
+58 -2
View File
@@ -30,6 +30,9 @@ Guarantees:
On a vector with an element destructor this is rejected with -1 instead, On a vector with an element destructor this is rejected with -1 instead,
as a byte-wise copy would leave two slots owning the same memory. To as a byte-wise copy would leave two slots owning the same memory. To
duplicate an owning element, copy what it owns yourself and push that. duplicate an owning element, copy what it owns yourself and push that.
- vector_take_at() and vector_take_back() transfer ownership of the element to the
caller, and do not call the destructor on it. It is the caller's responsibility
to free whatever the element owns.
Element destructors: Element destructors:
A vector may be given an element destructor with vector_set_destructor(). It is A vector may be given an element destructor with vector_set_destructor(). It is
@@ -37,6 +40,10 @@ called for every element that leaves the vector, i.e. by vector_pop_back(),
vector_pop_at(), vector_set() (on the element being overwritten), vector_clear() vector_pop_at(), vector_set() (on the element being overwritten), vector_clear()
and vector_destroy(). and vector_destroy().
The destructor is NOT called by vector_take_back() or vector_take_at(), as these
functions transfer ownership of the element to the caller. It is the caller's
responsibility to free whatever the element owns.
The destructor receives a pointer to the element's slot inside the vector's data The destructor receives a pointer to the element's slot inside the vector's data
array - NOT a pointer that was returned by malloc(). It must free whatever the array - NOT a pointer that was returned by malloc(). It must free whatever the
element owns, and must never free the pointer it was handed, as that memory element owns, and must never free the pointer it was handed, as that memory
@@ -103,6 +110,8 @@ static inline int vector_prune(vector_t* vec);
static inline int vector_push_back(vector_t* vec, const void* element); static inline int vector_push_back(vector_t* vec, const void* element);
static inline int vector_pop_back(vector_t* vec); static inline int vector_pop_back(vector_t* vec);
static inline int vector_pop_at(vector_t* vec, size_t index); static inline int vector_pop_at(vector_t* vec, size_t index);
static inline int vector_take_at(vector_t* vec, size_t index, void* out);
static inline int vector_take_back(vector_t* vec, void* out);
static inline int vector_insert(vector_t* vec, size_t index, const void* element); static inline int vector_insert(vector_t* vec, size_t index, const void* element);
static inline int vector_clear(vector_t* vec); static inline int vector_clear(vector_t* vec);
static inline int vector_set(vector_t* vec, size_t index, const void* element); static inline int vector_set(vector_t* vec, size_t index, const void* element);
@@ -329,7 +338,7 @@ static inline int vector_push_back(vector_t* vec, const void* element) {
@brief Removes the last element from the vector. @brief Removes the last element from the vector.
@param vec A pointer to the vector from which the element will be removed. @param vec A pointer to the vector from which the element will be removed.
@return 0 on success, -1 if the vector is NULL or empty. @return 0 on success, -1 if the vector is NULL or empty.
@attention After calling this function, the vector's size will be reduced by one. The memory occupied by the removed element will not be freed automatically unless a destructor is set. @attention After calling this function, the vector's size will be reduced by one. The memory occupied by the removed element will not be freed automatically unless a destructor is set (in which case, the destructor will be called on the element being removed if it is set). Use vector_take_back() to retrieve the last element, remove it from the vector, and NOT call the destructor on it (hands ownership to the caller).
*/ */
static inline int vector_pop_back(vector_t* vec) { static inline int vector_pop_back(vector_t* vec) {
if (!vec || vec->size == 0) { if (!vec || vec->size == 0) {
@@ -350,7 +359,7 @@ static inline int vector_pop_back(vector_t* vec) {
@param vec A pointer to the vector from which the element will be removed. @param vec A pointer to the vector from which the element will be removed.
@param index The index of the element to be removed. @param index The index of the element to be removed.
@return 0 on success, -1 if the vector is NULL or the index is out of bounds. @return 0 on success, -1 if the vector is NULL or the index is out of bounds.
@attention After calling this function, the vector's size will be reduced by one. The memory occupied by the removed element will not be freed automatically unless a destructor is set. @attention After calling this function, the vector's size will be reduced by one. The memory occupied by the removed element will not be freed automatically unless a destructor is set (in which case, the destructor will be called on the element being removed if it is set). Use vector_take_at() to retrieve the element at the specified index, remove it from the vector, and NOT call the destructor on it (hands ownership to the caller).
*/ */
static inline int vector_pop_at(vector_t* vec, size_t index) { static inline int vector_pop_at(vector_t* vec, size_t index) {
if (!vec || index >= vec->size) { if (!vec || index >= vec->size) {
@@ -371,6 +380,53 @@ static inline int vector_pop_at(vector_t* vec, size_t index) {
return 0; // Success return 0; // Success
} }
/*
@brief Removes the element at the specified index and transfers ownership of it to the caller.
@param vec A pointer to the vector from which the element will be taken.
@param index The index of the element to take.
@param out A pointer to a buffer of at least vector_element_size(vec) bytes, which the element is copied into.
@return 0 on success, -1 if the vector is NULL, out is NULL, out points into the vector's own data, or the index is out of bounds.
@attention The element destructor is deliberately NOT called. Whatever the element owns becomes the caller's responsibility to free.
@attention out must not point into the vector's own data array. Doing so would leave two slots owning the same memory and is rejected with -1.
*/
static inline int vector_take_at(vector_t* vec, size_t index, void* out) {
if (!vec || index >= vec->size || !out) {
return -1; // Invalid vector, index out of bounds, or output pointer is NULL
}
if (vector_is_aliased(vec, out)) {
return -1; // Refuse to take an element into a pointer that lives inside the vector
}
void* slot = (char*)vec->data + (index * vec->element_size);
memcpy(out, slot, vec->element_size); // Copy the element to the output pointer
// Move elements after the index one position to the left
if (index + 1 < vec->size) {
memmove(slot,
(char*)vec->data + ((index + 1) * vec->element_size),
(vec->size - index - 1) * vec->element_size);
}
vec->size--;
return 0; // Success
}
/*
@brief Removes the last element from the vector and transfers ownership of it to the caller.
@param vec A pointer to the vector from which the element will be taken.
@param out A pointer to a buffer of at least vector_element_size(vec) bytes, which the element is copied into.
@return 0 on success, -1 if the vector is NULL, empty, if out aliases the vector's data, or out is NULL.
@attention The element destructor is deliberately NOT called. Whatever the element owns becomes the caller's responsibility to free.
*/
static inline int vector_take_back(vector_t* vec, void* out) {
if (!vec || vec->size == 0 || !out) {
return -1; // Invalid vector, empty vector, or output pointer is NULL
}
return vector_take_at(vec, vec->size - 1, out); // Take the last element
}
/* /*
@brief Inserts an element at the specified index in the vector. @brief Inserts an element at the specified index in the vector.
@param vec A pointer to the vector into which the element will be inserted. @param vec A pointer to the vector into which the element will be inserted.