Variable-length stack allocated arrays
Andrei Alexandrescu
SeeWebsiteForEmail at erdani.org
Mon Jan 11 22:11:19 PST 2010
Chad J wrote:
> dsimcha wrote:
>> The optional buffer idiom is great for stuff that's returned from a function. I
>> use it all the time. On the other hand, for temporary buffers that are used
>> internally, whose mere existence is an implementation detail, having the caller
>> maintain and pass in a buffer is a horrible violation of encapsulation and good
>> API design, even by the standards of performance-oriented APIs. Even if you know
>> it's never going to change, it's still annoying for the caller to even have to
>> think about these kinds of details.
>>
>
> Yes, quite.
I think an API that is memory-safe relies on one global array like this:
template SuperStack(T) {
private T[] buffer;
private enum slackfactor = 2.5;
T[] getBuffer(size_t n) {
buffer.length = buffer.length + n;
return buffer[$ - n .. $];
}
void releaseBuffer(T[] b) {
enforce(b is buffer[$ - b.length .. $]);
buffer.length = buffer.length - b.length;
if (gc.capacity(buffer) > buffer.length * slackfactor) {
// Reallocate buffer to allow collection of slack
buffer = buffer.dup;
}
}
}
Further encapsulation is possible e.g. by defining a struct that pairs
calls to getBuffer and releaseBuffer.
The idea is that the API offers a means to define and use temporary
buffers without compromising memory safety. Even if you escape data
allocated via getBuffer that persists after releaseBuffer, that will not
cause undefined behavior. (It may, however, cause data to be overwritten
because another call to getBuffer will reuse memory.) Leaks are also
possible if you don't release the buffer. That can be solved by not
offering getBuffer and releaseBuffer as they are, but instead only
encapsulated in a struct with the appropriate constructor and destructor.
Andrei
More information about the Digitalmars-d
mailing list