Add 'xo-alloc/' from commit '8970f51dbd'
git-subtree-dir: xo-alloc git-subtree-mainline:b1ecf98791git-subtree-split:8970f51dbd
This commit is contained in:
commit
43161d5e89
13 changed files with 498 additions and 0 deletions
28
xo-alloc/CMakeLists.txt
Normal file
28
xo-alloc/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# alloc/CMakeLists.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(xo_alloc VERSION 0.1)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(cmake/xo-bootstrap-macros.cmake)
|
||||
|
||||
xo_cxx_toplevel_options3()
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# c++ settings
|
||||
|
||||
set(PROJECT_CXX_FLAGS "")
|
||||
#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") # gcc-only!
|
||||
add_definitions(${PROJECT_CXX_FLAGS})
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# must complete definition of expression lib before configuring examples
|
||||
add_subdirectory(src/alloc)
|
||||
|
||||
xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
add_subdirectory(utest)
|
||||
3
xo-alloc/README.md
Normal file
3
xo-alloc/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# xo-alloc -- arena allocator with rudimentary GC support
|
||||
|
||||
Xo-alloc is a lightweight arena allocator
|
||||
35
xo-alloc/cmake/xo-bootstrap-macros.cmake
Executable file
35
xo-alloc/cmake/xo-bootstrap-macros.cmake
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
# ----------------------------------------------------------------
|
||||
# for example:
|
||||
# $ PREFIX=/usr/local # for example
|
||||
# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build
|
||||
#
|
||||
# will get
|
||||
# CMAKE_MODULE_PATH
|
||||
# from xo-cmake-config --cmake-module-path
|
||||
#
|
||||
# and expect .cmake macros in
|
||||
# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED)
|
||||
|
||||
if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND")
|
||||
message(FATAL "could not find xo-cmake-config executable")
|
||||
endif()
|
||||
|
||||
message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}")
|
||||
|
||||
if (NOT XO_SUBMODULE_BUILD)
|
||||
if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix))
|
||||
# default to typical install location for xo-project-macros
|
||||
execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH)
|
||||
message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# needs to have been installed somewhere on CMAKE_MODULE_PATH,
|
||||
# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX)
|
||||
#
|
||||
include(xo_macros/xo_cxx)
|
||||
|
||||
xo_cxx_bootstrap_message()
|
||||
7
xo-alloc/cmake/xo_allocConfig.cmake.in
Normal file
7
xo-alloc/cmake/xo_allocConfig.cmake.in
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(indentlog)
|
||||
#find_dependency(xo_flatstring)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
20
xo-alloc/include/xo/alloc/GCAlloc.hpp
Normal file
20
xo-alloc/include/xo/alloc/GCAlloc.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* file GCAlloc.hpp
|
||||
*
|
||||
* author: Roland Conybeare, Jul 2025
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace xo {
|
||||
namespace gc {
|
||||
class GC : public IAlloc {
|
||||
enum class Space { A, B, N_Space };
|
||||
enum class Gen { Nursery, Tenured };
|
||||
|
||||
};
|
||||
|
||||
} /*namespace mem */
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/* end GCAlloc.hpp */
|
||||
57
xo-alloc/include/xo/alloc/IAlloc.hpp
Normal file
57
xo-alloc/include/xo/alloc/IAlloc.hpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/* file IAlloc.hpp
|
||||
*
|
||||
* author: Roland Conybeare, Jul 2025
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
template <typename T>
|
||||
using up = std::unique_ptr<T>;
|
||||
|
||||
namespace gc {
|
||||
/** @class IAllocator
|
||||
* @brief memory allocation interface with limited garbaga collector support
|
||||
**/
|
||||
class IAlloc {
|
||||
public:
|
||||
virtual ~IAlloc() {}
|
||||
|
||||
/** allocator size in bytes (up to soft limit).
|
||||
* Includes unallocated mmeory
|
||||
**/
|
||||
virtual std::size_t size() const = 0;
|
||||
/** number of unallocated bytes available (up to soft limit)
|
||||
* from this allocator
|
||||
**/
|
||||
virtual std::size_t available() const = 0;
|
||||
/** number of bytes allocated from this allocator **/
|
||||
virtual std::size_t allocated() const = 0;
|
||||
/** true iff object at address @p x was allocated by this allocator,
|
||||
* and before checkpoint
|
||||
**/
|
||||
virtual bool is_before_checkpoint(const std::uint8_t * x) const = 0;
|
||||
/** number of bytes allocated before @ref checkpoint **/
|
||||
virtual std::size_t before_checkpoint() const = 0;
|
||||
/** number of bytes allocated since @ref checkpoint **/
|
||||
virtual std::size_t after_checkpoint() const = 0;
|
||||
|
||||
/** reset allocator to empty state. **/
|
||||
virtual void clear() = 0;
|
||||
/** remember allocator state. All currently-allocated addresses x
|
||||
* will satisfy is_before_checkpoint(x). Subsequent allocations x
|
||||
* will fail is_before_checkpoint(x), until checkpoint superseded
|
||||
* by @ref clear or another call to @ref checkpoint
|
||||
**/
|
||||
virtual void checkpoint() = 0;
|
||||
/** allocate @p z bytes of memory. returns pointer to first address **/
|
||||
virtual std::uint8_t * alloc(std::size_t z) = 0;
|
||||
};
|
||||
} /*namespace gc*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/* end IAlloc.hpp */
|
||||
90
xo-alloc/include/xo/alloc/LinearAlloc.hpp
Normal file
90
xo-alloc/include/xo/alloc/LinearAlloc.hpp
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/* file LinearAlloc.hpp
|
||||
*
|
||||
* author: Roland Conybeare, Jul 2025
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IAlloc.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace gc {
|
||||
/** @class LinearAlloc
|
||||
* @brief Bump allocator with fixed capacity
|
||||
*
|
||||
* @text
|
||||
*
|
||||
* before @ref release_redline_memory
|
||||
*
|
||||
* <-----allocated----> <-free-> <-reserved->
|
||||
* XXXXXXXXXXXXXXXXXXXX______________________
|
||||
* ^ ^ ^ ^
|
||||
* lo free redline hi
|
||||
* limit
|
||||
*
|
||||
* after @ref release_redline_memory
|
||||
*
|
||||
* <-----allocated----> <--------free------->
|
||||
* XXXXXXXXXXXXXXXXXXXX______________________
|
||||
* ^ ^ ^
|
||||
* lo free hi
|
||||
* limit
|
||||
* @endtext
|
||||
*
|
||||
* TODO: rename to ArenaAlloc
|
||||
**/
|
||||
class LinearAlloc : public IAlloc {
|
||||
public:
|
||||
~LinearAlloc();
|
||||
|
||||
/** create allocator with capacity @p z,
|
||||
* with reserved capacity @p redline_z.
|
||||
**/
|
||||
static up<LinearAlloc> make(std::size_t redline_z, std::size_t z);
|
||||
|
||||
std::uint8_t * free_ptr() const { return free_ptr_; }
|
||||
void set_free_ptr(std::uint8_t * x);
|
||||
|
||||
// inherited from IAlloc...
|
||||
|
||||
virtual std::size_t size() const override;
|
||||
virtual std::size_t available() const override;
|
||||
virtual std::size_t allocated() const override;
|
||||
virtual bool is_before_checkpoint(const std::uint8_t * x) const override;
|
||||
virtual std::size_t before_checkpoint() const override;
|
||||
virtual std::size_t after_checkpoint() const override;
|
||||
|
||||
virtual void clear() override;
|
||||
virtual void checkpoint() override;
|
||||
virtual std::uint8_t * alloc(std::size_t z) override;
|
||||
|
||||
|
||||
private:
|
||||
LinearAlloc(std::size_t rz, std::size_t z);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Invariants:
|
||||
* - @ref free_ always a multiple of word size (assumed to be sizeof(void*))
|
||||
**/
|
||||
|
||||
/** allocator owns memory in range [@ref lo_, @ref hi_) **/
|
||||
std::uint8_t * lo_ = nullptr;
|
||||
/** checkpoint (for GC support); divides objects into
|
||||
* older (addresses below checkpoint)
|
||||
* and younger (addresses above checkpoint)
|
||||
**/
|
||||
std::uint8_t * checkpoint_;
|
||||
/** free pointer. memory in range [@ref free_, @ref limit_) available **/
|
||||
std::uint8_t * free_ptr_ = nullptr;
|
||||
/** soft limit: end of released memory **/
|
||||
std::uint8_t * limit_ = nullptr;
|
||||
/** hard limit: end of allocated memory **/
|
||||
std::uint8_t * hi_ = nullptr;
|
||||
};
|
||||
|
||||
} /*namespace gc*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/* end LinearAlloc.hpp */
|
||||
47
xo-alloc/include/xo/alloc/ListAlloc.hpp
Normal file
47
xo-alloc/include/xo/alloc/ListAlloc.hpp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* file ListAlloc.hpp
|
||||
*
|
||||
* author: Roland Conybeare, Jul 2025
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IAlloc.hpp"
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace gc {
|
||||
/** GC-compatible allocator using a linked list of buckets.
|
||||
*
|
||||
* GC Support:
|
||||
* - reserved memory, released after call to @ref release_redline_memory.
|
||||
*
|
||||
* TODO: reserve address space using mmap,
|
||||
* but don't commit until alloc requires it.
|
||||
**/
|
||||
class ListAlloc : public IAlloc {
|
||||
public:
|
||||
ListAlloc(LinearAlloc* hd,
|
||||
std::size_t cz, std::size_t nz; std::size_tz,
|
||||
LinearAlloc* marked, bool use_redline,
|
||||
bool redlined_flag, OnEmptyFn on_overflow);
|
||||
~ListAlloc();
|
||||
|
||||
static up<ListAlloc> make(std::size_t cz, std::size_t nz,
|
||||
OnEmptyFn on_overflow);
|
||||
|
||||
private:
|
||||
std::size_t start_z_ = 0;
|
||||
LinearAlloc* hd_ = nullptr;
|
||||
std::size_t current_z_ = 0;;
|
||||
std::size_t next_z_ = 0;;
|
||||
std::size_t total_z_ = 0;
|
||||
bool use_redline_ = false;
|
||||
bool redlined_flag_ = false;
|
||||
|
||||
};
|
||||
} /*namespace gc*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/* end ListAlloc.hpp */
|
||||
11
xo-alloc/src/alloc/CMakeLists.txt
Normal file
11
xo-alloc/src/alloc/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# alloc/CMakeLists.txt
|
||||
|
||||
set(SELF_LIB xo_alloc)
|
||||
set(SELF_SRCS
|
||||
LinearAlloc.cpp
|
||||
)
|
||||
|
||||
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})
|
||||
xo_dependency(${SELF_LIB} indentlog)
|
||||
|
||||
#end CMakeLists.txt
|
||||
135
xo-alloc/src/alloc/LinearAlloc.cpp
Normal file
135
xo-alloc/src/alloc/LinearAlloc.cpp
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/* file LinearAlloc.cpp
|
||||
*
|
||||
* author: Roland Conybeare
|
||||
*/
|
||||
|
||||
#include "LinearAlloc.hpp"
|
||||
#include "xo/indentlog/print/tag.hpp"
|
||||
#include <cassert>
|
||||
|
||||
namespace xo {
|
||||
namespace gc {
|
||||
LinearAlloc::LinearAlloc(std::size_t rz, std::size_t z)
|
||||
{
|
||||
this->lo_ = (new std::uint8_t [rz + z]);
|
||||
this->checkpoint_ = lo_;
|
||||
this->free_ptr_ = lo_;
|
||||
this->limit_ = lo_ + z;
|
||||
this->hi_ = limit_ + rz;
|
||||
|
||||
if (!lo_) {
|
||||
throw std::runtime_error(tostr("LinearAlloc: allocation failed",
|
||||
xtag("size", rz + z)));
|
||||
}
|
||||
}
|
||||
|
||||
LinearAlloc::~LinearAlloc()
|
||||
{
|
||||
delete [] this->lo_;
|
||||
|
||||
// hygiene..
|
||||
|
||||
this->lo_ = nullptr;
|
||||
this->checkpoint_ = nullptr;
|
||||
this->free_ptr_ = nullptr;
|
||||
this->limit_ = nullptr;
|
||||
this->hi_ = nullptr;
|
||||
}
|
||||
|
||||
up<LinearAlloc>
|
||||
LinearAlloc::make(std::size_t rz, std::size_t z)
|
||||
{
|
||||
return up<LinearAlloc>(new LinearAlloc(rz, z));
|
||||
}
|
||||
|
||||
void
|
||||
LinearAlloc::set_free_ptr(std::uint8_t * x)
|
||||
{
|
||||
assert(lo_ <= x);
|
||||
assert(x < limit_);
|
||||
|
||||
if (lo_ <= x && x < limit_) {
|
||||
this->free_ptr_ = x;
|
||||
} else {
|
||||
throw std::runtime_error(tostr("LinearAllog::set_free_ptr(x): expected lo <= x < limit",
|
||||
xtag("lo", lo_), xtag("x", x), xtag("limit", limit_)));
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t
|
||||
LinearAlloc::size() const {
|
||||
return limit_ - lo_;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
LinearAlloc::available() const {
|
||||
return limit_ - free_ptr_;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
LinearAlloc::allocated() const {
|
||||
return free_ptr_ - lo_;
|
||||
}
|
||||
|
||||
bool
|
||||
LinearAlloc::is_before_checkpoint(const std::uint8_t * x) const {
|
||||
return (lo_ <= x) && (x < checkpoint_);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
LinearAlloc::before_checkpoint() const
|
||||
{
|
||||
return checkpoint_ - lo_;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
LinearAlloc::after_checkpoint() const
|
||||
{
|
||||
return free_ptr_ - checkpoint_;
|
||||
}
|
||||
|
||||
void
|
||||
LinearAlloc::clear()
|
||||
{
|
||||
this->checkpoint_ = lo_;
|
||||
this->free_ptr_ = lo_;
|
||||
this->limit_ = lo_;
|
||||
}
|
||||
|
||||
void
|
||||
LinearAlloc::checkpoint()
|
||||
{
|
||||
this->checkpoint_ = this->free_ptr_;
|
||||
}
|
||||
|
||||
std::uint8_t *
|
||||
LinearAlloc::alloc(std::size_t z)
|
||||
{
|
||||
/* word size for alignment */
|
||||
constexpr uint32_t c_bpw = sizeof(void*);
|
||||
|
||||
std::uintptr_t free_u64 = reinterpret_cast<std::uintptr_t>(free_ptr_);
|
||||
|
||||
assert(free_u64 % c_bpw == 0ul);
|
||||
|
||||
/* round up to multiple of c_bpw */
|
||||
std::uint32_t dz = (c_bpw - (z % c_bpw));
|
||||
z += dz;
|
||||
|
||||
assert(z % c_bpw == 0ul);
|
||||
|
||||
std::uint8_t * retval = this->free_ptr_;
|
||||
|
||||
this->free_ptr_ += z;
|
||||
|
||||
if (free_ptr_ > limit_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
} /*namespace gc*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/* end LinearAlloc.cpp */
|
||||
10
xo-alloc/utest/CMakeLists.txt
Normal file
10
xo-alloc/utest/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# build unittest alloc/utest
|
||||
|
||||
set(SELF_EXE utest.alloc)
|
||||
set(SELF_SRCS
|
||||
alloc_utest_main.cpp
|
||||
LinearAlloc.test.cpp)
|
||||
|
||||
xo_add_utest_executable(${SELF_EXE} ${SELF_SRCS})
|
||||
xo_self_dependency(${SELF_EXE} xo_alloc)
|
||||
xo_external_target_dependency(${SELF_EXE} Catch2 Catch2::Catch2)
|
||||
49
xo-alloc/utest/LinearAlloc.test.cpp
Normal file
49
xo-alloc/utest/LinearAlloc.test.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* @file LinearAlloc.test.cpp
|
||||
*
|
||||
* author: Roland Conybeare, Jul 2025
|
||||
*/
|
||||
|
||||
#include "xo/alloc/LinearAlloc.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
namespace xo {
|
||||
using xo::gc::LinearAlloc;
|
||||
|
||||
namespace ut {
|
||||
|
||||
namespace {
|
||||
struct testcase_alloc {
|
||||
testcase_alloc(std::size_t rz, std::size_t z)
|
||||
: redline_z_{rz}, arena_z_{z} {}
|
||||
|
||||
std::size_t redline_z_;
|
||||
std::size_t arena_z_;
|
||||
|
||||
};
|
||||
|
||||
std::vector<testcase_alloc>
|
||||
s_testcase_v = {
|
||||
testcase_alloc(0, 4096)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("linearalloc", "[alloc]")
|
||||
{
|
||||
for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) {
|
||||
const testcase_alloc & tc = s_testcase_v[i_tc];
|
||||
|
||||
auto alloc = LinearAlloc::make(tc.redline_z_, tc.arena_z_);
|
||||
|
||||
REQUIRE(alloc.get());
|
||||
REQUIRE(alloc->size() == tc.arena_z_);
|
||||
REQUIRE(alloc->available() == tc.arena_z_);
|
||||
REQUIRE(alloc->allocated() == 0);
|
||||
REQUIRE(alloc->is_before_checkpoint(alloc->free_ptr()) == false);
|
||||
REQUIRE(alloc->before_checkpoint() == 0);
|
||||
REQUIRE(alloc->after_checkpoint() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
} /*namespace ut */
|
||||
} /*namespace xo*/
|
||||
6
xo-alloc/utest/alloc_utest_main.cpp
Normal file
6
xo-alloc/utest/alloc_utest_main.cpp
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* file alloc_utest_main.cpp */
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
/* end alloc_utest_main.cpp */
|
||||
Loading…
Add table
Add a link
Reference in a new issue