From b8d45ad32a182e94dae800ec325b5f2791276194 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Wed, 19 Aug 2026 22:02:43 +0200 Subject: [PATCH] added vector_take_at() (and take_back()) which removes like pop() without destructor calling, and copies into supplied pointer - ownership transfer --- vector.h | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/vector.h b/vector.h index 8416300..a0c0d8a 100644 --- a/vector.h +++ b/vector.h @@ -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.