Skip to content

Commit de0b94f

Browse files
Add o1heapMinArenaSize, o1heapGetMaxAllocationSize() (#25)
1 parent d506b28 commit de0b94f

File tree

5 files changed

+62
-6
lines changed

5 files changed

+62
-6
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,8 @@ The list of intentional deviations can be obtained by simply searching the codeb
288288
### Next version WIP
289289

290290
- Add optional trace events, enabled via the `O1HEAP_TRACE` build-time option.
291+
- Add [`o1heapMinArenaSize`](https://github.com/pavel-kirienko/o1heap/issues/17#issuecomment-1518245157).
292+
- Add [`o1heapGetMaxAllocationSize`](https://github.com/pavel-kirienko/o1heap/issues/18).
291293

292294
### v2.1
293295

o1heap/o1heap.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
// Copyright (c) Pavel Kirienko
1515
// Authors: Pavel Kirienko <pavel.kirienko@zubax.com>
1616

17-
// ReSharper disable CppDFANullDereference
17+
// ReSharper disable CppDFANullDereference CppRedundantCastExpression
1818

1919
#include "o1heap.h"
2020
#include <assert.h>
@@ -258,11 +258,12 @@ O1HEAP_PRIVATE void unbin(O1HeapInstance* const handle, const Fragment* const fr
258258

259259
// ---------------------------------------- PUBLIC API IMPLEMENTATION ----------------------------------------
260260

261+
const size_t o1heapMinArenaSize = INSTANCE_SIZE_PADDED + FRAGMENT_SIZE_MIN;
262+
261263
O1HeapInstance* o1heapInit(void* const base, const size_t size)
262264
{
263265
O1HeapInstance* out = NULL;
264-
if ((base != NULL) && ((((size_t) base) % O1HEAP_ALIGNMENT) == 0U) &&
265-
(size >= (INSTANCE_SIZE_PADDED + FRAGMENT_SIZE_MIN)))
266+
if ((base != NULL) && ((((size_t) base) % O1HEAP_ALIGNMENT) == 0U) && (size >= o1heapMinArenaSize))
266267
{
267268
// Allocate the core heap metadata structure in the beginning of the arena.
268269
O1HEAP_ASSERT(((size_t) base) % sizeof(O1HeapInstance*) == 0U);
@@ -476,6 +477,14 @@ void o1heapFree(O1HeapInstance* const handle, void* const pointer)
476477
}
477478
}
478479

480+
size_t o1heapGetMaxAllocationSize(const O1HeapInstance* const handle)
481+
{
482+
O1HEAP_ASSERT(handle != NULL);
483+
// The largest allocation is smaller (up to almost two times) than the arena capacity,
484+
// due to the power-of-two padding and the fragment header overhead.
485+
return pow2(log2Floor(handle->diagnostics.capacity)) - O1HEAP_ALIGNMENT;
486+
}
487+
479488
bool o1heapDoInvariantsHold(const O1HeapInstance* const handle)
480489
{
481490
O1HEAP_ASSERT(handle != NULL);

o1heap/o1heap.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ typedef struct
6666
uint64_t oom_count;
6767
} O1HeapDiagnostics;
6868

69+
/// o1heapInit() will fail unless the arena size is at least this large.
70+
/// This value depends only on the machine architecture.
71+
/// The other reason to fail is if the arena pointer is not aligned at O1HEAP_ALIGNMENT.
72+
extern const size_t o1heapMinArenaSize;
73+
6974
/// The arena base pointer shall be aligned at O1HEAP_ALIGNMENT, otherwise NULL is returned.
7075
///
7176
/// The total heap capacity cannot exceed approx. (SIZE_MAX/2). If the arena size allows for a larger heap,
@@ -76,10 +81,13 @@ typedef struct
7681
/// own needs (normally about 40..600 bytes depending on the architecture, but this parameter is not characterized).
7782
/// A pointer to the newly initialized instance is returned.
7883
///
79-
/// If the provided space is insufficient, NULL is returned.
84+
/// The function fails and returns NULL iff:
85+
/// - The provided space is less than o1heapMinArenaSize.
86+
/// - The base pointer is not aligned at O1HEAP_ALIGNMENT.
87+
/// - The base pointer is NULL.
8088
///
81-
/// An initialized instance does not hold any resources. Therefore, if the instance is no longer needed,
82-
/// it can be discarded without any de-initialization procedures.
89+
/// An initialized instance does not hold any resources except for the arena memory.
90+
/// Therefore, if the instance is no longer needed, it can be discarded without any de-initialization procedures.
8391
///
8492
/// The heap is not thread-safe; external synchronization may be required.
8593
O1HeapInstance* o1heapInit(void* const base, const size_t size);
@@ -104,6 +112,10 @@ void* o1heapAllocate(O1HeapInstance* const handle, const size_t amount);
104112
/// The function is executed in constant time.
105113
void o1heapFree(O1HeapInstance* const handle, void* const pointer);
106114

115+
/// Obtains the maximum theoretically possible allocation size for this heap instance.
116+
/// This is useful when implementing std::allocator_traits<Alloc>::max_size.
117+
size_t o1heapGetMaxAllocationSize(const O1HeapInstance* const handle);
118+
107119
/// Performs a basic sanity check on the heap.
108120
/// This function can be used as a weak but fast method of heap corruption detection.
109121
/// If the handle pointer is NULL, the behavior is undefined.

tests/internal.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ struct O1HeapInstance final
167167
validate();
168168
}
169169

170+
[[nodiscard]] auto getMaxAllocationSize() const
171+
{
172+
return o1heapGetMaxAllocationSize(reinterpret_cast<const ::O1HeapInstance*>(this));
173+
}
174+
170175
[[nodiscard]] auto doInvariantsHold() const
171176
{
172177
return o1heapDoInvariantsHold(reinterpret_cast<const ::O1HeapInstance*>(this));

tests/test_general.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,34 @@ TEST_CASE("General: random A")
562562
}
563563
}
564564

565+
TEST_CASE("General: min arena size")
566+
{
567+
alignas(128U) std::array<std::byte, 1024> arena{};
568+
REQUIRE(nullptr == init(arena.data(), o1heapMinArenaSize - 1U));
569+
REQUIRE(nullptr != init(arena.data(), o1heapMinArenaSize));
570+
}
571+
572+
TEST_CASE("General: max allocation size")
573+
{
574+
alignas(128U) std::array<std::byte, 4096U + sizeof(internal::O1HeapInstance) + O1HEAP_ALIGNMENT - 1U> arena{};
575+
{
576+
auto heap = init(arena.data(), std::size(arena));
577+
REQUIRE(heap != nullptr);
578+
REQUIRE(heap->diagnostics.capacity == 4096);
579+
REQUIRE(4096 - O1HEAP_ALIGNMENT == heap->getMaxAllocationSize());
580+
REQUIRE(nullptr == heap->allocate(heap->getMaxAllocationSize() + 1));
581+
REQUIRE(nullptr != heap->allocate(heap->getMaxAllocationSize() + 0));
582+
}
583+
{
584+
auto heap = init(arena.data(), std::size(arena) - O1HEAP_ALIGNMENT);
585+
REQUIRE(heap != nullptr);
586+
REQUIRE(heap->diagnostics.capacity < 4095);
587+
REQUIRE(2048 - O1HEAP_ALIGNMENT == heap->getMaxAllocationSize());
588+
REQUIRE(nullptr == heap->allocate(heap->getMaxAllocationSize() + 1));
589+
REQUIRE(nullptr != heap->allocate(heap->getMaxAllocationSize() + 0));
590+
}
591+
}
592+
565593
TEST_CASE("General: invariant checker")
566594
{
567595
using internal::Fragment;

0 commit comments

Comments
 (0)