Skip to content

Commit

Permalink
Merge pull request #9 from vibe-d/add_internal_appenders
Browse files Browse the repository at this point in the history
Add vibe.container.internal.appender module.
  • Loading branch information
l-kramer authored Feb 15, 2024
2 parents 391519b + dc885ee commit 9044a78
Showing 1 changed file with 273 additions and 0 deletions.
273 changes: 273 additions & 0 deletions source/vibe/container/internal/appender.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
module vibe.container.internal.appender;

import vibe.container.internal.utilallocator;
import std.algorithm.comparison : max;
import std.traits : Unqual, hasAliasing, hasElaborateDestructor, hasIndirections;


enum AppenderResetMode {
keepData,
freeData,
reuseData
}

struct AllocAppender(ArrayType : E[], E) {
alias ElemType = Unqual!E;

static assert(!hasIndirections!E && !hasElaborateDestructor!E);

private {
ElemType[] m_data;
ElemType[] m_remaining;
IAllocator m_alloc;
bool m_allocatedBuffer = false;
}

this(IAllocator alloc, ElemType[] initial_buffer = null)
{
m_alloc = alloc;
m_data = initial_buffer;
m_remaining = initial_buffer;
}

@disable this(this);

@property ArrayType data() { return cast(ArrayType)m_data[0 .. m_data.length - m_remaining.length]; }

void reset(AppenderResetMode reset_mode = AppenderResetMode.keepData)
{
if (reset_mode == AppenderResetMode.keepData) m_data = null;
else if (reset_mode == AppenderResetMode.freeData) { if (m_allocatedBuffer) m_alloc.deallocate(m_data); m_data = null; }
m_remaining = m_data;
}

/** Grows the capacity of the internal buffer so that it can hold a minumum amount of elements.
Params:
amount = The minimum amount of elements that shall be appendable without
triggering a re-allocation.
*/
void reserve(size_t amount)
@trusted {
size_t nelems = m_data.length - m_remaining.length;
if (!m_data.length) {
m_data = cast(ElemType[])m_alloc.allocate(amount*E.sizeof);
m_remaining = m_data;
m_allocatedBuffer = true;
}
if (m_remaining.length < amount) {
if (m_allocatedBuffer) {
void[] vdata = m_data;
m_alloc.reallocate(vdata, (nelems+amount)*E.sizeof);
m_data = () @trusted { return cast(ElemType[])vdata; } ();
} else {
auto newdata = cast(ElemType[])m_alloc.allocate((nelems+amount)*E.sizeof);
newdata[0 .. nelems] = m_data[0 .. nelems];
m_data = newdata;
m_allocatedBuffer = true;
}
}
m_remaining = m_data[nelems .. m_data.length];
}

void put(E el)
@safe {
if( m_remaining.length == 0 ) grow(1);
m_remaining[0] = el;
m_remaining = m_remaining[1 .. $];
}

void put(ArrayType arr)
@safe {
if (m_remaining.length < arr.length) grow(arr.length);
m_remaining[0 .. arr.length] = arr[];
m_remaining = m_remaining[arr.length .. $];
}

static if( !hasAliasing!E ){
void put(in ElemType[] arr)
@trusted
{
put(cast(ArrayType)arr);
}
}

static if( is(ElemType == char) ){
void put(dchar el)
@safe
{
import std.utf : encode;

if( el < 128 ) put(cast(char)el);
else {
char[4] buf;
auto len = encode(buf, el);
put(() @trusted { return cast(ArrayType)buf[0 .. len]; }());
}
}
}

static if( is(ElemType == wchar) ){
void put(dchar el)
@safe
{
if( el < 128 ) put(cast(wchar)el);
else {
wchar[3] buf;
auto len = std.utf.encode(buf, el);
put(() @trusted { return cast(ArrayType)buf[0 .. len]; } ());
}
}
}

static if (!is(E == immutable) || !hasAliasing!E) {
/** Appends a number of bytes in-place.
The delegate will get the memory slice of the memory that follows
the already written data. Use `reserve` to ensure that this slice
has enough room. The delegate should overwrite as much of the
slice as desired and then has to return the number of elements
that should be appended (counting from the start of the slice).
*/
void append(scope size_t delegate(scope ElemType[] dst) @safe del)
{
auto n = del(m_remaining);
assert(n <= m_remaining.length);
m_remaining = m_remaining[n .. $];
}
}

void grow(size_t min_free)
{
if( !m_data.length && min_free < 16 ) min_free = 16;

auto min_size = m_data.length + min_free - m_remaining.length;
auto new_size = max(m_data.length, 16);
while( new_size < min_size )
new_size = (new_size * 3) / 2;
reserve(new_size - m_data.length + m_remaining.length);
}
}

unittest {
auto a = AllocAppender!string(theAllocator());
a.put("Hello");
a.put(' ');
a.put("World");
assert(a.data == "Hello World");
a.reset();
assert(a.data == "");
}

unittest {
char[4] buf;
auto a = AllocAppender!string(theAllocator(), buf);
a.put("He");
assert(a.data == "He");
assert(a.data.ptr == buf.ptr);
a.put("ll");
assert(a.data == "Hell");
assert(a.data.ptr == buf.ptr);
a.put('o');
assert(a.data == "Hello");
assert(a.data.ptr != buf.ptr);
}

unittest {
char[4] buf;
auto a = AllocAppender!string(theAllocator(), buf);
a.put("Hello");
assert(a.data == "Hello");
assert(a.data.ptr != buf.ptr);
}

unittest {
auto app = AllocAppender!(int[])(theAllocator);
app.reserve(2);
app.append((scope mem) {
assert(mem.length >= 2);
mem[0] = 1;
mem[1] = 2;
return size_t(2);
});
assert(app.data == [1, 2]);
}

unittest {
auto app = AllocAppender!string(theAllocator);
app.reserve(3);
app.append((scope mem) {
assert(mem.length >= 3);
mem[0] = 'f';
mem[1] = 'o';
mem[2] = 'o';
return size_t(3);
});
assert(app.data == "foo");
}


struct FixedAppender(ArrayType : E[], size_t NELEM, E) {
alias ElemType = Unqual!E;
private {
ElemType[NELEM] m_data;
size_t m_fill;
}

void clear()
{
m_fill = 0;
}

void put(E el)
{
m_data[m_fill++] = el;
}

static if( is(ElemType == char) ){
void put(dchar el)
{
import std.utf : encode;

if( el < 128 ) put(cast(char)el);
else {
char[4] buf;
auto len = encode(buf, el);
put(cast(ArrayType)buf[0 .. len]);
}
}
}

static if( is(ElemType == wchar) ){
void put(dchar el)
{
if( el < 128 ) put(cast(wchar)el);
else {
wchar[3] buf;
auto len = std.utf.encode(buf, el);
put(cast(ArrayType)buf[0 .. len]);
}
}
}

void put(ArrayType arr)
{
m_data[m_fill .. m_fill+arr.length] = arr[];
m_fill += arr.length;
}

@property ArrayType data() { return cast(ArrayType)m_data[0 .. m_fill]; }

static if (!is(E == immutable)) {
void reset() { m_fill = 0; }
}
}

unittest {
FixedAppender!(string, 16) app;
app.put("foo");
app.put('b');
app.put("ar");
assert(app.data == "foobar");
}

0 comments on commit 9044a78

Please sign in to comment.