xo-alloc2: ++ documentation

This commit is contained in:
Roland Conybeare 2025-12-24 01:29:57 -05:00
commit 0136044ba2
18 changed files with 474 additions and 37 deletions

View file

@ -0,0 +1,67 @@
.. _AAllocIterator-reference:
AAllocIterator Reference
========================
Abstract interface facet for an alloc iterator.
Base class for runtime polymorphism over alloc-iterator implementations,
using faceted object model.
* runtime size consists of vtable pointer only.
* per FOMO principles, runtime state is stored separately.
Classes that inherit AAllocIterator will not add state.
Context
-------
.. ditaa::
:--scale: 0.99
+----------------------+-------------------------+-----------------------------------+
| RAllocator | RAllocIterator | IAllocator_DArena |
| | | IAllocIterator_DArenaIterator |
+----------------------+-------------------------+-----------------------------------+
| IAllocator_Xfer | IAllocIterator_Xfer | DArena |
| IAllocator_Any | IAllocIterator_Any | DArenaIterator |
| IAllocator_Impltype | IAllocIterator_Impltype | |
| | | |
+----------------------+-------------------------+-----------------------------------+
| AAllocator | AAllocIterator cBLU| ArenaConfig |
+----------------------+-------------------------+-----------------------------------+
+-----------------+----------------------------------------------+-------------------+
| | AllocInfo | |
| +----------------------------------------------+ |
| AllocError | AllocHeaderConfig | cmpresult |
| +----------------------------------------------+ |
| | AllocHeader | |
+-----------------+----------------------------------------------+-------------------+
Application code will likely use:
.. code-block:: cpp
#include <xo/alloc2/AllocIterator.hpp>
to get definitions for cooperating AllocIterator classes
:cpp:class:`xo::mm::AAllocIterator`,
:cpp:class:`xo::mm::IAllocIterator_Any`,
:cpp:class:`xo::mm::IAllocIterator_Xfer`,
:cpp:class:`xo::mm::RAllocator`
Instead, to get just :cpp:class:`xo::mm::AAllocIterator` definition:
.. code-block:: cpp
#include <xo/alloc2/AAllocIterator.hpp>
Class
-----
.. doxygenclass:: xo::mm::AAllocIterator
Methods
-------
.. doxygengroup:: mm-allociterator-methods

View file

@ -11,25 +11,50 @@ Context
.. ditaa::
:--scale: 0.99
+--------------------------------+
| IAllocator_DArena |
+--------------------------------+
| IAllocator_Xfer |
+--------------------------------+
| IAllocator_ImplType |
+--------------+-----------------+
| | DArena |
| AAllocator +-----------------+
| | ArenaConfig cBLU|
+--------------+-----------------+
+----------------------+-------------------------+-----------------------------------+
| RAllocator | RAllocIterator | IAllocator_DArena |
| | | IAllocIterator_DArenaIterator |
+----------------------+-------------------------+-----------------------------------+
| IAllocator_Xfer | IAllocIterator_Xfer | DArena |
| IAllocator_Any | IAllocIterator_Any | DArenaIterator |
| IAllocator_Impltype | IAllocIterator_Impltype | |
| | | |
+----------------------+-------------------------+-----------------------------------+
| AAllocator | AAllocIterator | ArenaConfig cBLU|
+----------------------+-------------------------+-----------------------------------+
+-----------------+----------------------------------------------+-------------------+
| | AllocInfo | |
| +----------------------------------------------+ |
| AllocError | AllocHeaderConfig | cmpresult |
| +----------------------------------------------+ |
| | AllocHeader | |
+-----------------+----------------------------------------------+-------------------+
.. uml::
:caption: example arena config
:scale: 99%
:align: center
object cfg<<AreanConfig>>
cfg : name = "tmp"
cfg : size = 128MB
cfg : hugepage_z = 2MB
cfg : guard_z = 8
cfg : guard_byte = 0xfd
cfg : store_header_flag = true
cfg : header_size_mask = 0xffffffff
cfg : debug_flag = false
.. code-block:: cpp
#include <xo/alloc2/DArena.hpp>
#include <xo/alloc2/ArenaConfig.hpp>
Class
-----
.. doxygenclass:: xo::mm::ArenaConfig
.. doxygenclass:: mm-arenaconfig-instance-vars
Instance Variables
------------------
.. doxygengroup:: mm-arenaconfig-instance-vars

View file

@ -8,9 +8,12 @@ xo_docdir_sphinx_config(
examples.rst
implementation.rst
AAllocator-reference.rst
IAllocator_Xfer-reference.rst
AAllocIterator-reference.rst
ArenaConfig-reference.rst
DArena-reference.rst
AllocInfo-reference.rst
cmpresult-reference.rst
#install.rst
#introduction.rst
#implementation.rst

View file

@ -1,9 +1,9 @@
.. _DArena-reference:
DArena Reference
================
DArena
======
Native representation for arena allocator
Native arena allocator
Context
-------
@ -34,7 +34,6 @@ Context
#include <xo/alloc2/DArena.hpp>
Arena memory layout
~~~~~~~~~~~~~~~~~~~

View file

@ -0,0 +1,55 @@
.. _DArenaIterator-reference:
DArenaIterator
==============
Iterator for allocs obtained from a :cpp:class:`DArena`.
Context
-------
.. ditaa::
:--scale: 0.99
+----------------------+-------------------------+-----------------------------------+
| RAllocator | RAllocIterator | IAllocator_DArena |
| | | IAllocIterator_DArenaIterator |
+----------------------+-------------------------+-----------------------------------+
| IAllocator_Xfer | IAllocIterator_Xfer | DArena |
| IAllocator_Any | IAllocIterator_Any +-----------------------------------+
| IAllocator_Impltype | IAllocIterator_Impltype | DArenaIterator |
| | | cBLU|
+----------------------+-------------------------+-----------------------------------+
| AAllocator | AAllocIterator | ArenaConfig |
+----------------------+-------------------------+-----------------------------------+
+-----------------+----------------------------------------------+-------------------+
| | AllocInfo | |
| +----------------------------------------------+ |
| AllocError | AllocHeaderConfig | cmpresult |
| +----------------------------------------------+ |
| | AllocHeader | |
+-----------------+----------------------------------------------+-------------------+
.. code-block:: cpp
#include <xo/alloc2/DArenaIterator.hpp>
Class
-----
.. doxygenclass:: xo::mm::DArenaIterator
Member Variables
----------------
.. doxygengroup:: mm-arenaiterator-instance-vars
Constructors
------------
.. doxygengroup:: mm-arenaiterator-ctors
Methods
-------
.. doxygengroup:: mm-arenaiterator-methods

View file

@ -0,0 +1,73 @@
.. _IAllocator_Xfer-reference:
IAllocator_Xfer
===============
IAllocator_Xfer provides a type-erased interface to a specific native allocator
implementation.
Context
-------
.. ditaa::
:--scale: 0.99
+----------------------+-------------------------+-----------------------------------+
| RAllocator | RAllocIterator | IAllocator_DArena |
| | | IAllocIterator_DArenaIterator |
+----------------------+-------------------------+-----------------------------------+
|cBLU IAllocator_Xfer | IAllocIterator_Xfer | DArena |
+----------------------+ IAllocIterator_Any | DArenaIterator |
| IAllocator_Any | IAllocIterator_Impltype | |
| IAllocator_Impltype | | |
+----------------------+-------------------------+-----------------------------------+
| AAllocator | AAllocIterator | ArenaConfig |
+----------------------+-------------------------+-----------------------------------+
+-----------------+----------------------------------------------+-------------------+
| | AllocInfo | |
| +----------------------------------------------+ |
| AllocError | AllocHeaderConfig | cmpresult |
| +----------------------------------------------+ |
| | AllocHeader | |
+-----------------+----------------------------------------------+-------------------+
Each method operates by downcasting its first argument to the known target
native interface type, and delegating to native interface method with the same name.
For example (paraphrasing):
.. code-block:: cpp
template <typename DRepr, typename IAllocator_DRepr>
bool IAllocator_Xfer<DRepr, IAllocator_DRepr>::contains(Copaque d,
const void * p) const ...
{
return IAllocator_DRepr::contains(*(DRepr*)(d), p);
};
Code for other methods follows the same pattern;
in fact expect to be able to generate class definition mechanically at some point.
Application code will not normally interact with :cpp:class:`IAllocator_Xfer`.
It will be used once for each specific allocator implementation
(e.g. with :cpp:class:`xo::mm::IAllocator_DArena`).
In any case, can include the transfer template with:
.. code-block:: cpp
#include <xo/alloc2/IAllocator_Xfer.hpp>
Class
-----
.. doxygenclass:: xo::mm::IAllocator_Xfer
Types
-----
.. doxygengroup:: mm-allocator-xfer-types
Methods
-------
.. doxygengroup:: mm-allocator-xfer-methods

View file

@ -0,0 +1,55 @@
.. _cmpresult-reference:
cmpresult
=========
Represent the result of a partially ordered comparison
Context
-------
.. ditaa::
:--scale: 0.99
+----------------------+-------------------------+-----------------------------------+
| RAllocator | RAllocIterator | IAllocator_DArena |
| | | IAllocIterator_DArenaIterator |
+----------------------+-------------------------+-----------------------------------+
| IAllocator_Xfer | IAllocIterator_Xfer | DArena |
| IAllocator_Any | IAllocIterator_Any | DArenaIterator |
| IAllocator_Impltype | IAllocIterator_Impltype | |
| | | |
+----------------------+-------------------------+-----------------------------------+
| AAllocator | AAllocIterator | ArenaConfig |
+----------------------+-------------------------+-----------------------------------+
+-----------------+----------------------------------------------+-------------------+
| | AllocInfo | |
| +----------------------------------------------+ |
| AllocError | AllocHeaderConfig | cmpresult |
| +----------------------------------------------+ |
| | AllocHeader | cBLU |
+-----------------+----------------------------------------------+-------------------+
.. code-block:: cpp
#include <xo/alloc2/cmpresult.hpp>
Class
-----
.. doxygenclass:: xo::mm::cmpresult
Constructors
------------
.. doxgyengroup:: mm-cmpresult-ctors
Methods
-------
.. doxygengroup:: mm-cmpresult-methods
Member Variables
----------------
.. doxygengroup:: mm-cmpresult-instance-vars

View file

@ -84,6 +84,17 @@ Given a successful allocation:
info.guard_lo(); // guard bytes preceding alloc
info.guard_hi(); // guard bytes following alloc
Can alternatively scan all live allocs in arena:
.. code-block:: cpp
for (AllocInfo info : arena) {
info.payload(); // allocated memory range
info.is_valid(); // true
info.guard_lo(); // guard bytes preceding alloc
info.guard_hi(); // guard bytes following alloc
}
Recycling memory
----------------
@ -102,8 +113,10 @@ Recycling memory
arena.allocated(); // 0k
arena.available(); // 8k
Memory released by @ref Darena::clear is still committed.
It's in use as far as operating system is concerned.
Memory recycled by :cpp:func:`DArena::clear()`
is available for reuse by application; it's still owned by arena.
We're just resetting the free pointer back to the beginning of arena
memory.
To release memory to the operating system, destroy arena:

View file

@ -24,9 +24,13 @@ Implemented using FOMO (faceted rust-like object model) from xo-facet
examples
implementation
AAllocator-reference
IAllocator_Xfer-reference
AAllocIterator-reference
ArenaConfig-reference
DArena-reference
DArenaIterator-reference
AllocInfo-reference
cmpresult-reference
glossary
genindex
search

View file

@ -0,0 +1,70 @@
/** @file ArenaConfig.hpp
*
* @author Roland Conybeare, Dec 2025
**/
#pragma once
#include <string>
#include <cstdint>
namespace xo {
namespace mm {
/** @class ArenaConfig
* @brief configuration for a @ref DArena instance
**/
struct ArenaConfig {
/** @defgroup mm-arenaconfig-ctors ArenaConfig ctors **/
///@{
/** create anonymous arena with size @p z **/
static ArenaConfig simple(std::size_t z);
///@}
/** @defgroup mm-arenaconfig-instance-vars ArenaConfig members **/
///@{
/** optional name, for diagnostics **/
std::string name_;
/** desired arena size -- hard max = reserved virtual memory **/
std::size_t size_;
/** hugepage size -- using huge pages relieves some TLB pressure
* (provided you use their full extent :)
**/
std::size_t hugepage_z_ = 2 * 1024 * 1024;
/** if non-zero, allocate extra space between allocs, and fill
* with fixed test-pattern contents. Allows for simple
* runtime arena sanitizing checks.
* Will be rounded up to multiple of @ref padding::c_alloc_alignment
**/
std::size_t guard_z_ = 0;
/** if guard_z_ > 0, write at least that many copies
* of this guard byte following each complete allocation
**/
std::uint8_t guard_byte_ = 0xfd;
/** if store_header_flag_ is true: mask bits for allocation size.
* remaining bits can be stolen for other purposes
* otherwise ignored
**/
/** true to store header (8 bytes) at the beginning of each allocation.
* necessary and sufficient to allows iterating over allocs
* present in arena
**/
bool store_header_flag_ = false;
/** mask applied to 8-byte alloc header.
* bits set to 1 store alloc size; remaining
* bits in alloc header can be used for other purposes.
**/
std::uint64_t header_size_mask_ = 0xffffffff;
/** true to enable debug logging **/
bool debug_flag_ = false;
///@}
};
} /*namespace mm*/
} /*namespace xo*/
/* end ArenaConfig.hpp */

View file

@ -23,6 +23,8 @@ namespace xo {
struct AAllocIterator {
using obj_AAllocIterator = xo::facet::obj<AAllocIterator>;
/** @defgroup mm-allociterator-methods AllocIterator methods **/
///@{
/** RTTI: unique id# for actual runtime *data* representation **/
virtual int32_t _typeseq() const noexcept = 0;
/** retrieve AllocInfo for current iterator position
@ -33,6 +35,7 @@ namespace xo {
const obj_AAllocIterator & other) const noexcept = 0;
/** advance iterator to next position **/
virtual void next(Opaque d) const noexcept = 0;
///@}
};
} /*namespace mm*/
} /*namespace xo*/

View file

@ -11,15 +11,24 @@ namespace xo {
namespace mm {
/** @class IAllocator_Xfer
*
* Adapts typed allocator implementation @tparam IAllocator_DRepr
* @tparam DRepr target representation
* @tparam IAllocator_DRepr typed interface for @p DRepr
*
* Adapts typed allocator implementation @p IAllocator_DRepr
* to type-erased @ref AAllocator interface
**/
template <typename DRepr, typename IAllocator_DRepr>
struct IAllocator_Xfer : public AAllocator {
/** @defgroup mm-allocator-xfer-types **/
///@{
// parallel interface to AAllocator, with specific data type
using Impl = IAllocator_DRepr;
using size_type = AAllocator::size_type;
using value_type = AAllocator::value_type;
///@}
/** @defgroup mm-allocator-xfer-methods IAllocator_Xfer methods **/
///@{
static const DRepr & _dcast(Copaque d) { return *(const DRepr *)d; }
static DRepr & _dcast(Opaque d) { return *(DRepr *)d; }
@ -28,6 +37,7 @@ namespace xo {
// const methods
/** return typeseq for @tparam DRepr **/
int32_t _typeseq() const noexcept override { return s_typeseq; }
std::string_view name(Copaque d) const noexcept override { return I::name(_dcast(d)); }
size_type reserved(Copaque d) const noexcept override { return I::reserved(_dcast(d)); }
@ -59,6 +69,7 @@ namespace xo {
}
void clear(Opaque d) const override { return I::clear(_dcast(d)); }
void destruct_data(Opaque d) const override { return I::destruct_data(_dcast(d)); }
///@}
private:
using I = Impl;

View file

@ -200,6 +200,9 @@ namespace xo {
**/
bool expand(size_type z) noexcept;
/** create initial guard **/
void establish_initial_guard() noexcept;
/** discard all allocated memory, return to empty state
* Promise:
* - committed memory unchanged

View file

@ -18,7 +18,7 @@ namespace xo {
*
* Map showing an arena allocation:
*
* @pre
* @verbatim
*
* <-------------z1--------------->
* < guard >< hz >< req_z >< dz >< guard >
@ -37,9 +37,11 @@ namespace xo {
* dz [p] padding (to uintptr_t alignment. req_z+dz recorded in header)
* free_ DArena::free_ just after guard bytes for last allocation
*
* @endpre
* @endverbatim
**/
struct DArenaIterator {
/** @defgroup mm-arenaiterator-ctors DArenaIterator instance vars **/
///@{
DArenaIterator() = default;
DArenaIterator(const DArena * arena,
AllocHeader * pos) : arena_{arena},
@ -58,7 +60,10 @@ namespace xo {
* an iterator error in @p *arena
**/
static DArenaIterator end(const DArena * arena);
///@}
/** @defgroup mm-arenaiterator-methods DArenaIterator methods **/
///@{
/** Address of allocation header for beginning of alloc range in @p arena **/
static AllocHeader * begin_header(const DArena * arena);
/** Address of allocation header for end of alloc range.
@ -71,6 +76,9 @@ namespace xo {
* It can be dereferenced if is also non-empty
**/
bool is_valid() const noexcept { return (arena_ != nullptr) && (pos_ != nullptr); }
/** An invalid (or sentinel) iterator is incomparable with all
* iterators including itself
**/
bool is_invalid() const noexcept { return !is_valid(); }
/** fetch contents at current iterator position **/
@ -82,26 +90,35 @@ namespace xo {
/** advance iterator to next allocation **/
void next() noexcept;
/** cast iterator position to byte* */
std::byte * pos_as_byte() const { return (std::byte *)pos_; }
/** *ix synonym for ix.deref() **/
AllocInfo operator*() const noexcept { return this->deref(); }
/** ++ix synonym for ix.next() **/
DArenaIterator & operator++() noexcept { this->next(); return *this; }
///@}
/** @defgroup mm-arenaiterator-instance-vars **/
///@{
/** iterator visits allocations from this arena **/
const DArena * arena_ = nullptr;
/** current iterator position **/
AllocHeader * pos_ = nullptr;
///@}
};
inline bool
operator==(const DArenaIterator & x, const DArenaIterator & y) {
operator==(const DArenaIterator & x,
const DArenaIterator & y)
{
return x.compare(y).is_equal();
}
inline bool
operator!=(const DArenaIterator & x, const DArenaIterator & y) {
operator!=(const DArenaIterator & x,
const DArenaIterator & y)
{
return !x.compare(y).is_equal();
}
} /*namespace mm*/

View file

@ -24,7 +24,11 @@ namespace xo {
return os;
}
/** Result of a generic comparison operation
**/
struct cmpresult {
/** @defgroup mm-cmpresult-ctors cmpresult ctors **/
///@{
cmpresult() : err_{comparison::invalid}, cmp_{0} {}
cmpresult(comparison err, std::int16_t cmp) : err_{err}, cmp_{cmp} {}
@ -32,7 +36,6 @@ namespace xo {
static cmpresult lesser() { return cmpresult(comparison::comparable, -1); }
static cmpresult equal() { return cmpresult(comparison::comparable, 0); }
static cmpresult greater() { return cmpresult(comparison::comparable, +1); }
template<typename T>
static cmpresult from_cmp(T && x, T && y) {
if (x < y)
@ -43,6 +46,11 @@ namespace xo {
return cmpresult::greater();
}
///@}
/** @defgroup mm-cmpresult-methods cmpresult methods **/
///@{
/** print to stream **/
void display(std::ostream & os) const;
@ -52,14 +60,18 @@ namespace xo {
bool is_equal() const {
return (err_ == comparison::comparable) && (cmp_ == 0);
}
///@}
/* -1 -> invalid (sentinel)
* 0 -> comparable
* +1 -> incomparable (e.g. iterators from different arenas)
*/
/** @defgroup mm-cmpresult-instance-vars cmpresult instance vars **/
///@{
/** -1 -> invalid (sentinel)
* 0 -> comparable
* +1 -> incomparable (e.g. iterators from different arenas)
**/
comparison err_ = comparison::invalid;
/* <0 -> lesser; 0 -> equal, >0 -> greater */
/** <0 -> lesser; 0 -> equal, >0 -> greater **/
std::int16_t cmp_ = 0;
///@}
};
inline std::ostream & operator<<(std::ostream & os,

View file

@ -0,0 +1,17 @@
/** @file ArenaConfig.cpp
*
* @author Roland Conybeare, Dec 2025
**/
#include "ArenaConfig.hpp"
namespace xo {
namespace mm {
ArenaConfig
ArenaConfig::simple(std::size_t z)
{
return ArenaConfig { .size_ = z };
}
}
}
/* end ArenaConfig.cpp */

View file

@ -8,6 +8,7 @@ set(SELF_SRCS
cmpresult.cpp
AAllocator.cpp
ArenaConfig.cpp
DArena.cpp
IAllocator_Any.cpp
IAllocator_DArena.cpp

View file

@ -81,8 +81,8 @@ namespace xo {
::munmap(aligned_hi, suffix);
}
#ifdef __linux__
if (enable_hugepage_flag) {
#ifdef __linux__
/** linux:
* opt-in to transparent huge pages (THP)
* provided OS configured to support them.
@ -100,8 +100,8 @@ namespace xo {
* Page table has a handful of levels
**/
::madvise(aligned_base, target_z, MADV_HUGEPAGE); // 8.
}
#endif
}
return std::make_pair(aligned_base, aligned_hi);
}
@ -483,6 +483,18 @@ namespace xo {
return mem;
}
void
DArena::establish_initial_guard() noexcept
{
assert(free_ == lo_);
::memset(this->free_,
config_.header_.guard_byte_,
config_.header_.guard_z_);
this->free_ += config_.header_.guard_z_;
}
bool
DArena::expand(size_t target_z) noexcept
{
@ -551,11 +563,7 @@ namespace xo {
if (commit_start == lo_) [[unlikely]] {
/* first expand() for this allocator - start with guard_z_ bytes */
::memset(free_,
config_.header_.guard_byte_,
config_.header_.guard_z_);
free_ += config_.header_.guard_z_;
this->establish_initial_guard();
}
assert(committed_z_ % arena_align_z_ == 0);
@ -568,6 +576,7 @@ namespace xo {
DArena::clear() noexcept
{
this->free_ = lo_;
this->establish_initial_guard();
}
}
} /*namespace xo*/