On Tuesday, 29 August 2017 at 09:59:30 UTC, Jacob Carlborg wrote:
[...]
But if I keep the range internal, can't I just do the
allocation inside the range and only use "formattedWrite"?
Instead of using both formattedWrite and sformat and go through
the data twice. Then of course the final size is not known
before allocating.
Certainly, that's what dynamic arrays (aka vectors, e.g.
std::vector in C++ STL) are for:
---
import core.exception;
import std.stdio;
import std.experimental.allocator;
import std.algorithm;
struct PoorMansVector(T)
{
private:
T[] store;
size_t length;
IAllocator alloc;
public:
@disable this(this);
this(IAllocator alloc)
{
this.alloc = alloc;
}
~this()
{
if (store)
{
alloc.dispose(store);
store = null;
}
}
void put(T t)
{
if (!store)
{
// Allocate only once for "small" vectors
store = alloc.makeArray!T(8);
if (!store) onOutOfMemoryError();
}
else if (length == store.length)
{
// Growth factor of 1.5
auto expanded = alloc.expandArray!char(store, store.length /
2);
if (!expanded) onOutOfMemoryError();
}
assert (length < store.length);
moveEmplace(t, store[length++]);
}
char[] release()
{
auto elements = store[0..length];
store = null;
return elements;
}
}
char[] sanitize(string value, IAllocator alloc)
{
import std.format : formattedWrite, sformat;
auto r = PoorMansVector!char(alloc);
(&r).formattedWrite!"'%s'"(value); // do not copy the range
return r.release();
}
void main()
{
auto s = sanitize("foo", theAllocator);
scope (exit) theAllocator.dispose(s);
writeln(s);
}
---
Do be aware that the above vector is named "poor man's vector"
for a reason, that's a hasty write down from memory and is sure
to contain bugs.
For better vector implementations you can use at collection
libraries such as EMSI containers; my own attempt at a DbI vector
container can be found here [1]
[1]
https://github.com/Calrama/libds/blob/6a1fc347e1f742b8f67513e25a9fdbf79f007417/src/ds/vector.d