1 module mir.appender; 2 3 // import std.traits: isAssignable, hasElaborateDestructorhasElaborateCopyConstructor, hasElaborateAssign; 4 5 package void _mir_destroy(T)(T[] ar) 6 { 7 static if (__traits(hasMember, T, "__xdtor")) 8 foreach (ref e; ar) 9 static if (__traits(isSame, T, __traits(parent, e.__xdtor))) 10 { 11 pragma(inline, false) 12 e.__xdtor(); 13 } 14 } 15 16 private extern(C) @system nothrow @nogc pure void* memcpy(scope void* s1, scope const void* s2, size_t n); 17 18 19 /// 20 struct ScopedBuffer(T, size_t bytes = 4096) 21 if (bytes) 22 { 23 import std.traits: isIterable, hasElaborateAssign, isAssignable, isArray; 24 import mir.primitives: hasLength; 25 import mir.conv: emplaceRef; 26 27 private enum size_t _bufferLength = bytes / T.sizeof + (bytes % T.sizeof != 0); 28 private T[] _buffer; 29 private size_t _currentLength; 30 private align(T.alignof) ubyte[_bufferLength * T.sizeof] _scopeBufferPayload = void; 31 32 private ref T[_bufferLength] _scopeBuffer() @trusted scope 33 { 34 return *cast(T[_bufferLength]*)&_scopeBufferPayload; 35 } 36 37 private T[] prepare(size_t n) @trusted scope 38 { 39 import mir.internal.memory: realloc, malloc; 40 _currentLength += n; 41 if (_buffer.length == 0) 42 { 43 if (_currentLength <= _bufferLength) 44 { 45 return _scopeBuffer[0 .. _currentLength]; 46 } 47 else 48 { 49 auto newLen = _currentLength << 1; 50 _buffer = (cast(T*)malloc(T.sizeof * newLen))[0 .. newLen]; 51 memcpy(_buffer.ptr, _scopeBuffer.ptr, T.sizeof * (_currentLength - n)); 52 } 53 } 54 else 55 if (_currentLength > _buffer.length) 56 { 57 auto newLen = _currentLength << 1; 58 _buffer = (cast(T*)realloc(_buffer.ptr, T.sizeof * newLen))[0 .. newLen]; 59 } 60 return _buffer[0 .. _currentLength]; 61 } 62 63 static if (isAssignable!(T, const T)) 64 private alias R = const T; 65 else 66 private alias R = T; 67 68 @disable this(this); 69 70 ~this() 71 { 72 import mir.internal.memory: free; 73 data._mir_destroy; 74 (() @trusted => free(_buffer.ptr))(); 75 } 76 77 void popBackN(size_t n) 78 { 79 sizediff_t t = _currentLength - n; 80 if (t < 0) 81 assert(0, "ScopedBffer.popBackN: n is too large."); 82 import mir.exception; 83 data[t .. _currentLength]._mir_destroy; 84 _currentLength = t; 85 } 86 87 void put(R e) @safe scope 88 { 89 auto cl = _currentLength; 90 prepare(1); 91 emplaceRef(data[cl], e); 92 } 93 94 static if (T.sizeof > 8 || hasElaborateAssign!T) 95 void put(ref R e) scope 96 { 97 auto cl = _currentLength; 98 auto d = prepare(1); 99 emplaceRef(d[cl], e); 100 } 101 102 static if (!hasElaborateAssign!T && isAssignable!(T, const T)) 103 void put(scope const(T)[] e) scope 104 { 105 auto cl = _currentLength; 106 auto d = prepare(e.length); 107 auto len = e.length * T.sizeof; 108 if (!__ctfe) 109 (()@trusted=>memcpy(d.ptr + cl, e.ptr, len))(); 110 else 111 (()@trusted { (d.ptr + cl)[0 .. len] = e[0 .. len]; })(); 112 } 113 114 static if (!hasElaborateAssign!T && !isAssignable!(T, const T)) 115 void put()(scope T[] e) scope 116 { 117 auto cl = _currentLength; 118 auto d = prepare(e.length); 119 auto len = e.length * T.sizeof; 120 if (!__ctfe) 121 (()@trusted=>memcpy(d.ptr + cl, e.ptr, len))(); 122 else 123 (()@trusted { (d.ptr + cl)[0 .. len] = e[0 .. len]; })(); 124 } 125 126 void put(Iterable)(Iterable range) scope 127 if (isIterable!Iterable && !isArray!Iterable) 128 { 129 static if (hasLength!Iterable) 130 { 131 auto cl = _currentLength; 132 auto d = prepare(range.length); 133 static if (is(Iterable : R[]) && !hasElaborateAssign!T) 134 { 135 auto len = range.length * T.sizeof; 136 if (!__ctfe) 137 (()@trusted=>memcpy(d.ptr + cl, e.ptr, len))(); 138 else 139 (()@trusted { (d.ptr + cl)[0 .. len] = e[0 .. len]; })(); 140 } 141 else 142 { 143 foreach(ref e; range) 144 emplaceRef(d[cl++], e); 145 assert(_currentLength == cl); 146 } 147 } 148 else 149 { 150 foreach(ref e; range) 151 put(e); 152 } 153 } 154 155 void reset() scope nothrow 156 { 157 this.__dtor; 158 _currentLength = 0; 159 _buffer = null; 160 } 161 162 T[] data() @property @safe scope 163 { 164 return _buffer.length ? _buffer[0 .. _currentLength] : _scopeBuffer[0 .. _currentLength]; 165 } 166 } 167 168 /// 169 @safe pure nothrow @nogc 170 unittest 171 { 172 ScopedBuffer!char buf; 173 buf.put('c'); 174 buf.put("str"); 175 assert(buf.data == "cstr"); 176 177 buf.popBackN(2); 178 assert(buf.data == "cs"); 179 } 180 181 @safe pure nothrow @nogc 182 unittest 183 { 184 ScopedBuffer!(char, 3) buf; 185 buf.put('c'); 186 buf.put("str"); 187 assert(buf.data == "cstr"); 188 189 buf.popBackN(2); 190 assert(buf.data == "cs"); 191 }