xo-alloc2: DX1CollectorIterator infra [WIP]
This commit is contained in:
parent
1383c477f0
commit
4fb50592ac
32 changed files with 1049 additions and 185 deletions
|
|
@ -6,6 +6,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "alloc/AllocInfo.hpp"
|
||||
#include "cmpresult.hpp"
|
||||
#include <xo/facet/obj.hpp>
|
||||
|
||||
namespace xo {
|
||||
|
|
@ -19,7 +20,8 @@ namespace xo {
|
|||
/** @class AAllocIterator
|
||||
* @brief Abstract facet for iterating over allocs
|
||||
*
|
||||
* Iterator refers to an AllocInfo instance.
|
||||
* Iterator refers to an AllocInfo instance
|
||||
* Only supporting forward-allocator.
|
||||
**/
|
||||
struct AAllocIterator {
|
||||
using obj_AAllocIterator = xo::facet::obj<AAllocIterator>;
|
||||
|
|
@ -29,13 +31,11 @@ namespace xo {
|
|||
/** retrieve AllocInfo for current iterator position
|
||||
**/
|
||||
virtual AllocInfo deref(Copaque d) const noexcept = 0;
|
||||
/** compare alloc iterators @p d and @p other for equality **/
|
||||
virtual int compare(Copaque d,
|
||||
const obj_AAllocIterator & other) const noexcept = 0;
|
||||
/** compare alloc iterators @p d and @p other **/
|
||||
virtual cmpresult compare(Copaque d,
|
||||
const obj_AAllocIterator & other) const noexcept = 0;
|
||||
/** advance iterator to next position **/
|
||||
virtual void next(Opaque d) const noexcept = 0;
|
||||
/** retreat iterator to previous position **/
|
||||
virtual void prev(Opaque d) const noexcept = 0;
|
||||
};
|
||||
} /*namespace mm*/
|
||||
} /*namespace xo*/
|
||||
|
|
|
|||
|
|
@ -29,6 +29,14 @@ namespace xo {
|
|||
alloc_info_disabled,
|
||||
/** attempt to call alloc_info for address not owned by allocator **/
|
||||
alloc_info_address,
|
||||
/** for example: alloc iteration not supported in arenas with
|
||||
* AllocConfig.store_header_flag_ = false
|
||||
**/
|
||||
alloc_iterator_not_supported,
|
||||
/** attempt to deref an iterator that does not refer to an alloc **/
|
||||
alloc_iterator_deref,
|
||||
/** attempt to advance an iterator that does not refer to an alloc **/
|
||||
alloc_iterator_next,
|
||||
};
|
||||
|
||||
struct AllocError {
|
||||
|
|
|
|||
|
|
@ -17,20 +17,6 @@ namespace xo {
|
|||
|
||||
explicit AllocHeader(repr_type x) : repr_{x} {}
|
||||
|
||||
#ifdef OBSOLETE
|
||||
std::uint32_t tseq(const AllocHeaderConfig & cfg) const noexcept {
|
||||
return cfg.tseq(repr_);
|
||||
}
|
||||
|
||||
std::uint32_t age(const AllocHeaderConfig & cfg) const noexcept {
|
||||
return cfg.age(repr_);
|
||||
}
|
||||
|
||||
size_type size(const AllocHeaderConfig & cfg) const noexcept {
|
||||
return cfg.size(repr_);
|
||||
}
|
||||
#endif
|
||||
|
||||
repr_type repr_;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,11 @@ namespace xo {
|
|||
p_guard_hi_{p_guard_hi} {}
|
||||
|
||||
/** error when alloc-header not configured **/
|
||||
static AllocInfo error_not_configured(AllocHeaderConfig * p_cfg) {
|
||||
static AllocInfo error_not_configured(const AllocHeaderConfig * p_cfg) {
|
||||
return AllocInfo(p_cfg, nullptr, nullptr, nullptr);
|
||||
}
|
||||
/** error on deref empty iterator **/
|
||||
static AllocInfo error_invalid_iterator(const AllocHeaderConfig * p_cfg) {
|
||||
return AllocInfo(p_cfg, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +50,8 @@ namespace xo {
|
|||
std::uint32_t age() const noexcept { return p_config_->age (*p_header_); }
|
||||
/** Allocation size (including allocator-supplied padding) **/
|
||||
size_type size() const noexcept { return p_config_->size(*p_header_); }
|
||||
/** Payload for this allocation. This is the memory available to application **/
|
||||
span_type payload() const noexcept;
|
||||
/** Guard bytes immediately following allocation **/
|
||||
span_type guard_hi() const noexcept;
|
||||
/** Number of guard bytes **/
|
||||
|
|
|
|||
|
|
@ -27,12 +27,11 @@ namespace xo {
|
|||
|
||||
// const methods
|
||||
[[noreturn]] AllocInfo deref(Copaque) const noexcept override { _fatal(); }
|
||||
[[noreturn]] int compare(Copaque,
|
||||
const obj_AAllocIterator &) const noexcept override { _fatal(); }
|
||||
[[noreturn]] cmpresult compare(Copaque,
|
||||
const obj_AAllocIterator &) const noexcept override { _fatal(); }
|
||||
|
||||
// non-const methods
|
||||
[[noreturn]] void next(Opaque) const noexcept override { _fatal(); }
|
||||
[[noreturn]] void prev(Opaque) const noexcept override { _fatal(); }
|
||||
|
||||
private:
|
||||
[[noreturn]] static void _fatal();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AAllocIterator.hpp"
|
||||
|
||||
namespace xo {
|
||||
|
|
@ -27,15 +29,14 @@ namespace xo {
|
|||
int32_t _typeseq() const noexcept override { return s_typeseq; }
|
||||
AllocInfo deref(Copaque d)
|
||||
const noexcept override { return I::deref(_dcast(d)); }
|
||||
int compare(Copaque d,
|
||||
const obj_AAllocIterator & other)
|
||||
cmpresult compare(Copaque d,
|
||||
const obj_AAllocIterator & other)
|
||||
const noexcept override
|
||||
{ return I::compare(_dcast(d), other); }
|
||||
|
||||
// non-const methods
|
||||
|
||||
void next(Opaque d) const noexcept override { I::prev(_dcast(d)); }
|
||||
void prev(Opaque d) const noexcept override { I::next(_dcast(d)); }
|
||||
void next(Opaque d) const noexcept override { I::next(_dcast(d)); }
|
||||
|
||||
private:
|
||||
using I = Impl;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
**/
|
||||
|
||||
#include "AAllocIterator.hpp"
|
||||
#include <xo/facet/RRouter.hpp"
|
||||
#include <xo/facet/RRouter.hpp>
|
||||
//#include <string>
|
||||
|
||||
namespace xo {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "alloc/AllocHeader.hpp"
|
||||
#include "alloc/AllocHeaderConfig.hpp"
|
||||
#include "alloc/AllocError.hpp"
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
|
@ -34,11 +34,6 @@ namespace xo {
|
|||
* present in arena
|
||||
**/
|
||||
bool store_header_flag_ = false;
|
||||
#ifdef OBSOLETE
|
||||
/** number of bits to represent allocation size **/
|
||||
std::uint64_t header_size_bits_ = 32;
|
||||
std::uint64_t header_size_mask_ = (1ul << header_size_bits_) - 1;
|
||||
#endif
|
||||
/** configuration for per-alloc header **/
|
||||
AllocHeaderConfig header_;
|
||||
/** true to enable debug logging **/
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "ArenaConfig.hpp"
|
||||
#include "alloc/AllocInfo.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace mm {
|
||||
struct DArenaIterator; // see DArenaIterator.hpp
|
||||
|
||||
/** @class DArena
|
||||
*
|
||||
|
|
@ -85,6 +87,11 @@ namespace xo {
|
|||
/** true if arena is mapped i.e. has a reserved address range **/
|
||||
bool is_mapped() const noexcept { return (lo_ != nullptr) && (hi_ != nullptr); }
|
||||
|
||||
/** @ret iterator pointing to the first allocation in this arena **/
|
||||
DArenaIterator begin() const noexcept;
|
||||
/** @ret iterator pointing to just after the last allocation in this arena **/
|
||||
DArenaIterator end() const noexcept;
|
||||
|
||||
/** get header from allocated object address **/
|
||||
header_type * obj2hdr(void * obj) noexcept;
|
||||
|
||||
|
|
@ -98,7 +105,11 @@ namespace xo {
|
|||
*
|
||||
* Note: non-const, may stash error details
|
||||
**/
|
||||
AllocInfo alloc_info(value_type mem) noexcept;
|
||||
AllocInfo alloc_info(value_type mem) const noexcept;
|
||||
|
||||
/** capture error information: advance error count + set last_error **/
|
||||
void capture_error(error err,
|
||||
size_type target_z = 0) const;
|
||||
|
||||
/** discard all allocated memory, return to empty state
|
||||
* Promise:
|
||||
|
|
|
|||
102
xo-alloc2/include/xo/alloc2/arena/DArenaIterator.hpp
Normal file
102
xo-alloc2/include/xo/alloc2/arena/DArenaIterator.hpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/** @file DArenaIterator.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "alloc/AllocInfo.hpp"
|
||||
#include "alloc/AllocHeader.hpp"
|
||||
#include "cmpresult.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace mm {
|
||||
struct DArena;
|
||||
|
||||
/** @class DArenaIterator
|
||||
* @brief Representation for alloc iterator over arena
|
||||
*
|
||||
* Map showing an arena allocation:
|
||||
*
|
||||
* @pre
|
||||
*
|
||||
* <-------------z1--------------->
|
||||
* < guard >< hz >< req_z >< dz >< guard >
|
||||
*
|
||||
* +++++++++0000zzzz@@@@@@@@@@@@@@@@@ppppppp+++++++++
|
||||
*
|
||||
* ^ ^ ^
|
||||
* header mem header
|
||||
* ^ (next alloc)
|
||||
* DArenaIterator::pos_
|
||||
*
|
||||
* guard [+] guard before+after each allocation, for simple sanitize checks
|
||||
* header [0] alloc header (non-size bits)
|
||||
* [z] alloc header (size bits)
|
||||
* mem [@] app-requested memory, including padding [p]
|
||||
* dz [p] padding (to uintptr_t alignment. req_z+dz recorded in header)
|
||||
* free_ DArena::free_ just after guard bytes for last allocation
|
||||
*
|
||||
* @endpre
|
||||
**/
|
||||
struct DArenaIterator {
|
||||
DArenaIterator() = default;
|
||||
DArenaIterator(const DArena * arena,
|
||||
AllocHeader * pos) : arena_{arena},
|
||||
pos_{pos} {}
|
||||
|
||||
/** Create iterator in invalid state **/
|
||||
static DArenaIterator invalid() { return DArenaIterator(); }
|
||||
|
||||
/** Create iterator pointing to the beginning of @p arena
|
||||
* Iterator cannot modify memory, but can capture
|
||||
* an iterator error in @p *arena
|
||||
**/
|
||||
static DArenaIterator begin(const DArena * arena);
|
||||
/** Create iterator pointing to the end of @p arena
|
||||
* Iterator cannot modify memory, but can capture
|
||||
* an iterator error in @p *arena
|
||||
**/
|
||||
static DArenaIterator end(const DArena * arena);
|
||||
|
||||
/** A valid iterator can be compared, at least with itself
|
||||
* It can be dereferenced if is also non-empty
|
||||
**/
|
||||
bool is_valid() const noexcept { return (arena_ != nullptr) && (pos_ != nullptr); }
|
||||
bool is_invalid() const noexcept { return !is_valid(); }
|
||||
|
||||
/** fetch contents at current iterator position **/
|
||||
AllocInfo deref() const noexcept;
|
||||
/** compare two iterators. To be comparable,
|
||||
* iterators must refer to the same arena
|
||||
**/
|
||||
cmpresult compare(const DArenaIterator & other) const noexcept;
|
||||
/** advance iterator to next allocation **/
|
||||
void next() noexcept;
|
||||
|
||||
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; }
|
||||
|
||||
/** 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) {
|
||||
return x.compare(y).is_equal();
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator!=(const DArenaIterator & x, const DArenaIterator & y) {
|
||||
return !x.compare(y).is_equal();
|
||||
}
|
||||
} /*namespace mm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end DArenaIterator.hpp */
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/** @file IAllocIterator_DArenaIterator.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "alloc/IAllocIterator_Xfer.hpp"
|
||||
#include "arena/DArenaIterator.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace mm { struct IAllocIterator_DArenaIterator; }
|
||||
|
||||
namespace facet {
|
||||
template <>
|
||||
struct FacetImplementation<xo::mm::AAllocIterator,
|
||||
xo::mm::DArenaIterator> {
|
||||
using ImplType = xo::mm::IAllocIterator_Xfer<xo::mm::DArenaIterator,
|
||||
xo::mm::IAllocIterator_DArenaIterator>;
|
||||
};
|
||||
}
|
||||
|
||||
namespace mm {
|
||||
/** @class IAllocIterator_DArena
|
||||
* @brief alloc iteration for the DArena allocator
|
||||
**/
|
||||
struct IAllocIterator_DArenaIterator {
|
||||
static AllocInfo deref(const DArenaIterator &) noexcept;
|
||||
static cmpresult compare(const DArenaIterator &,
|
||||
const obj<AAllocIterator> & other) noexcept;
|
||||
static void next(DArenaIterator &) noexcept;
|
||||
};
|
||||
} /*namespace mm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end IAllocIterator_DArenaIterator.cpp */
|
||||
59
xo-alloc2/include/xo/alloc2/cmpresult.hpp
Normal file
59
xo-alloc2/include/xo/alloc2/cmpresult.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/** @file cmpresult.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace mm {
|
||||
enum class comparison : int32_t {
|
||||
invalid = -1,
|
||||
comparable = 0,
|
||||
incomparable = 1,
|
||||
};
|
||||
|
||||
struct cmpresult {
|
||||
cmpresult() : err_{comparison::invalid}, cmp_{0} {}
|
||||
cmpresult(comparison err, std::int16_t cmp) : err_{err}, cmp_{cmp} {}
|
||||
|
||||
static cmpresult incomparable() { return cmpresult(comparison::incomparable, 0); }
|
||||
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)
|
||||
return cmpresult::lesser();
|
||||
else if (x == y)
|
||||
return cmpresult::equal();
|
||||
else
|
||||
return cmpresult::greater();
|
||||
}
|
||||
|
||||
|
||||
bool is_equal() const { return (err_ == comparison::comparable) && (cmp_ == 0); }
|
||||
|
||||
/* -1 -> invalid (sentinel)
|
||||
* 0 -> comparable
|
||||
* 1 -> incomparable (e.g. iterators from different arenas)
|
||||
*/
|
||||
comparison err_ = comparison::invalid;
|
||||
/* <0 -> lesser; 0 -> equal, >0 -> greater */
|
||||
std::int16_t cmp_ = 0;
|
||||
};
|
||||
|
||||
inline std::ostream & operator<<(std::ostream & os,
|
||||
const cmpresult & x)
|
||||
{
|
||||
os << "<cmpresult " << xtag("err", x.err_) << xtag("cmp", x.cmp_) << ">";
|
||||
}
|
||||
|
||||
} /*namespace mm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end cmpresult.hpp */
|
||||
|
|
@ -145,6 +145,8 @@ namespace xo {
|
|||
generation gc_upto_;
|
||||
};
|
||||
|
||||
struct DX1CollectorIterator;
|
||||
|
||||
// ----- DX1Collector -----
|
||||
|
||||
struct DX1Collector {
|
||||
|
|
@ -212,6 +214,15 @@ namespace xo {
|
|||
/** Retreive bookkeeping info for allocation at @p mem. **/
|
||||
AllocInfo alloc_info(value_type mem) noexcept;
|
||||
|
||||
// ----- iteration -----
|
||||
|
||||
/** alloc iterator at begin position **/
|
||||
DX1CollectorIterator begin() const noexcept;
|
||||
/** alloc iterator at end position
|
||||
* (valid, but cannot be dereferenced)
|
||||
**/
|
||||
DX1CollectorIterator end() const noexcept;
|
||||
|
||||
// ----- book-keeping -----
|
||||
|
||||
/** reverse to-space and from-space roles for generation g **/
|
||||
|
|
|
|||
90
xo-alloc2/include/xo/alloc2/gc/DX1CollectorIterator.hpp
Normal file
90
xo-alloc2/include/xo/alloc2/gc/DX1CollectorIterator.hpp
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/** @file DX1CollectorIterator.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "alloc/AllocInfo.hpp"
|
||||
#include "gc/generation.hpp"
|
||||
#include "arena/DArenaIterator.hpp"
|
||||
#include "cmpresult.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace mm {
|
||||
struct DX1Collector;
|
||||
|
||||
/** @class DX1CollectorIterator
|
||||
* @brief Representation for alloc iterator over X1 collector
|
||||
*
|
||||
* Will iterate across all allocs in all generations
|
||||
**/
|
||||
struct DX1CollectorIterator {
|
||||
DX1CollectorIterator() = default;
|
||||
DX1CollectorIterator(const DX1Collector * gc,
|
||||
generation gen_ix,
|
||||
generation gen_hi,
|
||||
DArenaIterator arena_ix,
|
||||
DArenaIterator arena_hi);
|
||||
|
||||
/** Invalid iterator. Does not compare equal to anything, including itself **/
|
||||
static DX1CollectorIterator invalid() { return DX1CollectorIterator(); }
|
||||
/** Create iterator pointing to the beginning of @p gc.
|
||||
* Iterator cannot modify payload memory
|
||||
**/
|
||||
static DX1CollectorIterator begin(DX1Collector * gc);
|
||||
/** Create iterator pointing to the end of @p gc.
|
||||
* Iterator cannot modify payload memory.
|
||||
**/
|
||||
static DX1CollectorIterator end(DX1Collector * gc);
|
||||
|
||||
/** true if iterator is invalid. invalid iterators are not comparable **/
|
||||
bool is_valid() const noexcept { return (gc_ != nullptr); }
|
||||
bool is_invalid() const noexcept { return !is_valid(); }
|
||||
|
||||
/** fetch contents at current iterator position **/
|
||||
AllocInfo deref() const noexcept;
|
||||
/** compare two iterators. To be comparable,
|
||||
* iterators must refer to the same collector
|
||||
**/
|
||||
cmpresult compare(const DX1CollectorIterator & other) const noexcept;
|
||||
/** advance iterator to next allocation **/
|
||||
void next() noexcept;
|
||||
|
||||
/** for *ix synonym for ix.deref() **/
|
||||
AllocInfo operator*() const noexcept { return this->deref(); }
|
||||
|
||||
private:
|
||||
/** if non-empty, normalize to state with arena_ix_ != arena_hi_ **/
|
||||
void normalize() noexcept;
|
||||
|
||||
private:
|
||||
/** Iterator visits allocations from this collector **/
|
||||
const DX1Collector * gc_ = nullptr;
|
||||
/** Iterating over generations in [@p gen_ix_, @p gen_hi_).
|
||||
* Current position is within arena for @p gen_ix_ to-space,
|
||||
* Provided @p gen_ix_ < @p gen_hi_
|
||||
**/
|
||||
generation gen_ix_;
|
||||
generation gen_hi_;
|
||||
/** Iterating over allocs in [@p arena_ix_, @p arena_hi_).
|
||||
* Current position is at @p arena_ix_
|
||||
**/
|
||||
DArenaIterator arena_ix_;
|
||||
DArenaIterator arena_hi_;
|
||||
};
|
||||
|
||||
inline bool
|
||||
operator==(const DX1CollectorIterator & x, const DX1CollectorIterator & y) {
|
||||
return x.compare(y).is_equal();
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator!=(const DX1CollectorIterator & x, const DX1CollectorIterator & y) {
|
||||
return !x.compare(y).is_equal();
|
||||
}
|
||||
|
||||
} /*namespace mm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end DX1CollectorIterator.hpp */
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/** @file IAllocIterator_DX1Collector.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "alloc/IAllocIterator_Xfer.hpp"
|
||||
#include "gc/DX1CollectorIterator.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace mm { struct IAllocIterator_DX1CollectorIterator; }
|
||||
|
||||
namespace facet {
|
||||
template <>
|
||||
struct FacetImplementation<xo::mm::AAllocIterator,
|
||||
xo::mm::DX1CollectorIterator> {
|
||||
using ImplType = xo::mm::IAllocIterator_Xfer<xo::mm::DX1CollectorIterator,
|
||||
xo::mm::IAllocIterator_DX1CollectorIterator>;
|
||||
};
|
||||
}
|
||||
|
||||
namespace mm {
|
||||
/** @class IAllocIterator_DX1Collector
|
||||
* @brief alloc iteration for the DX1Collector allocator
|
||||
**/
|
||||
struct IAllocIterator_DX1CollectorIterator {
|
||||
static AllocInfo deref(const DX1CollectorIterator &) noexcept;
|
||||
static cmpresult compare(const DX1CollectorIterator &,
|
||||
const obj<AAllocIterator> & other) noexcept;
|
||||
static void next(DX1CollectorIterator &) noexcept;
|
||||
};
|
||||
} /*namespace mm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end IAllocIterator_DX1Collector.hpp */
|
||||
|
|
@ -19,6 +19,7 @@ namespace xo {
|
|||
struct generation {
|
||||
using value_type = std::uint32_t;
|
||||
|
||||
constexpr generation() = default;
|
||||
explicit constexpr generation(value_type x) : value_{x} {}
|
||||
|
||||
static generation nursery() { return generation{0}; }
|
||||
|
|
@ -27,7 +28,7 @@ namespace xo {
|
|||
|
||||
generation & operator++() { ++value_; return *this; }
|
||||
|
||||
std::uint32_t value_;
|
||||
std::uint32_t value_ = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
namespace xo {
|
||||
namespace mm {
|
||||
|
||||
auto
|
||||
AllocInfo::guard_lo() const noexcept -> span_type
|
||||
{
|
||||
|
|
@ -18,6 +17,18 @@ namespace xo {
|
|||
p_guard_lo_ + p_config_->guard_z_);
|
||||
}
|
||||
|
||||
auto
|
||||
AllocInfo::payload() const noexcept -> span_type
|
||||
{
|
||||
if (!p_header_)
|
||||
return span_type(nullptr, nullptr);
|
||||
|
||||
byte * lo = (byte *)(p_header_ + 1);
|
||||
size_type z = this->size();
|
||||
|
||||
return span_type(lo, lo+z);
|
||||
}
|
||||
|
||||
auto
|
||||
AllocInfo::guard_hi() const noexcept -> span_type
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,13 +11,17 @@ set(SELF_SRCS
|
|||
IAllocator_DArena.cpp
|
||||
|
||||
IAllocIterator_Any.cpp
|
||||
DArenaIterator.cpp
|
||||
IAllocIterator_DArenaIterator.cpp
|
||||
|
||||
ICollector_Any.cpp
|
||||
IGCObject_Any.cpp
|
||||
IAllocator_DX1Collector.cpp
|
||||
ICollector_DX1Collector.cpp
|
||||
IAllocIterator_DX1CollectorIterator.cpp
|
||||
|
||||
DX1Collector.cpp
|
||||
DX1CollectorIterator.cpp
|
||||
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "alloc/AAllocator.hpp"
|
||||
#include "arena/DArena.hpp"
|
||||
#include "arena/DArenaIterator.hpp"
|
||||
#include "xo/alloc2/padding.hpp"
|
||||
#include "xo/indentlog/print/tag.hpp"
|
||||
#include <cassert>
|
||||
|
|
@ -249,15 +250,10 @@ namespace xo {
|
|||
}
|
||||
|
||||
AllocInfo
|
||||
DArena::alloc_info(value_type mem) noexcept
|
||||
DArena::alloc_info(value_type mem) const noexcept
|
||||
{
|
||||
if (!config_.store_header_flag_) [[unlikely]] {
|
||||
++(error_count_);
|
||||
last_error_ = AllocError(error::alloc_info_disabled,
|
||||
error_count_,
|
||||
0 /*add_commit_z*/,
|
||||
committed_z_,
|
||||
this->reserved());
|
||||
this->capture_error(error::alloc_info_disabled);
|
||||
|
||||
return AllocInfo::error_not_configured(&config_.header_);
|
||||
}
|
||||
|
|
@ -265,12 +261,7 @@ namespace xo {
|
|||
byte * header_mem = mem - sizeof(AllocHeader);
|
||||
|
||||
if (!this->contains(header_mem)) {
|
||||
++(error_count_);
|
||||
last_error_ = AllocError(error::alloc_info_address,
|
||||
error_count_,
|
||||
0 /*add_commit_z*/,
|
||||
committed_z_,
|
||||
this->reserved());
|
||||
this->capture_error(error::alloc_info_address);
|
||||
}
|
||||
|
||||
AllocHeader * header = (AllocHeader *)header_mem;
|
||||
|
|
@ -286,6 +277,32 @@ namespace xo {
|
|||
guard_hi);
|
||||
}
|
||||
|
||||
DArenaIterator
|
||||
DArena::begin() const noexcept
|
||||
{
|
||||
return DArenaIterator::begin(this);
|
||||
}
|
||||
|
||||
DArenaIterator
|
||||
DArena::end() const noexcept
|
||||
{
|
||||
return DArenaIterator::end(this);
|
||||
}
|
||||
|
||||
void
|
||||
DArena::capture_error(error err,
|
||||
size_type target_z) const
|
||||
{
|
||||
DArena * self = const_cast<DArena *>(this);
|
||||
|
||||
++(self->error_count_);
|
||||
self->last_error_ = AllocError(err,
|
||||
error_count_,
|
||||
target_z,
|
||||
committed_z_,
|
||||
reserved());
|
||||
}
|
||||
|
||||
void
|
||||
DArena::clear() noexcept
|
||||
{
|
||||
|
|
|
|||
134
xo-alloc2/src/alloc2/DArenaIterator.cpp
Normal file
134
xo-alloc2/src/alloc2/DArenaIterator.cpp
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/** @file DArenaIterator.cpp
|
||||
*
|
||||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#include "arena/DArenaIterator.hpp"
|
||||
#include "arena/DArena.hpp"
|
||||
#include <xo/indentlog/scope.hpp>
|
||||
#include <xo/indentlog/print/tag.hpp>
|
||||
#include <cassert>
|
||||
|
||||
namespace xo {
|
||||
using std::byte;
|
||||
|
||||
namespace mm {
|
||||
DArenaIterator
|
||||
DArenaIterator::begin(const DArena * arena)
|
||||
{
|
||||
constexpr bool c_debug_flag = false;
|
||||
scope log(XO_DEBUG(c_debug_flag));
|
||||
|
||||
assert(arena);
|
||||
|
||||
if (arena->config_.store_header_flag_ == false) {
|
||||
arena->capture_error(error::alloc_iterator_not_supported);
|
||||
|
||||
return DArenaIterator::invalid();
|
||||
}
|
||||
|
||||
byte * begin_byte = arena->lo_;
|
||||
AllocHeader * begin_hdr = (AllocHeader *)begin_byte;
|
||||
|
||||
log && log(xtag("begin_hdr", begin_hdr));
|
||||
|
||||
return DArenaIterator(arena, begin_hdr);
|
||||
}
|
||||
|
||||
DArenaIterator
|
||||
DArenaIterator::end(const DArena * arena)
|
||||
{
|
||||
constexpr bool c_debug_flag = false;
|
||||
scope log(XO_DEBUG(c_debug_flag));
|
||||
|
||||
assert(arena);
|
||||
|
||||
if (arena->config_.store_header_flag_ == false) {
|
||||
arena->capture_error(error::alloc_iterator_not_supported);
|
||||
|
||||
return DArenaIterator::invalid();
|
||||
}
|
||||
|
||||
byte * end_byte = arena->free_;
|
||||
AllocHeader * end_hdr = (AllocHeader *)end_byte;
|
||||
|
||||
log && log(xtag("end_hdr", end_hdr));
|
||||
|
||||
return DArenaIterator(arena, end_hdr);
|
||||
}
|
||||
|
||||
AllocInfo
|
||||
DArenaIterator::deref() const noexcept
|
||||
{
|
||||
constexpr bool c_debug_flag = false;
|
||||
scope log(XO_DEBUG(c_debug_flag));
|
||||
|
||||
bool contains_flag = arena_->contains(this->pos_as_byte());
|
||||
bool bounds_flag = (this->pos_as_byte() < arena_->free_);
|
||||
|
||||
log && log(xtag("contains_flag", contains_flag),
|
||||
xtag("bounds_flag", bounds_flag));
|
||||
|
||||
if (!contains_flag || !bounds_flag) {
|
||||
arena_->capture_error(error::alloc_iterator_deref);
|
||||
|
||||
return AllocInfo::error_invalid_iterator(&(arena_->config_.header_));
|
||||
}
|
||||
|
||||
/* iterator points to beginning of header.
|
||||
* memory given to application start immediately followed header
|
||||
*/
|
||||
|
||||
byte * mem = (byte *)(pos_ + 1);
|
||||
|
||||
return arena_->alloc_info(mem);
|
||||
}
|
||||
|
||||
cmpresult
|
||||
DArenaIterator::compare(const DArenaIterator & other_ix) const noexcept
|
||||
{
|
||||
if (is_invalid() || (arena_ != other_ix.arena_))
|
||||
return cmpresult::incomparable();
|
||||
|
||||
if (pos_ < other_ix.pos_) {
|
||||
return cmpresult::lesser();
|
||||
} else if(pos_ == other_ix.pos_) {
|
||||
return cmpresult::equal();
|
||||
} else {
|
||||
return cmpresult::greater();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DArenaIterator::next() noexcept
|
||||
{
|
||||
constexpr bool c_debug_flag = false;
|
||||
scope log(XO_DEBUG(c_debug_flag));
|
||||
|
||||
bool contains_flag = arena_->contains(this->pos_as_byte());
|
||||
bool bounds_flag = (this->pos_as_byte() < arena_->free_);
|
||||
|
||||
log && log(xtag("contains_flag", contains_flag),
|
||||
xtag("bounds_flag", bounds_flag));
|
||||
|
||||
if (!contains_flag || !bounds_flag) {
|
||||
arena_->capture_error(error::alloc_iterator_next);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t mem_z = arena_->config_.header_.size(*pos_);
|
||||
size_t guard_z = arena_->config_.header_.guard_z_;
|
||||
|
||||
byte * next_as_byte = ((byte *)pos_ + sizeof(AllocHeader) + mem_z + guard_z);
|
||||
/* next == ix.arena_free_ --> iterator is at end of allocator */
|
||||
assert(next_as_byte <= arena_->free_);
|
||||
|
||||
AllocHeader * next = (AllocHeader *)next_as_byte;
|
||||
|
||||
this->pos_ = next;
|
||||
}
|
||||
|
||||
} /*namespace mm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end DArenaIterator.cpp */
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
#include "Allocator.hpp"
|
||||
#include "arena/IAllocator_DArena.hpp"
|
||||
#include "gc/DX1Collector.hpp"
|
||||
#include "gc/DX1CollectorIterator.hpp"
|
||||
#include "gc/generation.hpp"
|
||||
#include "gc/object_age.hpp"
|
||||
#include <xo/facet/obj.hpp>
|
||||
|
|
@ -24,23 +25,6 @@ namespace xo {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef OBSOLETE
|
||||
constexpr std::uint64_t
|
||||
CollectorConfig::gen_shift() const {
|
||||
return arena_config_.header_size_bits_;
|
||||
}
|
||||
|
||||
constexpr std::uint64_t
|
||||
CollectorConfig::gen_mask_unshifted() const {
|
||||
return (1ul << gen_bits_) - 1;
|
||||
}
|
||||
|
||||
constexpr std::uint64_t
|
||||
CollectorConfig::gen_mask_shifted() const {
|
||||
return gen_mask_unshifted() << arena_config_.header_size_bits_;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NOT_USING
|
||||
constexpr std::uint64_t
|
||||
CollectorConfig::tseq_mult() const {
|
||||
|
|
@ -48,23 +32,6 @@ namespace xo {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef OBSOLETE
|
||||
constexpr std::uint64_t
|
||||
CollectorConfig::tseq_shift() const {
|
||||
return gen_bits_ + arena_config_.header_size_bits_;
|
||||
}
|
||||
|
||||
constexpr std::uint64_t
|
||||
CollectorConfig::tseq_mask_unshifted() const {
|
||||
return (1ul << tseq_bits_) - 1;
|
||||
}
|
||||
|
||||
constexpr std::uint64_t
|
||||
CollectorConfig::tseq_mask_shifted() const {
|
||||
return tseq_mask_unshifted() << (gen_bits_ + arena_config_.header_size_bits_);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----- GCRunState -----
|
||||
|
||||
GCRunState::GCRunState(generation gc_upto)
|
||||
|
|
@ -257,6 +224,27 @@ namespace xo {
|
|||
return this->new_space()->alloc_info(mem);
|
||||
}
|
||||
|
||||
DX1CollectorIterator
|
||||
DX1Collector::begin() const noexcept
|
||||
{
|
||||
return DX1CollectorIterator(this,
|
||||
generation{0},
|
||||
generation{config_.n_generation_},
|
||||
DArenaIterator(),
|
||||
DArenaIterator());
|
||||
}
|
||||
|
||||
DX1CollectorIterator
|
||||
DX1Collector::end() const noexcept {
|
||||
generation gen_hi = generation{config_.n_generation_};
|
||||
|
||||
return DX1CollectorIterator(this,
|
||||
gen_hi,
|
||||
gen_hi,
|
||||
DArenaIterator(),
|
||||
DArenaIterator());
|
||||
}
|
||||
|
||||
void
|
||||
DX1Collector::reverse_roles(generation g) noexcept {
|
||||
assert(g < config_.n_generation_);
|
||||
|
|
|
|||
88
xo-alloc2/src/alloc2/DX1CollectorIterator.cpp
Normal file
88
xo-alloc2/src/alloc2/DX1CollectorIterator.cpp
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/** @file DX1CollectorIterator.cpp
|
||||
*
|
||||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#include "gc/DX1CollectorIterator.hpp"
|
||||
#include "gc/DX1Collector.hpp"
|
||||
#include <xo/indentlog/scope.hpp>
|
||||
#include <xo/indentlog/print/tag.hpp>
|
||||
|
||||
namespace xo {
|
||||
namespace mm {
|
||||
DX1CollectorIterator::DX1CollectorIterator(const DX1Collector * gc,
|
||||
generation gen_ix,
|
||||
generation gen_hi,
|
||||
DArenaIterator arena_ix,
|
||||
DArenaIterator arena_hi) : gc_{gc},
|
||||
gen_ix_{gen_ix},
|
||||
gen_hi_{gen_hi},
|
||||
arena_ix_{arena_ix},
|
||||
arena_hi_{arena_hi}
|
||||
{
|
||||
this->normalize();
|
||||
}
|
||||
|
||||
void
|
||||
DX1CollectorIterator::normalize() noexcept
|
||||
{
|
||||
/* normalize: find lowest generation with non-empty to-space */
|
||||
|
||||
for (; gen_ix_ < gen_hi_; ++gen_ix_) {
|
||||
const DArena * arena
|
||||
= gc_->get_space(role::to_space(), gen_ix_);
|
||||
|
||||
arena_ix_ = arena->begin();
|
||||
arena_hi_ = arena->end();
|
||||
|
||||
if (arena_ix_ != arena_hi_) {
|
||||
// normalization achieved!
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AllocInfo
|
||||
DX1CollectorIterator::deref() const noexcept
|
||||
{
|
||||
return arena_ix_.deref();
|
||||
}
|
||||
|
||||
cmpresult
|
||||
DX1CollectorIterator::compare(const DX1CollectorIterator & other_ix) const noexcept
|
||||
{
|
||||
scope log(XO_DEBUG(true),
|
||||
xtag("is_valid", is_valid()),
|
||||
xtag("other_ix.is_valid", other_ix.is_valid()) );
|
||||
|
||||
if (is_invalid() || (gc_ != other_ix.gc_)) {
|
||||
log && log("incomparable!");
|
||||
return cmpresult::incomparable();
|
||||
}
|
||||
|
||||
if (gen_ix_ != other_ix.gen_ix_) {
|
||||
log && log(xtag("gen", gen_ix_), xtag("other.gen", other_ix.gen_ix_));
|
||||
|
||||
/* same collector, different arenas -> compare based on gen# */
|
||||
|
||||
return cmpresult::from_cmp(gen_ix_, other_ix.gen_ix_);
|
||||
}
|
||||
|
||||
/* both iterators refer to the same arena,
|
||||
* so can compare their arena iterators directly
|
||||
*/
|
||||
return arena_ix_.compare(other_ix.arena_ix_);
|
||||
}
|
||||
|
||||
void
|
||||
DX1CollectorIterator::next() noexcept
|
||||
{
|
||||
if (arena_ix_ != arena_hi_) {
|
||||
++arena_ix_;
|
||||
this->normalize();
|
||||
}
|
||||
}
|
||||
} /*namespace mm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end DX1CollectorIterator.cpp */
|
||||
43
xo-alloc2/src/alloc2/IAllocIterator_DArenaIterator.cpp
Normal file
43
xo-alloc2/src/alloc2/IAllocIterator_DArenaIterator.cpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/** @file IAllocIterator_DArenaIterator.cpp
|
||||
*
|
||||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#include "arena/IAllocIterator_DArenaIterator.hpp"
|
||||
#include "AllocIterator.hpp"
|
||||
#include <cassert>
|
||||
|
||||
namespace xo {
|
||||
using std::byte;
|
||||
|
||||
namespace mm {
|
||||
AllocInfo
|
||||
IAllocIterator_DArenaIterator::deref(const DArenaIterator & ix) noexcept
|
||||
{
|
||||
return ix.deref();
|
||||
}
|
||||
|
||||
cmpresult
|
||||
IAllocIterator_DArenaIterator::compare(const DArenaIterator & ix,
|
||||
const obj<AAllocIterator> & other_arg) noexcept
|
||||
{
|
||||
/* downcast from variant */
|
||||
auto other = obj<AAllocIterator, DArenaIterator>::from(other_arg);
|
||||
|
||||
if (!other)
|
||||
return cmpresult::incomparable();
|
||||
|
||||
DArenaIterator & other_ix = *other;
|
||||
|
||||
return ix.compare(other_ix);
|
||||
}
|
||||
|
||||
void
|
||||
IAllocIterator_DArenaIterator::next(DArenaIterator & ix) noexcept
|
||||
{
|
||||
ix.next();
|
||||
}
|
||||
} /*namespace mm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end IAllocIterator_DArenaIterator.cpp */
|
||||
41
xo-alloc2/src/alloc2/IAllocIterator_DX1CollectorIterator.cpp
Normal file
41
xo-alloc2/src/alloc2/IAllocIterator_DX1CollectorIterator.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/** @file IAllocIterator_DX1CollectorIterator.cpp
|
||||
*
|
||||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#include "gc/IAllocIterator_DX1CollectorIterator.hpp"
|
||||
#include "AllocIterator.hpp"
|
||||
//#include <cassert>
|
||||
|
||||
namespace xo {
|
||||
namespace mm {
|
||||
AllocInfo
|
||||
IAllocIterator_DX1CollectorIterator::deref(const DX1CollectorIterator & ix) noexcept
|
||||
{
|
||||
return ix.deref();
|
||||
}
|
||||
|
||||
cmpresult
|
||||
IAllocIterator_DX1CollectorIterator::compare(const DX1CollectorIterator & ix,
|
||||
const obj<AAllocIterator> & other_arg) noexcept
|
||||
{
|
||||
/* downcast from variant */
|
||||
auto other = obj<AAllocIterator, DX1CollectorIterator>::from(other_arg);
|
||||
|
||||
if (!other)
|
||||
return cmpresult::incomparable();
|
||||
|
||||
DX1CollectorIterator & other_ix = *other;
|
||||
|
||||
return ix.compare(other_ix);
|
||||
}
|
||||
|
||||
void
|
||||
IAllocIterator_DX1CollectorIterator::next(DX1CollectorIterator & ix) noexcept
|
||||
{
|
||||
ix.next();
|
||||
}
|
||||
} /*namespace mm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end IAllocIterator_DX1CollectorIterator.cpp */
|
||||
|
|
@ -80,10 +80,7 @@ namespace xo {
|
|||
}
|
||||
|
||||
if (s.lo_ + target_z > s.hi_) [[unlikely]] {
|
||||
++(s.error_count_);
|
||||
s.last_error_ = AllocError(error::reserve_exhausted,
|
||||
s.error_count_,
|
||||
target_z, s.committed_z_, reserved(s));
|
||||
s.capture_error(error::reserve_exhausted, target_z);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -124,15 +121,7 @@ namespace xo {
|
|||
add_commit_z,
|
||||
PROT_READ | PROT_WRITE) != 0) [[unlikely]]
|
||||
{
|
||||
++(s.error_count_);
|
||||
s.last_error_ = AllocError(error::commit_failed,
|
||||
s.error_count_,
|
||||
add_commit_z, s.committed_z_, reserved(s));
|
||||
#ifdef OBSOLETE
|
||||
throw std::runtime_error(tostr("ArenaAlloc::expand: commit failure",
|
||||
xtag("committed_z", s.committed_z_),
|
||||
xtag("add_commit_z", add_commit_z)));
|
||||
#endif
|
||||
s.capture_error(error::commit_failed, add_commit_z);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -198,75 +187,6 @@ namespace xo {
|
|||
? alloc_mode::sub_complete
|
||||
: alloc_mode::sub_incomplete));
|
||||
|
||||
#ifdef OBSOLETE
|
||||
if ((req_z == 0) && complete_flag) [[unlikely]] {
|
||||
/** use zero req_z with complete_flag to clear s.last_header_ **/
|
||||
|
||||
if (s.config_.store_header_flag_) {
|
||||
if (!s.last_header_) [[unlikely]] {
|
||||
++(s.error_count_);
|
||||
s.last_error_ = AllocError(error::orphan_sub_alloc,
|
||||
s.error_count_,
|
||||
0 /*add_commit_z*/, s.committed_z_, reserved(s));
|
||||
} else {
|
||||
s.last_header_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
byte * free0 = s.free_;
|
||||
byte * mem = _alloc(s, req_z,
|
||||
complete_flag ? alloc_mode::sub_complete : alloc_mode::sub,
|
||||
false /*!store_header_flag*/,
|
||||
false /*!remember_header_flag*/);
|
||||
|
||||
if (!mem) [[unlikely]] {
|
||||
/* error already captured */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
byte * free1 = s.free_;
|
||||
/* used: accounting for padding applied to req_z */
|
||||
size_t z0 = (free1 - free0);
|
||||
|
||||
assert(z0 > 0);
|
||||
|
||||
if (s.config_.store_header_flag_) {
|
||||
if (!s.last_header_) [[unlikely]] {
|
||||
++(s.error_count_);
|
||||
s.last_error_ = AllocError(error::orphan_sub_alloc,
|
||||
s.error_count_,
|
||||
0 /*add_commit_z*/, s.committed_z_, reserved(s));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* s.last_header_ holds aggregate size of preceding super_alloc
|
||||
* (+ any sub-alloc's).
|
||||
*
|
||||
* Accumulate allocation size
|
||||
*/
|
||||
uint64_t header = *s.last_header_;
|
||||
|
||||
if ((header & s.config_.header_size_mask_ & z0) != z0) [[unlikely]] {
|
||||
/* cumulative alloc size doesn't fit in configured header_size_mask bits */
|
||||
++(s.error_count_);
|
||||
s.last_error_ = AllocError(error::header_size_mask,
|
||||
s.error_count_,
|
||||
0 /*add_commit_z*/, s.committed_z_, reserved(s));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*s.last_header_ = ((header & ~s.config_.header_size_mask_) | z0);
|
||||
|
||||
if (complete_flag) {
|
||||
s.last_header_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return mem;
|
||||
#endif
|
||||
}
|
||||
|
||||
byte *
|
||||
|
|
@ -340,12 +260,7 @@ namespace xo {
|
|||
hz = sizeof(header);
|
||||
} else {
|
||||
/* req_z doesn't fit in configured header_size_mask bits */
|
||||
++(s.error_count_);
|
||||
s.last_error_ = AllocError(error::header_size_mask,
|
||||
s.error_count_,
|
||||
0 /*add_commit_z*/,
|
||||
s.committed_z_,
|
||||
reserved(s));
|
||||
s.capture_error(error::header_size_mask);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
|
@ -391,6 +306,8 @@ namespace xo {
|
|||
xtag("z1", z1),
|
||||
xtag("size", size(s)),
|
||||
xtag("avail", available(s)));
|
||||
log && log(xtag("mem", mem),
|
||||
xtag("free", s.free_));
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
|
|
|||
199
xo-alloc2/utest/AllocIterator.test.cpp
Normal file
199
xo-alloc2/utest/AllocIterator.test.cpp
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/** @file AllocIterator.test.cpp
|
||||
*
|
||||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#include "Allocator.hpp"
|
||||
#include "AllocIterator.hpp"
|
||||
#include "arena/IAllocator_DArena.hpp"
|
||||
#include "arena/IAllocIterator_DArenaIterator.hpp"
|
||||
#include "padding.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
namespace xo {
|
||||
using xo::mm::AAllocator;
|
||||
|
||||
using xo::mm::AllocInfo;
|
||||
|
||||
using xo::mm::AAllocIterator;
|
||||
using xo::mm::IAllocIterator_Any;
|
||||
using xo::mm::IAllocIterator_Xfer;
|
||||
using xo::mm::IAllocIterator_DArenaIterator;
|
||||
using xo::mm::DArenaIterator;
|
||||
|
||||
using xo::mm::ArenaConfig;
|
||||
using xo::mm::DArena;
|
||||
|
||||
using xo::mm::padding;
|
||||
using xo::mm::error;
|
||||
|
||||
using std::byte;
|
||||
|
||||
namespace ut {
|
||||
TEST_CASE("IAllocIterator_Xfer_DArenaIterator", "[alloc2]")
|
||||
{
|
||||
/* verify IAllocIterator_Xfer is constructible + satisfies concept checks */
|
||||
IAllocIterator_Xfer<DArenaIterator, IAllocIterator_DArenaIterator> xfer;
|
||||
REQUIRE(IAllocIterator_Xfer<DArenaIterator, IAllocIterator_DArenaIterator>::_valid);
|
||||
}
|
||||
|
||||
TEST_CASE("IAllocIterator_Any", "[alloc2]")
|
||||
{
|
||||
/* verify IAllocIterator_Any is constructible + satisfies concept checks */
|
||||
IAllocIterator_Any any;
|
||||
REQUIRE(IAllocIterator_Any::_valid);
|
||||
}
|
||||
|
||||
TEST_CASE("obj_IAllocIterator", "[alloc2]")
|
||||
{
|
||||
/* verify variant obj constructible */
|
||||
obj<AAllocIterator> obj_any;
|
||||
REQUIRE(obj_any.iface());
|
||||
REQUIRE(obj_any.data() == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("IAllocIterator-disabled-1", "[alloc2]")
|
||||
{
|
||||
/* verify iteration over empty arena */
|
||||
/* typed allocator a1o */
|
||||
ArenaConfig cfg { .name_ = "testarena",
|
||||
.size_ = 64*1024,
|
||||
.debug_flag_ = false };
|
||||
DArena arena = DArena::map(cfg);
|
||||
obj<AAllocator, DArena> a1o{&arena};
|
||||
|
||||
REQUIRE(a1o.reserved() >= cfg.size_);
|
||||
REQUIRE(a1o.committed() == 0);
|
||||
REQUIRE(a1o.available() == 0);
|
||||
REQUIRE(a1o.allocated() == 0);
|
||||
|
||||
DArenaIterator ix = arena.begin();
|
||||
/* iteration not supported since we did not set
|
||||
* ArenaConfig.store_header_flag_
|
||||
*/
|
||||
REQUIRE(ix.is_invalid());
|
||||
REQUIRE(ix != ix);
|
||||
REQUIRE(arena.error_count_ == 1);
|
||||
REQUIRE(arena.last_error_.error_seq_ == 1);
|
||||
REQUIRE(arena.last_error_.error_ == error::alloc_iterator_not_supported);
|
||||
|
||||
DArenaIterator end_ix = arena.end();
|
||||
/* iteration not supported since we did not set
|
||||
* ArenaConfig.store_header_flag_
|
||||
*/
|
||||
REQUIRE(end_ix.is_invalid());
|
||||
REQUIRE(end_ix != end_ix);
|
||||
REQUIRE(arena.error_count_ == 2);
|
||||
REQUIRE(arena.last_error_.error_seq_ == 2);
|
||||
REQUIRE(arena.last_error_.error_ == error::alloc_iterator_not_supported);
|
||||
}
|
||||
|
||||
TEST_CASE("IAllocIterator-emptyarena", "[alloc2]")
|
||||
{
|
||||
/* verify iteration over empty arena */
|
||||
/* typed allocator a1o */
|
||||
ArenaConfig cfg { .name_ = "testarena",
|
||||
.size_ = 64*1024,
|
||||
.store_header_flag_ = true,
|
||||
.debug_flag_ = false };
|
||||
DArena arena = DArena::map(cfg);
|
||||
obj<AAllocator, DArena> a1o{&arena};
|
||||
|
||||
REQUIRE(a1o.reserved() >= cfg.size_);
|
||||
REQUIRE(a1o.committed() == 0);
|
||||
REQUIRE(a1o.available() == 0);
|
||||
REQUIRE(a1o.allocated() == 0);
|
||||
|
||||
DArenaIterator ix = arena.begin();
|
||||
DArenaIterator end_ix = arena.end();
|
||||
|
||||
REQUIRE(ix.is_valid());
|
||||
REQUIRE(end_ix.is_valid());
|
||||
|
||||
/* iteration not supported since we did not set */
|
||||
|
||||
/* arena is empty, so begin==end */
|
||||
REQUIRE(ix == end_ix);
|
||||
|
||||
REQUIRE(arena.error_count_ == 0);
|
||||
|
||||
/* empty iterator cannot be dereferenced */
|
||||
{
|
||||
AllocInfo bad_info = *ix;
|
||||
REQUIRE(!bad_info.is_valid());
|
||||
|
||||
REQUIRE(arena.error_count_ == 1);
|
||||
REQUIRE(arena.last_error_.error_seq_ == 1);
|
||||
REQUIRE(arena.last_error_.error_ == error::alloc_iterator_deref);
|
||||
}
|
||||
|
||||
/* empty iterator cannot be advanced */
|
||||
{
|
||||
ix.next();
|
||||
|
||||
REQUIRE(arena.error_count_ == 2);
|
||||
REQUIRE(arena.last_error_.error_seq_ == 2);
|
||||
REQUIRE(arena.last_error_.error_ == error::alloc_iterator_next);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("IAllocIterator-singlearena", "[alloc2]")
|
||||
{
|
||||
ArenaConfig cfg { .name_ = "testarena",
|
||||
.size_ = 64*1024,
|
||||
.store_header_flag_ = true,
|
||||
.debug_flag_ = false };
|
||||
DArena arena = DArena::map(cfg);
|
||||
obj<AAllocator, DArena> a1o{&arena};
|
||||
|
||||
REQUIRE(arena.error_count_ == 0);
|
||||
REQUIRE(a1o.reserved() >= cfg.size_);
|
||||
REQUIRE(a1o.committed() == 0);
|
||||
REQUIRE(a1o.available() == 0);
|
||||
REQUIRE(a1o.allocated() == 0);
|
||||
|
||||
/* arbitrary alloc size */
|
||||
size_t req_z = 13;
|
||||
|
||||
byte * mem = a1o.alloc(req_z);
|
||||
REQUIRE(arena.error_count_ == 0);
|
||||
REQUIRE(mem != nullptr);
|
||||
|
||||
DArenaIterator ix = arena.begin();
|
||||
DArenaIterator end_ix = arena.end();
|
||||
|
||||
REQUIRE(ix.is_valid());
|
||||
REQUIRE(end_ix.is_valid());
|
||||
|
||||
/* arena is non-empty, so begin!=end */
|
||||
REQUIRE (ix != end_ix);
|
||||
|
||||
REQUIRE(arena.error_count_ == 0);
|
||||
|
||||
/* valid iterator can be dereferenced */
|
||||
{
|
||||
AllocInfo info = *ix;
|
||||
|
||||
REQUIRE(arena.error_count_ == 0);
|
||||
REQUIRE(info.is_valid());
|
||||
REQUIRE(info.size() == padding::with_padding(req_z));
|
||||
|
||||
auto [payload_lo, payload_hi] = info.payload();
|
||||
|
||||
REQUIRE(payload_lo == mem);
|
||||
REQUIRE(payload_hi == mem + info.size());
|
||||
}
|
||||
|
||||
/* valid iterator can be advanced */
|
||||
{
|
||||
ix.next();
|
||||
|
||||
REQUIRE(arena.error_count_ == 0);
|
||||
REQUIRE(ix == end_ix);
|
||||
}
|
||||
}
|
||||
|
||||
} /*namespace ut*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end AllocIterator.test.cpp */
|
||||
|
|
@ -4,9 +4,11 @@
|
|||
set(UTEST_EXE utest.alloc2)
|
||||
set(UTEST_SRCS
|
||||
alloc2_utest_main.cpp
|
||||
arena.test.cpp
|
||||
objectmodel.test.cpp
|
||||
arena.test.cpp
|
||||
AllocIterator.test.cpp
|
||||
Collector.test.cpp
|
||||
DX1CollectorIterator.test.cpp
|
||||
random_allocs.cpp
|
||||
)
|
||||
|
||||
|
|
@ -19,5 +21,4 @@ if (ENABLE_TESTING)
|
|||
xo_external_target_dependency(${UTEST_EXE} Catch2 Catch2::Catch2)
|
||||
endif()
|
||||
|
||||
|
||||
# end CMakeLists.txt
|
||||
|
|
|
|||
64
xo-alloc2/utest/DX1CollectorIterator.test.cpp
Normal file
64
xo-alloc2/utest/DX1CollectorIterator.test.cpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/** @file DX1CollectorIterator.test.cpp
|
||||
*
|
||||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#include "Allocator.hpp"
|
||||
#include "AllocIterator.hpp"
|
||||
#include "gc/DX1CollectorIterator.hpp"
|
||||
#include "gc/IAllocator_DX1Collector.hpp"
|
||||
#include "gc/IAllocIterator_DX1CollectorIterator.hpp"
|
||||
#include "arena/ArenaConfig.hpp"
|
||||
#include "padding.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
namespace xo {
|
||||
using xo::mm::AAllocIterator;
|
||||
using xo::mm::IAllocIterator_Any;
|
||||
using xo::mm::IAllocIterator_Xfer;
|
||||
using xo::mm::IAllocIterator_DX1CollectorIterator;
|
||||
using xo::mm::DX1Collector;
|
||||
using xo::mm::DX1CollectorIterator;
|
||||
using xo::mm::CollectorConfig;
|
||||
using xo::mm::ArenaConfig;
|
||||
using xo::mm::AllocHeaderConfig;
|
||||
|
||||
namespace ut {
|
||||
TEST_CASE("IAllocIterator_Xfer_DX1CollectorIterator", "[alloc2]")
|
||||
{
|
||||
/* verify IAllocIterator_Xfer is constructible + satisfies concept checks */
|
||||
IAllocIterator_Xfer<DX1CollectorIterator, IAllocIterator_DX1CollectorIterator> xfer;
|
||||
REQUIRE(IAllocIterator_Xfer<DX1CollectorIterator, IAllocIterator_DX1CollectorIterator>::_valid);
|
||||
}
|
||||
|
||||
TEST_CASE("DX1CollectorIterator", "[alloc2][gc][DX1Collector]")
|
||||
{
|
||||
ArenaConfig arena_cfg = { .name_ = "_test_unused",
|
||||
.size_ = 4*1024*1024,
|
||||
.store_header_flag_ = true,
|
||||
.header_ = AllocHeaderConfig(0 /*guard_z*/,
|
||||
0xfd /*guard_byte*/,
|
||||
0 /*tseq_bits*/,
|
||||
0 /*age_bits*/,
|
||||
16 /*size_bits*/), };
|
||||
CollectorConfig cfg = { .arena_config_ = arena_cfg,
|
||||
.n_generation_ = 2,
|
||||
.gc_trigger_v_ = {{64*1024, 1024*1024, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0}} };
|
||||
|
||||
DX1Collector gc = DX1Collector{cfg};
|
||||
|
||||
auto ix = gc.begin();
|
||||
auto end_ix = gc.end();
|
||||
|
||||
REQUIRE(ix.is_valid());
|
||||
REQUIRE(end_ix.is_valid());
|
||||
|
||||
REQUIRE(ix == end_ix);
|
||||
}
|
||||
} /*namespace ut*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end DX1CollectorIterator.test.cpp */
|
||||
|
|
@ -269,7 +269,11 @@ namespace xo {
|
|||
REQUIRE(a1o.last_error().error_ == error::none);
|
||||
REQUIRE(a1o.last_error().error_seq_ == 0);
|
||||
|
||||
REQUIRE(a1o.allocated() == cfg.header_.guard_z_ + sizeof(header_type) + z0 + pad + cfg.header_.guard_z_);
|
||||
REQUIRE(a1o.allocated() == (cfg.header_.guard_z_
|
||||
+ sizeof(header_type)
|
||||
+ z0
|
||||
+ pad
|
||||
+ cfg.header_.guard_z_));
|
||||
REQUIRE(a1o.allocated() <= a1o.committed());
|
||||
REQUIRE(a1o.allocated() + a1o.available() == a1o.committed());
|
||||
REQUIRE(a1o.committed() <= a1o.reserved());
|
||||
|
|
|
|||
|
|
@ -162,6 +162,19 @@ namespace utest {
|
|||
info.guard_hi().second == nullptr);
|
||||
|
||||
}
|
||||
|
||||
#ifdef NOT_YET // to verify iteration here, need iterator support in AAllocator
|
||||
|
||||
/* verify iteration visits all the allocs, exactly once */
|
||||
{
|
||||
auto alloc_map = allocs_by_lo_map;
|
||||
|
||||
for (AllocInfo info : mm) {
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ namespace xo {
|
|||
* concerned.
|
||||
**/
|
||||
template <typename DOther>
|
||||
DOther * downcast()
|
||||
DOther * downcast() const
|
||||
requires (std::is_same_v<DataType, DVariantPlaceholder>)
|
||||
{
|
||||
if (data_ && (typeseq::id<DOther>() == this->iface()->_typeseq())) {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,13 @@ namespace xo {
|
|||
obj() {}
|
||||
explicit obj(Super::DataPtr d) : Super(d) {}
|
||||
|
||||
/** copy constructor **/
|
||||
/** copy constructor
|
||||
*
|
||||
* Intended for use cases:
|
||||
* obj<AFoo, DRepr> lhs = obj<AFoo, DRepr> // same type on rhs
|
||||
* obj<AFoo> lhs = obj<AFoo, DRepr> // variant lhs, typed rhs
|
||||
* obj<Afoo> lhs = obj<AFoo> // variant on both sides
|
||||
**/
|
||||
template <typename DOther>
|
||||
obj(const obj<AFacet, DOther> & other)
|
||||
requires (std::is_same_v<DRepr, DVariantPlaceholder>
|
||||
|
|
@ -98,13 +104,6 @@ namespace xo {
|
|||
static obj<AFacet, DRepr> mkobj(DRepr * data) { obj<AFacet, DRepr> x(data); return x; }
|
||||
};
|
||||
|
||||
#ifdef DEPRECATED
|
||||
template <typename AFacet, typename DRepr>
|
||||
inline obj<AFacet, DRepr>
|
||||
with_facet(DRepr * data) {
|
||||
return obj<AFacet, DRepr>(data);
|
||||
}
|
||||
#endif
|
||||
} /*namespace facet*/
|
||||
|
||||
using facet::obj;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue