1
/**
2
 * An expandable buffer in which you can write text or binary data.
3
 *
4
 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
5
 * Authors:   Walter Bright, http://www.digitalmars.com
6
 * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7
 * Source:    $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/outbuffer.d, root/_outbuffer.d)
8
 * Documentation: https://dlang.org/phobos/dmd_root_outbuffer.html
9
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/outbuffer.d
10
 */
11

12
module dmd.root.outbuffer;
13

14
import core.stdc.stdarg;
15
import core.stdc.stdio;
16
import core.stdc.string;
17
import dmd.root.rmem;
18
import dmd.root.rootobject;
19
import dmd.root.string;
20

21
debug
22
{
23
    debug = stomp; // flush out dangling pointer problems by stomping on unused memory
24
}
25

26
struct OutBuffer
27
{
28
    private ubyte[] data;
29
    private size_t offset;
30
    private bool notlinehead;
31

32
    /// Whether to indent
33
    bool doindent;
34
    /// Whether to indent by 4 spaces or by tabs;
35
    bool spaces;
36
    /// Current indent level
37
    int level;
38

39
    extern (C++) ~this() pure nothrow
40
    {
41 1
        debug (stomp) memset(data.ptr, 0xFF, data.length);
42 1
        mem.xfree(data.ptr);
43
    }
44

45 1
    extern (C++) size_t length() const pure @nogc @safe nothrow { return offset; }
46

47
    /**********************
48
     * Transfer ownership of the allocated data to the caller.
49
     * Returns:
50
     *  pointer to the allocated data
51
     */
52
    extern (C++) char* extractData() pure nothrow @nogc @trusted
53
    {
54 1
        char* p = cast(char*)data.ptr;
55 1
        data = null;
56 1
        offset = 0;
57 1
        return p;
58
    }
59

60
    extern (C++) void destroy() pure nothrow @trusted
61
    {
62 1
        debug (stomp) memset(data.ptr, 0xFF, data.length);
63 1
        mem.xfree(extractData());
64
    }
65

66
    extern (C++) void reserve(size_t nbytes) pure nothrow
67
    {
68
        //debug (stomp) printf("OutBuffer::reserve: size = %lld, offset = %lld, nbytes = %lld\n", data.length, offset, nbytes);
69 1
        if (data.length - offset < nbytes)
70
        {
71
            /* Increase by factor of 1.5; round up to 16 bytes.
72
             * The odd formulation is so it will map onto single x86 LEA instruction.
73
             */
74 1
            const size = (((offset + nbytes) * 3 + 30) / 2) & ~15;
75

76
            debug (stomp)
77
            {
78 1
                auto p = cast(ubyte*)mem.xmalloc(size);
79 1
                memcpy(p, data.ptr, offset);
80 1
                memset(data.ptr, 0xFF, data.length);  // stomp old location
81 1
                mem.xfree(data.ptr);
82 1
                memset(p + offset, 0xff, size - offset); // stomp unused data
83
            }
84
            else
85
            {
86
                auto p = cast(ubyte*)mem.xrealloc(data.ptr, size);
87
                if (mem.isGCEnabled) // clear currently unused data to avoid false pointers
88
                    memset(p + offset + nbytes, 0xff, size - offset - nbytes);
89
            }
90 1
            data = p[0 .. size];
91
        }
92
    }
93

94
    /************************
95
     * Shrink the size of the data to `size`.
96
     * Params:
97
     *  size = new size of data, must be <= `.length`
98
     */
99
    extern (C++) void setsize(size_t size) pure nothrow @nogc @safe
100
    {
101 1
        assert(size <= offset);
102 1
        offset = size;
103
    }
104

105
    extern (C++) void reset() pure nothrow @nogc @safe
106
    {
107 1
        offset = 0;
108
    }
109

110
    private void indent() pure nothrow
111
    {
112 1
        if (level)
113
        {
114 1
            const indentLevel = spaces ? level * 4 : level;
115 1
            reserve(indentLevel);
116 1
            data[offset .. offset + indentLevel] = (spaces ? ' ' : '\t');
117 1
            offset += indentLevel;
118
        }
119 1
        notlinehead = true;
120
    }
121

122
    extern (C++) void write(const(void)* data, size_t nbytes) pure nothrow
123
    {
124 0
        write(data[0 .. nbytes]);
125
    }
126

127
    void write(const(void)[] buf) pure nothrow
128
    {
129 1
        if (doindent && !notlinehead)
130 1
            indent();
131 1
        reserve(buf.length);
132 1
        memcpy(this.data.ptr + offset, buf.ptr, buf.length);
133 1
        offset += buf.length;
134
    }
135

136
    extern (C++) void writestring(const(char)* string) pure nothrow
137
    {
138 1
        write(string.toDString);
139
    }
140

141
    void writestring(const(char)[] s) pure nothrow
142
    {
143 1
        write(s);
144
    }
145

146
    void writestring(string s) pure nothrow
147
    {
148 1
        write(s);
149
    }
150

151
    void writestringln(const(char)[] s)
152
    {
153 1
        writestring(s);
154 1
        writenl();
155
    }
156

157
    extern (C++) void prependstring(const(char)* string) pure nothrow
158
    {
159 1
        size_t len = strlen(string);
160 1
        reserve(len);
161 1
        memmove(data.ptr + len, data.ptr, offset);
162 1
        memcpy(data.ptr, string, len);
163 1
        offset += len;
164
    }
165

166
    /// write newline
167
    extern (C++) void writenl() pure nothrow
168
    {
169
        version (Windows)
170
        {
171
            writeword(0x0A0D); // newline is CR,LF on Microsoft OS's
172
        }
173
        else
174
        {
175 1
            writeByte('\n');
176
        }
177 1
        if (doindent)
178 1
            notlinehead = false;
179
    }
180

181
    extern (C++) void writeByte(uint b) pure nothrow
182
    {
183 1
        if (doindent && !notlinehead && b != '\n')
184 1
            indent();
185 1
        reserve(1);
186 1
        this.data[offset] = cast(ubyte)b;
187 1
        offset++;
188
    }
189

190
    extern (C++) void writeUTF8(uint b) pure nothrow
191
    {
192 1
        reserve(6);
193 1
        if (b <= 0x7F)
194
        {
195 1
            this.data[offset] = cast(ubyte)b;
196 1
            offset++;
197
        }
198 1
        else if (b <= 0x7FF)
199
        {
200 1
            this.data[offset + 0] = cast(ubyte)((b >> 6) | 0xC0);
201 1
            this.data[offset + 1] = cast(ubyte)((b & 0x3F) | 0x80);
202 1
            offset += 2;
203
        }
204 1
        else if (b <= 0xFFFF)
205
        {
206 1
            this.data[offset + 0] = cast(ubyte)((b >> 12) | 0xE0);
207 1
            this.data[offset + 1] = cast(ubyte)(((b >> 6) & 0x3F) | 0x80);
208 1
            this.data[offset + 2] = cast(ubyte)((b & 0x3F) | 0x80);
209 1
            offset += 3;
210
        }
211 1
        else if (b <= 0x1FFFFF)
212
        {
213 1
            this.data[offset + 0] = cast(ubyte)((b >> 18) | 0xF0);
214 1
            this.data[offset + 1] = cast(ubyte)(((b >> 12) & 0x3F) | 0x80);
215 1
            this.data[offset + 2] = cast(ubyte)(((b >> 6) & 0x3F) | 0x80);
216 1
            this.data[offset + 3] = cast(ubyte)((b & 0x3F) | 0x80);
217 1
            offset += 4;
218
        }
219
        else
220 0
            assert(0);
221
    }
222

223
    extern (C++) void prependbyte(uint b) pure nothrow
224
    {
225 1
        reserve(1);
226 1
        memmove(data.ptr + 1, data.ptr, offset);
227 1
        data[0] = cast(ubyte)b;
228 1
        offset++;
229
    }
230

231
    extern (C++) void writewchar(uint w) pure nothrow
232
    {
233
        version (Windows)
234
        {
235
            writeword(w);
236
        }
237
        else
238
        {
239 0
            write4(w);
240
        }
241
    }
242

243
    extern (C++) void writeword(uint w) pure nothrow
244
    {
245
        version (Windows)
246
        {
247
            uint newline = 0x0A0D;
248
        }
249
        else
250
        {
251 1
            uint newline = '\n';
252
        }
253 1
        if (doindent && !notlinehead && w != newline)
254 0
            indent();
255

256 1
        reserve(2);
257 1
        *cast(ushort*)(this.data.ptr + offset) = cast(ushort)w;
258 1
        offset += 2;
259
    }
260

261
    extern (C++) void writeUTF16(uint w) pure nothrow
262
    {
263 1
        reserve(4);
264 1
        if (w <= 0xFFFF)
265
        {
266 1
            *cast(ushort*)(this.data.ptr + offset) = cast(ushort)w;
267 1
            offset += 2;
268
        }
269 1
        else if (w <= 0x10FFFF)
270
        {
271 1
            *cast(ushort*)(this.data.ptr + offset) = cast(ushort)((w >> 10) + 0xD7C0);
272 1
            *cast(ushort*)(this.data.ptr + offset + 2) = cast(ushort)((w & 0x3FF) | 0xDC00);
273 1
            offset += 4;
274
        }
275
        else
276 0
            assert(0);
277
    }
278

279
    extern (C++) void write4(uint w) pure nothrow
280
    {
281
        version (Windows)
282
        {
283
            bool notnewline = w != 0x000A000D;
284
        }
285
        else
286
        {
287 1
            bool notnewline = true;
288
        }
289 1
        if (doindent && !notlinehead && notnewline)
290 0
            indent();
291 1
        reserve(4);
292 1
        *cast(uint*)(this.data.ptr + offset) = w;
293 1
        offset += 4;
294
    }
295

296
    extern (C++) void write(const OutBuffer* buf) pure nothrow
297
    {
298 1
        if (buf)
299
        {
300 1
            reserve(buf.offset);
301 1
            memcpy(data.ptr + offset, buf.data.ptr, buf.offset);
302 1
            offset += buf.offset;
303
        }
304
    }
305

306
    extern (C++) void write(RootObject obj) /*nothrow*/
307
    {
308 1
        if (obj)
309
        {
310 1
            writestring(obj.toChars());
311
        }
312
    }
313

314
    extern (C++) void fill0(size_t nbytes) pure nothrow
315
    {
316 0
        reserve(nbytes);
317 0
        memset(data.ptr + offset, 0, nbytes);
318 0
        offset += nbytes;
319
    }
320

321
    /**
322
     * Allocate space, but leave it uninitialized.
323
     * Params:
324
     *  nbytes = amount to allocate
325
     * Returns:
326
     *  slice of the allocated space to be filled in
327
     */
328
    extern (D) char[] allocate(size_t nbytes) pure nothrow
329
    {
330 1
        reserve(nbytes);
331 1
        offset += nbytes;
332 1
        return cast(char[])data[offset - nbytes .. offset];
333
    }
334

335
    extern (C++) void vprintf(const(char)* format, va_list args) nothrow
336
    {
337 1
        int count;
338 1
        if (doindent && !notlinehead)
339 1
            indent();
340 1
        uint psize = 128;
341
        for (;;)
342
        {
343 1
            reserve(psize);
344 1
            va_list va;
345 1
            va_copy(va, args);
346
            /*
347
                The functions vprintf(), vfprintf(), vsprintf(), vsnprintf()
348
                are equivalent to the functions printf(), fprintf(), sprintf(),
349
                snprintf(), respectively, except that they are called with a
350
                va_list instead of a variable number of arguments. These
351
                functions do not call the va_end macro. Consequently, the value
352
                of ap is undefined after the call. The application should call
353
                va_end(ap) itself afterwards.
354
                */
355 1
            count = vsnprintf(cast(char*)data.ptr + offset, psize, format, va);
356 1
            va_end(va);
357 1
            if (count == -1) // snn.lib and older libcmt.lib return -1 if buffer too small
358 0
                psize *= 2;
359 1
            else if (count >= psize)
360 1
                psize = count + 1;
361
            else
362 1
                break;
363
        }
364 1
        offset += count;
365 1
        if (mem.isGCEnabled)
366 1
            memset(data.ptr + offset, 0xff, psize - count);
367
    }
368

369
    static if (__VERSION__ < 2092)
370
    {
371
        extern (C++) void printf(const(char)* format, ...) nothrow
372
        {
373
            va_list ap;
374
            va_start(ap, format);
375
            vprintf(format, ap);
376
            va_end(ap);
377
        }
378
    }
379
    else
380
    {
381
        pragma(printf) extern (C++) void printf(const(char)* format, ...) nothrow
382
        {
383 1
            va_list ap;
384 1
            va_start(ap, format);
385 1
            vprintf(format, ap);
386 1
            va_end(ap);
387
        }
388
    }
389

390
    /**************************************
391
     * Convert `u` to a string and append it to the buffer.
392
     * Params:
393
     *  u = integral value to append
394
     */
395
    extern (C++) void print(ulong u) pure nothrow
396
    {
397
        //import core.internal.string;  // not available
398 1
        UnsignedStringBuf buf = void;
399 1
        writestring(unsignedToTempString(u, buf));
400
    }
401

402
    extern (C++) void bracket(char left, char right) pure nothrow
403
    {
404 1
        reserve(2);
405 1
        memmove(data.ptr + 1, data.ptr, offset);
406 1
        data[0] = left;
407 1
        data[offset + 1] = right;
408 1
        offset += 2;
409
    }
410

411
    /******************
412
     * Insert left at i, and right at j.
413
     * Return index just past right.
414
     */
415
    extern (C++) size_t bracket(size_t i, const(char)* left, size_t j, const(char)* right) pure nothrow
416
    {
417 1
        size_t leftlen = strlen(left);
418 1
        size_t rightlen = strlen(right);
419 1
        reserve(leftlen + rightlen);
420 1
        insert(i, left, leftlen);
421 1
        insert(j + leftlen, right, rightlen);
422 1
        return j + leftlen + rightlen;
423
    }
424

425
    extern (C++) void spread(size_t offset, size_t nbytes) pure nothrow
426
    {
427 1
        reserve(nbytes);
428 1
        memmove(data.ptr + offset + nbytes, data.ptr + offset, this.offset - offset);
429 1
        this.offset += nbytes;
430
    }
431

432
    /****************************************
433
     * Returns: offset + nbytes
434
     */
435
    extern (C++) size_t insert(size_t offset, const(void)* p, size_t nbytes) pure nothrow
436
    {
437 1
        spread(offset, nbytes);
438 1
        memmove(data.ptr + offset, p, nbytes);
439 1
        return offset + nbytes;
440
    }
441

442
    size_t insert(size_t offset, const(char)[] s) pure nothrow
443
    {
444 1
        return insert(offset, s.ptr, s.length);
445
    }
446

447
    extern (C++) void remove(size_t offset, size_t nbytes) pure nothrow @nogc
448
    {
449 1
        memmove(data.ptr + offset, data.ptr + offset + nbytes, this.offset - (offset + nbytes));
450 1
        this.offset -= nbytes;
451
    }
452

453
    /**
454
     * Returns:
455
     *   a non-owning const slice of the buffer contents
456
     */
457
    extern (D) const(char)[] opSlice() const pure nothrow @nogc
458
    {
459 1
        return cast(const(char)[])data[0 .. offset];
460
    }
461

462
    extern (D) const(char)[] opSlice(size_t lwr, size_t upr) const pure nothrow @nogc
463
    {
464 1
        return cast(const(char)[])data[lwr .. upr];
465
    }
466

467
    extern (D) char opIndex(size_t i) const pure nothrow @nogc
468
    {
469 1
        return cast(char)data[i];
470
    }
471

472
    /***********************************
473
     * Extract the data as a slice and take ownership of it.
474
     *
475
     * When `true` is passed as an argument, this function behaves
476
     * like `dmd.utils.toDString(thisbuffer.extractChars())`.
477
     *
478
     * Params:
479
     *   nullTerminate = When `true`, the data will be `null` terminated.
480
     *                   This is useful to call C functions or store
481
     *                   the result in `Strings`. Defaults to `false`.
482
     */
483
    extern (D) char[] extractSlice(bool nullTerminate = false) pure nothrow
484
    {
485 1
        const length = offset;
486 1
        if (!nullTerminate)
487 1
            return extractData()[0 .. length];
488
        // There's already a terminating `'\0'`
489 1
        if (length && data[length - 1] == '\0')
490 0
            return extractData()[0 .. length - 1];
491 1
        writeByte(0);
492 1
        return extractData()[0 .. length];
493
    }
494

495
    // Append terminating null if necessary and get view of internal buffer
496
    extern (C++) char* peekChars() pure nothrow
497
    {
498 1
        if (!offset || data[offset - 1] != '\0')
499
        {
500 1
            writeByte(0);
501 1
            offset--; // allow appending more
502
        }
503 1
        return cast(char*)data.ptr;
504
    }
505

506
    // Append terminating null if necessary and take ownership of data
507
    extern (C++) char* extractChars() pure nothrow
508
    {
509 1
        if (!offset || data[offset - 1] != '\0')
510 1
            writeByte(0);
511 1
        return extractData();
512
    }
513
}
514

515
/****** copied from core.internal.string *************/
516

517
private:
518

519
alias UnsignedStringBuf = char[20];
520

521
char[] unsignedToTempString(ulong value, char[] buf, uint radix = 10) @safe pure nothrow @nogc
522
{
523 1
    size_t i = buf.length;
524
    do
525
    {
526 1
        if (value < radix)
527
        {
528 1
            ubyte x = cast(ubyte)value;
529 1
            buf[--i] = cast(char)((x < 10) ? x + '0' : x - 10 + 'a');
530 1
            break;
531
        }
532
        else
533
        {
534 1
            ubyte x = cast(ubyte)(value % radix);
535 1
            value = value / radix;
536 1
            buf[--i] = cast(char)((x < 10) ? x + '0' : x - 10 + 'a');
537
        }
538 1
    } while (value);
539 1
    return buf[i .. $];
540
}
541

542
/************* unit tests **************************************************/
543

544
unittest
545
{
546 1
    OutBuffer buf;
547 1
    buf.printf("betty");
548 1
    buf.insert(1, "xx".ptr, 2);
549 1
    buf.insert(3, "yy");
550 1
    buf.remove(4, 1);
551 1
    buf.bracket('(', ')');
552 1
    const char[] s = buf[];
553 1
    assert(s == "(bxxyetty)");
554 1
    buf.destroy();
555
}
556

557
unittest
558
{
559 1
    OutBuffer buf;
560 1
    buf.writestring("abc".ptr);
561 1
    buf.prependstring("def");
562 1
    buf.prependbyte('x');
563 1
    OutBuffer buf2;
564 1
    buf2.writestring("mmm");
565 1
    buf.write(&buf2);
566 1
    char[] s = buf.extractSlice();
567 1
    assert(s == "xdefabcmmm");
568
}
569

570
unittest
571
{
572 1
    OutBuffer buf;
573 1
    buf.writeByte('a');
574 1
    char[] s = buf.extractSlice();
575 1
    assert(s == "a");
576

577 1
    buf.writeByte('b');
578 1
    char[] t = buf.extractSlice();
579 1
    assert(t == "b");
580
}
581

582
unittest
583
{
584 1
    OutBuffer buf;
585 1
    char* p = buf.peekChars();
586 1
    assert(*p == 0);
587

588 1
    buf.writeByte('s');
589 1
    char* q = buf.peekChars();
590 1
    assert(strcmp(q, "s") == 0);
591
}
592

593
unittest
594
{
595 1
    char[10] buf;
596 1
    char[] s = unsignedToTempString(278, buf[], 10);
597 1
    assert(s == "278");
598

599 1
    s = unsignedToTempString(1, buf[], 10);
600 1
    assert(s == "1");
601

602 1
    s = unsignedToTempString(8, buf[], 2);
603 1
    assert(s == "1000");
604

605 1
    s = unsignedToTempString(29, buf[], 16);
606 1
    assert(s == "1d");
607
}

Read our documentation on viewing source code .

Loading