Abort instead of calling std::bad_alloc

This commit is contained in:
2025-08-18 11:04:45 -04:00
parent b5cb4d2a81
commit f4cbfc0c4f
2 changed files with 35 additions and 12 deletions

View File

@@ -3,12 +3,14 @@
#include <algorithm> #include <algorithm>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <limits> #include <limits>
#include <new> #include <new>
#include <type_traits> #include <type_traits>
#include <typeinfo>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -81,16 +83,25 @@ private:
* @param size Size of the data area for this block * @param size Size of the data area for this block
* @param prev Pointer to the previous block (nullptr for first block) * @param prev Pointer to the previous block (nullptr for first block)
* @return Pointer to the newly created block * @return Pointer to the newly created block
* @throws std::bad_alloc if memory allocation fails * @note Prints error to stderr and calls std::abort() if memory allocation
* fails
*/ */
static Block *create(size_t size, Block *prev) { static Block *create(size_t size, Block *prev) {
if (size > std::numeric_limits<uint32_t>::max()) { if (size > std::numeric_limits<uint32_t>::max()) {
throw std::bad_alloc(); std::fprintf(
stderr,
"ArenaAllocator: Block size %zu exceeds maximum uint32_t value\n",
size);
std::abort();
} }
void *memory = std::aligned_alloc( void *memory = std::aligned_alloc(
alignof(Block), align_up(sizeof(Block) + size, alignof(Block))); alignof(Block), align_up(sizeof(Block) + size, alignof(Block)));
if (!memory) { if (!memory) {
throw std::bad_alloc(); std::fprintf(
stderr,
"ArenaAllocator: Failed to allocate memory block of size %zu\n",
size);
std::abort();
} }
size_t total_size = size + (prev ? prev->total_size : 0); size_t total_size = size + (prev ? prev->total_size : 0);
size_t total_used = prev ? prev->total_used + prev->offset : 0; size_t total_used = prev ? prev->total_used + prev->offset : 0;
@@ -155,7 +166,8 @@ public:
* @param size Number of bytes to allocate (0 returns nullptr) * @param size Number of bytes to allocate (0 returns nullptr)
* @param alignment Required alignment (default: alignof(std::max_align_t)) * @param alignment Required alignment (default: alignof(std::max_align_t))
* @return Pointer to allocated memory, or nullptr if size is 0 * @return Pointer to allocated memory, or nullptr if size is 0
* @throws std::bad_alloc if memory allocation fails * @note Prints error to stderr and calls std::abort() if memory allocation
* fails
* *
* ## Performance: * ## Performance:
* - O(1) amortized allocation time * - O(1) amortized allocation time
@@ -210,7 +222,8 @@ public:
* `alignof(std::max_align_t)` * `alignof(std::max_align_t)`
* @return Pointer to the reallocated memory (may be the same as ptr or * @return Pointer to the reallocated memory (may be the same as ptr or
* different) * different)
* @throws std::bad_alloc if memory allocation fails * @note Prints error to stderr and calls std::abort() if memory allocation
* fails
* *
* ## Behavior: * ## Behavior:
* - If new_size == old_size, returns ptr unchanged * - If new_size == old_size, returns ptr unchanged
@@ -236,12 +249,17 @@ public:
* @param new_size Desired new size in number of T objects * @param new_size Desired new size in number of T objects
* @return Pointer to the reallocated memory (may be the same as ptr or * @return Pointer to the reallocated memory (may be the same as ptr or
* different) * different)
* @throws std::bad_alloc if memory allocation fails or size overflow occurs * @note Prints error to stderr and calls std::abort() if memory allocation
* fails or size overflow occurs
*/ */
template <typename T> template <typename T>
T *realloc(T *ptr, uint32_t old_size, uint32_t new_size) { T *realloc(T *ptr, uint32_t old_size, uint32_t new_size) {
if (size_t(new_size) * sizeof(T) > std::numeric_limits<uint32_t>::max()) { if (size_t(new_size) * sizeof(T) > std::numeric_limits<uint32_t>::max()) {
throw std::bad_alloc(); std::fprintf(stderr,
"ArenaAllocator: Reallocation size overflow for type %s "
"(new_size=%u, sizeof(T)=%zu)\n",
typeid(T).name(), new_size, sizeof(T));
std::abort();
} }
return static_cast<T *>(realloc_raw(ptr, old_size * sizeof(T), return static_cast<T *>(realloc_raw(ptr, old_size * sizeof(T),
new_size * sizeof(T), alignof(T))); new_size * sizeof(T), alignof(T)));
@@ -257,7 +275,8 @@ public:
* @tparam Args Types of constructor arguments * @tparam Args Types of constructor arguments
* @param args Arguments to forward to T's constructor * @param args Arguments to forward to T's constructor
* @return Pointer to the constructed object * @return Pointer to the constructed object
* @throws std::bad_alloc if memory allocation fails * @note Prints error to stderr and calls std::abort() if memory allocation
* fails
* *
* ## Type Requirements: * ## Type Requirements:
* T must be trivially destructible (std::is_trivially_destructible_v<T>). * T must be trivially destructible (std::is_trivially_destructible_v<T>).
@@ -293,7 +312,8 @@ public:
* @param size Number of T objects to allocate space for * @param size Number of T objects to allocate space for
* @return Pointer to allocated memory suitable for constructing an array of T * @return Pointer to allocated memory suitable for constructing an array of T
* objects * objects
* @throws std::bad_alloc if memory allocation fails * @note Prints error to stderr and calls std::abort() if memory allocation
* fails
* *
* ## Type Requirements: * ## Type Requirements:
* T must be trivially destructible (std::is_trivially_destructible_v<T>). * T must be trivially destructible (std::is_trivially_destructible_v<T>).
@@ -315,7 +335,11 @@ public:
return nullptr; return nullptr;
} }
if (size_t(size) * sizeof(T) > std::numeric_limits<uint32_t>::max()) { if (size_t(size) * sizeof(T) > std::numeric_limits<uint32_t>::max()) {
throw std::bad_alloc(); std::fprintf(stderr,
"ArenaAllocator: Allocation size overflow for type %s "
"(size=%u, sizeof(T)=%zu)\n",
typeid(T).name(), size, sizeof(T));
std::abort();
} }
void *ptr = allocate_raw(sizeof(T) * size, alignof(T)); void *ptr = allocate_raw(sizeof(T) * size, alignof(T));
return static_cast<T *>(ptr); return static_cast<T *>(ptr);

View File

@@ -2,7 +2,6 @@
#include "../benchmarks/test_data.hpp" #include "../benchmarks/test_data.hpp"
#include "parser_comparison.hpp" #include "parser_comparison.hpp"
#include <doctest/doctest.h> #include <doctest/doctest.h>
#include <vector>
/** /**
* @brief Test helper that uses parser comparison to validate both parsers. * @brief Test helper that uses parser comparison to validate both parsers.
@@ -479,4 +478,4 @@ TEST_CASE("Parser Comparison - Stress Tests") {
std::string stress_json = weaseldb::test_data::generate_large_json(50); std::string stress_json = weaseldb::test_data::generate_large_json(50);
test_parser_comparison(stress_json, "Deep nesting with many operations"); test_parser_comparison(stress_json, "Deep nesting with many operations");
} }
} }