Wolfgang Thallergcc 15.1.0ada185d1 year agoHistory
515 lines · 13.1 KB · D
RawDownload
// Written in the D programming language. /**Serialize data to `ubyte` arrays.  * Copyright: Copyright The D Language Foundation 2000 - 2015. * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors:   $(HTTP digitalmars.com, Walter Bright) * Source:    $(PHOBOSSRC std/outbuffer.d) * * $(SCRIPT inhibitQuickIndex = 1;) */module std.outbuffer; import core.stdc.stdarg;import std.traits : isSomeString; /********************************************* * OutBuffer provides a way to build up an array of bytes out * of raw data. It is useful for things like preparing an * array of bytes to write out to a file. * OutBuffer's byte order is the format native to the computer. * To control the byte order (endianness), use a class derived * from OutBuffer. * * OutBuffer's internal buffer is allocated with the GC. Pointers * stored into the buffer are scanned by the GC, but you have to * ensure proper alignment, e.g. by using `alignSize((void*).sizeof)`. */ class OutBuffer{    ubyte[] data;    size_t offset;     invariant()    {        assert(offset <= data.length);    }   pure nothrow @safe  {    /*********************************     * Convert to array of bytes.     */    inout(ubyte)[] toBytes() scope inout { return data[0 .. offset]; }     /***********************************     * Preallocate nbytes more to the size of the internal buffer.     *     * This is a     * speed optimization, a good guess at the maximum size of the resulting     * buffer will improve performance by eliminating reallocations and copying.     */    void reserve(size_t nbytes) @trusted        in        {            assert(offset + nbytes >= offset);        }        out        {            assert(offset + nbytes <= data.length);        }        do        {            if (data.length < offset + nbytes)            {                void[] vdata = data;                vdata.length = (offset + nbytes + 7) * 2; // allocates as void[] to not set BlkAttr.NO_SCAN                data = cast(ubyte[]) vdata;            }        }     /**********************************     * put enables OutBuffer to be used as an OutputRange.     */    alias put = write;     /*************************************     * Append data to the internal buffer.     */     void write(scope const(ubyte)[] bytes)        {            reserve(bytes.length);            data[offset .. offset + bytes.length] = bytes[];            offset += bytes.length;        }     void write(scope const(wchar)[] chars) @trusted        {        write(cast(ubyte[]) chars);        }     void write(scope const(dchar)[] chars) @trusted        {        write(cast(ubyte[]) chars);        }     void write(ubyte b)         /// ditto        {            reserve(ubyte.sizeof);            this.data[offset] = b;            offset += ubyte.sizeof;        }     void write(byte b) { write(cast(ubyte) b); }         /// ditto    void write(char c) { write(cast(ubyte) c); }         /// ditto    void write(dchar c) { write(cast(uint) c); }         /// ditto     void write(ushort w) @trusted                /// ditto    {        reserve(ushort.sizeof);        *cast(ushort *)&data[offset] = w;        offset += ushort.sizeof;    }     void write(short s) { write(cast(ushort) s); }               /// ditto     void write(wchar c) @trusted        /// ditto    {        reserve(wchar.sizeof);        *cast(wchar *)&data[offset] = c;        offset += wchar.sizeof;    }     void write(uint w) @trusted         /// ditto    {        reserve(uint.sizeof);        *cast(uint *)&data[offset] = w;        offset += uint.sizeof;    }     void write(int i) { write(cast(uint) i); }           /// ditto     void write(ulong l) @trusted         /// ditto    {        reserve(ulong.sizeof);        *cast(ulong *)&data[offset] = l;        offset += ulong.sizeof;    }     void write(long l) { write(cast(ulong) l); }         /// ditto     void write(float f) @trusted         /// ditto    {        reserve(float.sizeof);        *cast(float *)&data[offset] = f;        offset += float.sizeof;    }     void write(double f) @trusted               /// ditto    {        reserve(double.sizeof);        *cast(double *)&data[offset] = f;        offset += double.sizeof;    }     void write(real f) @trusted         /// ditto    {        reserve(real.sizeof);        *cast(real *)&data[offset] = f;        offset += real.sizeof;    }     void write(scope const(char)[] s) @trusted             /// ditto    {        write(cast(ubyte[]) s);    }     void write(scope const OutBuffer buf)           /// ditto    {        write(buf.toBytes());    }     /****************************************     * Append nbytes of val to the internal buffer.     * Params:     *   nbytes = Number of bytes to fill.     *   val = Value to fill, defaults to 0.     */     void fill(size_t nbytes, ubyte val = 0)    {        reserve(nbytes);        data[offset .. offset + nbytes] = val;        offset += nbytes;    }     /****************************************     * Append nbytes of 0 to the internal buffer.     * Param:     *   nbytes - number of bytes to fill.     */    void fill0(size_t nbytes)    {        fill(nbytes);    }     /**********************************     * Append bytes until the buffer aligns on a power of 2 boundary.     *     * By default fills with 0 bytes.     *     * Params:     *   alignsize = Alignment value. Must be power of 2.     *   val = Value to fill, defaults to 0.     */     void alignSize(size_t alignsize, ubyte val = 0)    in    {        assert(alignsize && (alignsize & (alignsize - 1)) == 0);    }    out    {        assert((offset & (alignsize - 1)) == 0);    }    do    {        auto nbytes = offset & (alignsize - 1);        if (nbytes)            fill(alignsize - nbytes, val);    }    ///    @safe unittest    {        OutBuffer buf = new OutBuffer();        buf.write(cast(ubyte) 1);        buf.align2();        assert(buf.toBytes() == "\x01\x00");        buf.write(cast(ubyte) 2);        buf.align4();        assert(buf.toBytes() == "\x01\x00\x02\x00");        buf.write(cast(ubyte) 3);        buf.alignSize(8);        assert(buf.toBytes() == "\x01\x00\x02\x00\x03\x00\x00\x00");    }    /// ditto    @safe unittest    {        OutBuffer buf = new OutBuffer();        buf.write(cast(ubyte) 1);        buf.align2(0x55);        assert(buf.toBytes() == "\x01\x55");        buf.write(cast(ubyte) 2);        buf.align4(0x55);        assert(buf.toBytes() == "\x01\x55\x02\x55");        buf.write(cast(ubyte) 3);        buf.alignSize(8, 0x55);        assert(buf.toBytes() == "\x01\x55\x02\x55\x03\x55\x55\x55");    }     /// Clear the data in the buffer    void clear()    {        offset = 0;    }     /****************************************     * Optimize common special case alignSize(2)     * Params:     *   val = Value to fill, defaults to 0.     */     void align2(ubyte val = 0)    {        if (offset & 1)            write(cast(byte) val);    }     /****************************************     * Optimize common special case alignSize(4)     * Params:     *   val = Value to fill, defaults to 0.     */     void align4(ubyte val = 0)    {        if (offset & 3)        {   auto nbytes = (4 - offset) & 3;            fill(nbytes, val);        }    }     /**************************************     * Convert internal buffer to array of chars.     */     override string toString() const    {        //printf("OutBuffer.toString()\n");        return cast(string) data[0 .. offset].idup;    }  }     /*****************************************     * Append output of C's vprintf() to internal buffer.     */     void vprintf(scope string format, va_list args) @system nothrow    {        import core.stdc.stdio : vsnprintf;        import core.stdc.stdlib : alloca;        import std.string : toStringz;         version (StdUnittest)            char[3] buffer = void;      // trigger reallocation        else            char[128] buffer = void;        int count;         // Can't use `tempCString()` here as it will result in compilation error:        // "cannot mix core.std.stdlib.alloca() and exception handling".        auto f = toStringz(format);        auto p = buffer.ptr;        auto psize = buffer.length;        for (;;)        {            va_list args2;            va_copy(args2, args);            count = vsnprintf(p, psize, f, args2);            va_end(args2);            if (count == -1)            {                if (psize > psize.max / 2) assert(0); // overflow check                psize *= 2;            }            else if (count >= psize)            {                if (count == count.max) assert(0); // overflow check                psize = count + 1;            }            else                break;             p = cast(char *) alloca(psize); // buffer too small, try again with larger size        }        write(cast(ubyte[]) p[0 .. count]);    }     /*****************************************     * Append output of C's printf() to internal buffer.     */     void printf(scope string format, ...) @system    {        va_list ap;        va_start(ap, format);        vprintf(format, ap);        va_end(ap);    }     /**     * Formats and writes its arguments in text format to the OutBuffer.     *     * Params:     *  fmt = format string as described in $(REF formattedWrite, std,format)     *  args = arguments to be formatted     *     * See_Also:     *  $(REF _writef, std,stdio);     *  $(REF formattedWrite, std,format);     */    void writef(Char, A...)(scope const(Char)[] fmt, A args)    {        import std.format.write : formattedWrite;        formattedWrite(this, fmt, args);    }     ///    @safe unittest    {        OutBuffer b = new OutBuffer();        b.writef("a%sb", 16);        assert(b.toString() == "a16b");    }     /// ditto    void writef(alias fmt, A...)(A args)    if (isSomeString!(typeof(fmt)))    {        import std.format : checkFormatException;         alias e = checkFormatException!(fmt, A);        static assert(!e, e);        return this.writef(fmt, args);    }     ///    @safe unittest    {        OutBuffer b = new OutBuffer();        b.writef!"a%sb"(16);        assert(b.toString() == "a16b");    }     /**     * Formats and writes its arguments in text format to the OutBuffer,     * followed by a newline.     *     * Params:     *  fmt = format string as described in $(REF formattedWrite, std,format)     *  args = arguments to be formatted     *     * See_Also:     *  $(REF _writefln, std,stdio);     *  $(REF formattedWrite, std,format);     */    void writefln(Char, A...)(scope const(Char)[] fmt, A args)    {        import std.format.write : formattedWrite;        formattedWrite(this, fmt, args);        put('\n');    }     ///    @safe unittest    {        OutBuffer b = new OutBuffer();        b.writefln("a%sb", 16);        assert(b.toString() == "a16b\n");    }     /// ditto    void writefln(alias fmt, A...)(A args)    if (isSomeString!(typeof(fmt)))    {        import std.format : checkFormatException;         alias e = checkFormatException!(fmt, A);        static assert(!e, e);        return this.writefln(fmt, args);    }     ///    @safe unittest    {        OutBuffer b = new OutBuffer();        b.writefln!"a%sb"(16);        assert(b.toString() == "a16b\n");    }     /*****************************************     * At offset index into buffer, create nbytes of space by shifting upwards     * all data past index.     */     void spread(size_t index, size_t nbytes) pure nothrow @safe        in        {            assert(index <= offset);        }        do        {            reserve(nbytes);             // This is an overlapping copy - should use memmove()            for (size_t i = offset; i > index; )            {                --i;                data[i + nbytes] = data[i];            }            offset += nbytes;        }} ///@safe unittest{    import std.string : cmp;     OutBuffer buf = new OutBuffer();     assert(buf.offset == 0);    buf.write("hello");    buf.write(cast(byte) 0x20);    buf.write("world");    buf.writef(" %d", 62665);    assert(cmp(buf.toString(), "hello world 62665") == 0);     buf.clear();    assert(cmp(buf.toString(), "") == 0);    buf.write("New data");    assert(cmp(buf.toString(),"New data") == 0);} @safe unittest{    import std.range;    static assert(isOutputRange!(OutBuffer, char));     import std.algorithm;  {    OutBuffer buf = new OutBuffer();    "hello".copy(buf);    assert(buf.toBytes() == "hello");  }  {    OutBuffer buf = new OutBuffer();

Lines 1–500 of 515.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t