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

Reply via email to