fixed vector_reserve() on the edge case where wrapping to 0 would return 0 as if the reservation succeeded - caused a heap overflow
This commit is contained in:
@@ -106,6 +106,7 @@ static inline vector_t* vector_create(size_t element_size);
|
||||
static inline int vector_set_destructor(vector_t* vec, vector_destructor_t destructor);
|
||||
static inline vector_destructor_t vector_get_destructor(const vector_t* vec);
|
||||
static inline int vector_reserve(vector_t* vec, size_t new_capacity);
|
||||
static inline int vector_grow(vector_t* vec);
|
||||
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);
|
||||
@@ -230,19 +231,24 @@ static inline vector_destructor_t vector_get_destructor(const vector_t* vec) {
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Reserves space for the specified capacity in the vector.
|
||||
@brief Ensures the vector has room for at least the specified number of elements.
|
||||
@param vec A pointer to the vector for which to reserve space.
|
||||
@param new_capacity The new capacity of the vector.
|
||||
@return 0 on success, -1 if the vector is NULL or allocation fails.
|
||||
@attention You cannot reserve space for zero or below the current size (doing so will return -1).
|
||||
@param new_capacity The minimum capacity the vector should have.
|
||||
@return 0 on success, -1 if the vector is NULL, its element size is zero, or allocation fails.
|
||||
@attention Capacity never decreases. If the vector already has room, this is a no-op and reports success.
|
||||
@attention If the buffer does grow, it is reallocated, so any pointers into the vector's data are invalidated. Use vector_prune() to release unused memory.
|
||||
*/
|
||||
static inline int vector_reserve(vector_t* vec, size_t new_capacity) {
|
||||
if (!vec) {
|
||||
return -1; // Invalid vector
|
||||
}
|
||||
|
||||
if (new_capacity == 0 || new_capacity < vec->size) {
|
||||
return -1; // Cannot reserve space for zero or below current size
|
||||
if (vec->element_size == 0) {
|
||||
return -1; // Not a usable vector; also guards the division below
|
||||
}
|
||||
|
||||
if (new_capacity <= vec->capacity) {
|
||||
return 0; // Already have the room. Never shrink
|
||||
}
|
||||
|
||||
if (new_capacity > SIZE_MAX / vec->element_size) {
|
||||
@@ -260,6 +266,25 @@ static inline int vector_reserve(vector_t* vec, size_t new_capacity) {
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Grows the vector's capacity to make room for at least one more element. Used internally by the functions that add elements.
|
||||
@param vec A pointer to the vector to grow.
|
||||
@return 0 on success, -1 if the vector is NULL or allocation fails.
|
||||
@attention This is an internal helper. You are not expected to call it directly.
|
||||
*/
|
||||
static inline int vector_grow(vector_t* vec) {
|
||||
if (!vec) {
|
||||
return -1; // Invalid vector
|
||||
}
|
||||
|
||||
if (vec->capacity > SIZE_MAX / 2) {
|
||||
return -1; // Prevent overflow
|
||||
}
|
||||
|
||||
size_t new_capacity = vec->capacity > 0 ? vec->capacity * 2 : 1; // Double the capacity, or set to 1 if it was 0
|
||||
return vector_reserve(vec, new_capacity);
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Prunes the vector to free unused memory. If the vector's size is less than its capacity, this function will reallocate the vector's data array to match its size, freeing any unused memory.
|
||||
@param vec A pointer to the vector to be pruned.
|
||||
@@ -310,16 +335,16 @@ static inline int vector_push_back(vector_t* vec, const void* element) {
|
||||
|
||||
if (vec->size >= vec->capacity) {
|
||||
// If the element lives inside our own data array, remember where it sits
|
||||
// so that we can find it again after the data has been reallocated
|
||||
// so that we can find it again after the data has been reallocated.
|
||||
// This must happen before the growth, while the old buffer is still valid
|
||||
size_t offset = 0;
|
||||
int aliased = vector_is_aliased(vec, element);
|
||||
if (aliased) {
|
||||
offset = (size_t)((const char*)element - (const char*)vec->data);
|
||||
}
|
||||
|
||||
// Reserve space for the vector to double its current capacity
|
||||
if (vector_reserve(vec, vec->capacity * 2) != 0) {
|
||||
return -1; // Reserve failed
|
||||
if (vector_grow(vec) != 0) {
|
||||
return -1; // Failed to grow the vector
|
||||
}
|
||||
|
||||
if (aliased) {
|
||||
@@ -460,8 +485,7 @@ static inline int vector_insert(vector_t* vec, size_t index, const void* element
|
||||
}
|
||||
|
||||
if (vec->size >= vec->capacity) {
|
||||
// Reserve space for the vector to double its current capacity
|
||||
if (vector_reserve(vec, vec->capacity * 2) != 0) {
|
||||
if (vector_grow(vec) != 0) {
|
||||
free(temp);
|
||||
return -1; // Reserve failed
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user