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,
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.
- 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:
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()
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
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
@@ -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_pop_back(vector_t* vec);
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_clear(vector_t* vec);
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.
@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.
@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) {
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 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.
@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) {
if (!vec || index >= vec->size) {
@@ -371,6 +380,53 @@ static inline int vector_pop_at(vector_t* vec, size_t index) {
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.
@param vec A pointer to the vector into which the element will be inserted.