xo-arena: CircularBuffer utest + bugfixes
This commit is contained in:
parent
7db67acadc
commit
deaa55564e
6 changed files with 114 additions and 10 deletions
|
|
@ -20,6 +20,7 @@ namespace xo {
|
|||
/** optional name, for diagnostics **/
|
||||
std::string name_;
|
||||
/** hard maximum buffer size = reserved virtual memory.
|
||||
* However actual max will be this value rounded up to at least page size.
|
||||
* Buffer will generally map much less than this amount of memory
|
||||
**/
|
||||
std::size_t max_capacity_ = 0;
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ namespace xo {
|
|||
const_span_type reserved_range() const noexcept { return reserved_range_; }
|
||||
const_span_type mapped_range() const noexcept { return mapped_range_; }
|
||||
const_span_type occupied_range() const noexcept { return occupied_range_; }
|
||||
const_span_type input_range() const noexcept { return input_range_; }
|
||||
|
||||
/** verify DCircularBuffer invariants.
|
||||
* Act on failure according to policy @p p
|
||||
|
|
@ -98,6 +99,8 @@ namespace xo {
|
|||
/** @defgroup mm-circularbuffer-nonconst-methods CircularBuffer non-const methods **/
|
||||
///@{
|
||||
|
||||
span_type input_range() noexcept { return input_range_; }
|
||||
|
||||
/** copy memory in span @p r into buffer starting at the end of
|
||||
* @ref occupied_range_. Map new physical memory as needed.
|
||||
* On success returns empty suffix of @p r.
|
||||
|
|
@ -139,7 +142,7 @@ namespace xo {
|
|||
* Caller represents that it won't need to read this memory again
|
||||
* unless overlaps with a pinned span.
|
||||
**/
|
||||
void consume(span_type r);
|
||||
void consume(const_span_type input);
|
||||
|
||||
/** pin memory range @p r. circular buffer will not touch
|
||||
* addresses that appear in any pinned range.
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ namespace xo {
|
|||
/** typealias for span size (in units of CharT) **/
|
||||
using size_type = std::uint64_t;
|
||||
|
||||
/** typealias for span elements **/
|
||||
using value_type = CharT;
|
||||
|
||||
///@}
|
||||
|
||||
public:
|
||||
|
|
@ -58,7 +61,8 @@ namespace xo {
|
|||
* A null span can be concatenated with any other span
|
||||
* without triggering matching-endpoint asserts.
|
||||
**/
|
||||
static span make_null() { return span(static_cast<CharT*>(nullptr), static_cast<CharT*>(nullptr)); }
|
||||
static span make_null() { return span(static_cast<CharT*>(nullptr),
|
||||
static_cast<CharT*>(nullptr)); }
|
||||
|
||||
/** @brief create span for C-style string @p cstr **/
|
||||
static span from_cstr(const CharT * cstr) {
|
||||
|
|
@ -69,13 +73,21 @@ namespace xo {
|
|||
}
|
||||
|
||||
/** @brief create span from std::string @p str **/
|
||||
static span from_string(const std::string& str) {
|
||||
static span from_string(const std::string & str) {
|
||||
CharT * lo = &(*str.begin());
|
||||
CharT * hi = &(*str.end());
|
||||
|
||||
return span(lo, hi);
|
||||
}
|
||||
|
||||
/** @brief create span from std::string @p str **/
|
||||
static span from_string_view(const std::string_view & sv) {
|
||||
CharT * lo = &(*sv.begin());
|
||||
CharT * hi = &(*sv.end());
|
||||
|
||||
return span(lo, hi);
|
||||
}
|
||||
|
||||
/** @brief concatenate two contiguous spans */
|
||||
static span concat(const span & span1, const span & span2) {
|
||||
if (span1.is_null())
|
||||
|
|
@ -119,6 +131,11 @@ namespace xo {
|
|||
return (other.lo() <= lo_) && (hi_ <= other.hi());
|
||||
}
|
||||
|
||||
/** convert to string view **/
|
||||
std::string_view to_string_view() const {
|
||||
return std::string_view((const char *)lo_, (const char *)hi_);
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
/** @defgroup span-general-methods **/
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
#include <sys/mman.h>
|
||||
|
||||
namespace xo {
|
||||
using xo::print::operator<<;
|
||||
using xo::print::printspan;
|
||||
|
||||
namespace mm {
|
||||
|
||||
DCircularBuffer::DCircularBuffer(DCircularBuffer && other)
|
||||
|
|
@ -152,6 +155,7 @@ namespace xo {
|
|||
::memcpy(occupied_range_.hi(), src.lo(), copy_z);
|
||||
|
||||
this->occupied_range_ += span_type(dest.lo(), copy_z);
|
||||
this->input_range_ += span_type(dest.lo(), copy_z);
|
||||
|
||||
return src.after_prefix(copy_z);
|
||||
}
|
||||
|
|
@ -198,30 +202,49 @@ namespace xo {
|
|||
}
|
||||
|
||||
void
|
||||
DCircularBuffer::consume(span_type r)
|
||||
DCircularBuffer::consume(const_span_type input)
|
||||
{
|
||||
if (r.lo() != input_range_.lo()) {
|
||||
scope log(XO_DEBUG(false), xtag("input", input.to_string_view()));
|
||||
|
||||
if (input.lo() != input_range_.lo()) {
|
||||
assert(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (r.hi() > occupied_range_.hi()) {
|
||||
if (input.hi() > occupied_range_.hi()) {
|
||||
assert(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (occupied_range_.lo() < input_range_.lo()) {
|
||||
log && log("pinned range prevents shrinking occupied range");
|
||||
|
||||
/* here: a pinned range prevents shrinking occupied_range */
|
||||
|
||||
this->input_range_ = input_range_.suffix_from(r.hi());
|
||||
this->input_range_
|
||||
= input_range_.suffix_from((span_type::value_type *)input.hi());
|
||||
} else {
|
||||
log && log(xtag("msg", "will shrink occupied range"),
|
||||
xtag("input.lo", (void*)input.lo()),
|
||||
xtag("input.hi", (void*)input.hi()),
|
||||
xtag("stored.lo", (void*)input_range_.lo()),
|
||||
xtag("stored.hi", (void*)input_range_.hi())
|
||||
);
|
||||
|
||||
/* here: input; recompute occupied boundary */
|
||||
|
||||
this->input_range_ = input_range_.suffix_from(r.hi());
|
||||
this->input_range_
|
||||
= input_range_.suffix_from((span_type::value_type *)input.hi());
|
||||
|
||||
log && log(xtag("occupied", occupied_range_.size()),
|
||||
xtag("input", input_range_.size()));
|
||||
|
||||
this->_shrink_occupied_to_fit();
|
||||
|
||||
log && log(xtag("occupied", occupied_range_.size()),
|
||||
xtag("input", input_range_.size()));
|
||||
}
|
||||
|
||||
this->_check_reset_map_start();
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@ set(UTEST_SRCS
|
|||
DArena.test.cpp
|
||||
DArenaVector.test.cpp
|
||||
DArenaHashMap.test.cpp
|
||||
DCircularBuffer.test.cpp
|
||||
# DArenaIterator.test.cpp
|
||||
# Collector.test.cpp
|
||||
# DX1CollectorIterator.test.cpp
|
||||
# random_allocs.cpp
|
||||
)
|
||||
|
||||
|
|
|
|||
61
xo-arena/utest/DCircularBuffer.test.cpp
Normal file
61
xo-arena/utest/DCircularBuffer.test.cpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/** @file DCircularBuffer.test.cpp
|
||||
*
|
||||
* @author Roland Conybeare, Jan 2026
|
||||
**/
|
||||
|
||||
#include "DCircularBuffer.hpp"
|
||||
#include "print.hpp"
|
||||
#include <xo/indentlog/print/tag.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
namespace xo {
|
||||
using xo::mm::DCircularBuffer;
|
||||
using xo::mm::CircularBufferConfig;
|
||||
using xo::mm::span;
|
||||
using std::byte;
|
||||
|
||||
namespace ut {
|
||||
TEST_CASE("DCircularBuffer-tiny", "[arena][DCircularBuffer]")
|
||||
{
|
||||
// buffer works with bytes, not chars
|
||||
|
||||
CircularBufferConfig cfg { .name_ = "testcbuf",
|
||||
.max_capacity_ = 1 };
|
||||
DCircularBuffer buf = DCircularBuffer::map(cfg);
|
||||
|
||||
REQUIRE(buf.reserved_range().size() == getpagesize());
|
||||
REQUIRE(buf.mapped_range().size() == 0);
|
||||
REQUIRE(buf.occupied_range().size() == 0);
|
||||
REQUIRE(buf.input_range().size() == 0);
|
||||
|
||||
REQUIRE(buf.verify_ok(verify_policy::log_only()));
|
||||
REQUIRE(buf.get_append_span(1).size() == getpagesize());
|
||||
REQUIRE(buf.mapped_range().size() == getpagesize());
|
||||
REQUIRE(buf.occupied_range().size() == 0);
|
||||
REQUIRE(buf.input_range().size() == 0);
|
||||
|
||||
std::string_view s0 = "abcdefghijk";
|
||||
/* return value is unaccepted suffix of input */
|
||||
REQUIRE(buf.append(DCircularBuffer::span_type((byte *)s0.begin(),
|
||||
(byte *)s0.end())).empty());
|
||||
REQUIRE(buf.verify_ok(verify_policy::log_only()));
|
||||
REQUIRE(buf.mapped_range().size() == getpagesize());
|
||||
REQUIRE(buf.occupied_range().size() == s0.size());
|
||||
REQUIRE(buf.input_range().size() == s0.size());
|
||||
|
||||
std::string_view s1 = "lmnopq";
|
||||
REQUIRE(buf.append(DCircularBuffer::span_type((byte *)s1.begin(),
|
||||
(byte *)s1.end())).empty());
|
||||
REQUIRE(buf.mapped_range().size() == getpagesize());
|
||||
REQUIRE(buf.occupied_range().size() == s0.size() + s1.size());
|
||||
|
||||
REQUIRE(buf.occupied_range().to_string_view() == std::string_view("abcdefghijklmnopq"));
|
||||
|
||||
buf.consume(buf.occupied_range().prefix(3));
|
||||
|
||||
REQUIRE(buf.occupied_range().to_string_view() == std::string_view("defghijklmnopq"));
|
||||
}
|
||||
} /*namespace ut*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end DCircularBuffer.test.cpp */
|
||||
Loading…
Add table
Add a link
Reference in a new issue