1 module dparse.stack_buffer;
2 
3 import core.memory : GC;
4 
5 import std.traits;
6 
7 //version = debug_stack_allocator;
8 
9 struct StackBuffer
10 {
11     bool put(T)(T t)
12     {
13         import std.experimental.allocator.mallocator : Mallocator;
14 
15         static if (is(T == class) || isPointer!T)
16             if (t is null)
17                 return false;
18 
19         if (_length == 0)
20             arr = stackSpace[];
21 
22         static if (is(T == class))
23             static assert(T.sizeof == size_t.sizeof);
24 
25         static if (hasIndirections!T)
26             while (_length % size_t.sizeof != 0)
27                 put(ubyte(1));
28 
29         if (arr.ptr != stackSpace.ptr)
30         {
31             if (_length + T.sizeof > arr.length)
32             {
33                 size_t newLength = arr.length << 1;
34                 while (_length + T.sizeof > newLength)
35                     newLength <<= 1;
36                 auto oldPtr = arr.ptr;
37                 Mallocator.instance.reallocate(arr, newLength);
38                 GC.removeRange(oldPtr);
39                 GC.addRange(arr.ptr, arr.length);
40                 version (debug_stack_allocator)
41                     (cast(ubyte[]) arr)[_length .. $] = 0;
42             }
43         }
44         else if (_length + T.sizeof > stackSpace.length)
45         {
46             size_t newLength = stackSpace.length << 1;
47             while (_length + T.sizeof > newLength)
48                 newLength <<= 1;
49             arr = Mallocator.instance.allocate(newLength);
50             GC.addRange(arr.ptr, arr.length);
51             version (debug_stack_allocator)
52                 (cast(ubyte[]) arr)[] = 0;
53             arr[0 .. stackSpace.length] = stackSpace[];
54         }
55         arr[_length .. _length + T.sizeof] = (cast(void*) &t)[0 .. T.sizeof];
56         _length += T.sizeof;
57         return true;
58     }
59 
60     ~this()
61     {
62         import std.experimental.allocator.mallocator:Mallocator;
63 
64         version (debug_stack_allocator)
65             (cast(ubyte[]) arr)[] = 0;
66         if (arr.ptr !is stackSpace.ptr)
67         {
68             GC.removeRange(arr.ptr);
69             Mallocator.instance.deallocate(arr);
70         }
71     }
72 
73     void[] opSlice()
74     {
75         return arr[0 .. _length];
76     }
77 
78     @disable this(this);
79 
80     uint length() const pure nothrow @nogc @safe @property
81     {
82         return _length;
83     }
84 
85     alias opDollar = length;
86 
87 private:
88 
89     void[8 * 16] stackSpace;
90     void[] arr;
91     uint _length;
92 }
93 
94 unittest
95 {
96     StackBuffer sb;
97     ubyte[80] u;
98     sb.put(u);
99     assert(sb.length == 80);
100     static struct S
101     {
102         void[100] u;
103     }
104 
105     S s;
106     sb.put(s);
107 
108     class B
109     {
110         int b;
111     }
112 
113     class D : B
114     {
115         double d;
116     }
117 
118     B b = new B;
119     sb.put(b);
120 
121     B d = new D;
122     sb.put(d);
123 }