Skip to content

Latest commit

 

History

History
154 lines (136 loc) · 9.58 KB

File metadata and controls

154 lines (136 loc) · 9.58 KB

Changelog — v0.3.0

Immutable release entry — part of the project changelog. One file per release; format and rationale in ADR-0038. Released entries are never edited.

0.3.0 — 2026-06-13

Milestone 3 — C++ Wrapper & Type Safety. A C++-ergonomics milestone layered on the v0.2.0 single-threaded core: the C ABI and its O(1) algorithm are unchanged. The C++ surface gains a resolved exception policy at the C/C++ boundary (the dual-verb allocate / try_allocate convention, with the Pool constructor now throwing std::bad_alloc on failure), the type-safe TypedPool<T>, an STL-compatible PoolAllocator<T> Adapter that lets standard and custom containers draw storage from a pool, and a read-only free-list diagnostic Iterator gated out of release builds. Four new Architecture Decision Records (0016–0019) take the running total to 19, and the patterns catalogue gains Adapter and Iterator as Implemented. No Spec Coverage Map row flips: Milestone 3 is C++-side ergonomics over an already-✅ core — §2.2's "std::bad_alloc (C++)" half is now satisfied by ADR-0016, but the row stays 🚧 pending the dynamic-growth half (Milestone 5). Full release notes in docs/releases/v0.3.0.md.

Added (M3.1)

  • ADR-0016 — exception policy at the C/C++ boundary: the C ABI is exception-free forever (every C failure is NULL / no-op), and the C++ surface adopts a dual-verb convention where the spec §2.2 "configurable knob" is resolved per call site, not per build.
  • Pool::try_allocate() — new noexcept allocation verb returning nullptr on exhaustion or on an empty (moved-from) wrapper; the exact Pool::allocate() semantics of v0.2.0.

Added (M3.2)

  • ADR-0017 and typed_pool.hppit::d4np::memorypool::TypedPool<T>, the header-only type-safe pool: the spec-conformant block_size is derived from T at compile time (ADR-0009 §2 satisfied by construction, over-aligned T rejected with a static_assert), the typed storage verbs follow the ADR-0016 dual-verb policy, and the construct / destroy object-lifetime pair offers the strong exception guarantee on throwing T constructors. Dedicated typed_pool CTest binary with eight TEST_CASEs.

Added (M3.3)

  • ADR-0018 and pool_allocator.hppit::d4np::memorypool::PoolAllocator<T>, the header-only STL-compatible allocator Adapter. It satisfies the Cpp17Allocator requirements and is a non-owning back-reference to a Pool (single Pool* member, sizeof == sizeof(void*); the pool must out-live every container and adapter copy). Single-block requests (n == 1, fitting, not over-aligned) route to the pool in O(1) — std::bad_alloc on exhaustion per ADR-0016 §2 — while everything else (n > 1, oversized / over-aligned T, rebound nodes larger than the block) falls back to over-aligned ::operator new / ::operator delete. The routing predicate is a pure function of (n, sizeof(T), alignof(T), block_size), so deallocate returns each pointer through the path that allocated it with no per-pointer bookkeeping. std::list / std::map / std::set run on the pool fast path; std::vector runs on the fallback.
  • Propagation traits specified per ADR-0018 §4: propagate_on_container_copy_assignment, propagate_on_container_move_assignment, and propagate_on_container_swap are all std::false_type; is_always_equal is std::false_type (stateful); operator== compares the underlying Pool identity.
  • Public C function memory_pool_block_size(const memory_pool_t*) — reports the configured per-block size, O(1), NULL-tolerant (returns 0), ANSI C C89-compatible; the introspection companion to memory_pool_metadata_bytes that backs the adapter's size-fit decision (ADR-0018 §3). C++ forwarder [[nodiscard]] std::size_t Pool::block_size() const noexcept added in lock-step; the accessor is exercised by c_consumer_min.c under the C89/C99 CI jobs.
  • Dedicated pool_allocator CTest binary (pool_allocator_test.cpp) with seven TEST_CASEs: pool-fast-path exhaustion, multi-block + oversized-T fallback leaving the pool untouched, equality / statefulness / rebinding, the propagation-trait static_asserts, and end-to-end std::list (pool path) + std::vector (fallback) round-trips.
  • Adapter added to docs/patterns/README.md Adopted / Planned table as row #5, status Implemented.

Added (M3.4)

  • ADR-0019 and free_list_iterator.hppit::d4np::memorypool::FreeListIterator / FreeListView, a read-only Iterator (LegacyForwardIterator) over the implicit free list for diagnostics. value_type is const void* (a free-slot address); FreeListView is the range adaptor (begin() / end(), constructible from a const memory_pool_t* or a Pool&) so the walk composes with range-for, std::distance, std::find, and the rest of <algorithm>. Diagnostics-only: the walk is O(free_count) and must never touch the allocation hot path.
  • The entire diagnostic surface is gated behind the PBR_MEMORY_POOL_DIAGNOSTICS macro (ADR-0019 §1), defaulting to 1 in debug builds (!NDEBUG) and 0 in release builds (NDEBUG); an explicit definition wins. The new CMake option PBR_MEMORY_POOL_ENABLE_DIAGNOSTICS (default OFF) is the documented opt-in — when ON it forces the macro to 1 as a PUBLIC compile definition on pbr_memory_pool so the library and every linking consumer agree.
  • Three gated public C accessors backing the traversal: memory_pool_debug_free_list_head, memory_pool_debug_free_list_next, and memory_pool_debug_free_count — NULL-tolerant, const-correct, ANSI C C89-compatible. They keep the ADR-0009 §1 next-link layout encapsulated inside memory_pool.cpp (Pimpl boundary intact), and are exercised by c_consumer_min.c under the C89/C99 CI jobs (under the same macro guard).
  • Dedicated free_list_iterator CTest binary (free_list_iterator_test.cpp) with five cases when diagnostics are enabled (ascending strided walk of a fresh pool with free_count vs std::distance cross-check, allocation shrinks the list, a freed block returns to the head and is walked first, exhausted-pool empty range, LegacyForwardIterator behaviours) plus a placeholder case when the surface is gated out, so the binary builds in every configuration.
  • Iterator added to docs/patterns/README.md Adopted / Planned table as row #6, status Implemented.

Added (M3.5)

  • Dedicated container_integration CTest binary (container_integration_test.cpp) exercising the M3.3 PoolAllocator<T> (ADR-0018) end-to-end through std::list (pool fast path, with diagnostics-gated free-count delta assertions and std::string elements for non-trivial construct/destroy), std::vector (heap fallback, contents/growth/copy/<algorithm> interop), and a small hand-written allocator-aware ForwardList<T, Allocator> driven with both std::allocator and PoolAllocator. Seven TEST_CASEs.
  • The pool-sizing recipe for node-based containers (deferred to M3.5 by ADR-0018 §3) is documented as a worked example in the test file header: block_size ≥ sizeof(rebound node); an undersized pool degrades safely to the heap fallback. The comprehensive README usage section remains M7.2.

Changed (M3.1)

  • Breaking (pre-1.0): Pool::allocate() now throws std::bad_alloc on exhaustion (and on a moved-from wrapper) instead of returning nullptr — migration: allocate()try_allocate() for the in-band-failure behaviour.
  • Breaking (pre-1.0): the Pool(block_size, block_count) constructor now throws std::bad_alloc when the underlying memory_pool_create fails, retiring the ADR-0010 §2 silent-empty-state semantics — migration: use Pool::make or PoolBuilder::build for failure-as-a-value construction. Pool::make is restructured around a private adopt-handle constructor so the non-throwing path contains no try/catch.
  • The microbenchmark's timed loops call try_allocate() instead of allocate() — the apples-to-apples comparison against malloc's in-band NULL, byte-identical to the code path that produced the committed v0.2.0 numbers (ADR-0016 §4).

Spec Coverage Map flips

None. Milestone 3 adds C++-side ergonomics over the already-✅ single-threaded core, so no traceability row changes state. ADR-0016 satisfies the "std::bad_alloc (C++)" clause of §2.2, but that row stays 🚧 because its dynamic-growth clause lands in Milestone 5. Coverage at the close of Milestone 3 is unchanged from v0.2.0: eight ✅, one 🚧 (§6.3), and §2.2 / §2.4 / §6.3-concurrent in flight for Milestones 4–5.