1 /** 2 * Optimized copying appender, no chaining 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.appender; 15 16 import core.memory; 17 import std.traits; 18 19 struct FastAppender(I) 20 { 21 static assert(T.sizeof == 1, "TODO"); 22 23 private: 24 enum PAGE_SIZE = 4096; 25 enum MIN_SIZE = PAGE_SIZE / 2 + 1; // smallest size that can expand 26 27 alias Unqual!I T; 28 29 T* cursor, start, end; 30 31 void reserve(size_t len) 32 { 33 auto size = cursor-start; 34 auto newSize = size + len; 35 auto capacity = end-start; 36 37 if (start) 38 { 39 auto extended = GC.extend(start, newSize, newSize * 2); 40 if (extended) 41 { 42 end = start + extended; 43 return; 44 } 45 } 46 47 auto newCapacity = newSize < MIN_SIZE ? MIN_SIZE : newSize * 2; 48 49 auto bi = GC.qalloc(newCapacity * T.sizeof, (typeid(T[]).next.flags & 1) ? 0 : GC.BlkAttr.NO_SCAN); 50 auto newStart = cast(T*)bi.base; 51 newCapacity = bi.size; 52 53 newStart[0..size] = start[0..size]; 54 start = newStart; 55 cursor = start + size; 56 end = start + newCapacity; 57 } 58 59 public: 60 /// Preallocate 61 this(size_t capacity) 62 { 63 reserve(capacity); 64 } 65 66 /// Start with a given buffer 67 this(I[] arr) 68 { 69 start = cursor = cast(T*)arr.ptr; 70 end = start + arr.length; 71 } 72 73 void put(U...)(U items) 74 if (CanPutAll!U) 75 { 76 // TODO: check for static if length is 1 77 auto cursorEnd = cursor; 78 foreach (item; items) 79 static if (is(typeof(cursor[0] = item))) 80 cursorEnd++; 81 else 82 // TODO: is this too lax? it allows passing static arrays by value 83 static if (is(typeof(cursor[0..1] = item[0..1]))) 84 cursorEnd += item.length; 85 else 86 static assert(0, "Can't put " ~ typeof(item).stringof); 87 88 if (cursorEnd > end) 89 { 90 auto len = cursorEnd - cursor; 91 reserve(len); 92 cursorEnd = cursor + len; 93 } 94 auto cursor = this.cursor; 95 this.cursor = cursorEnd; 96 97 static if (items.length == 1) 98 { 99 alias items[0] item; 100 static if (is(typeof(cursor[0] = item))) 101 cursor[0] = item; 102 else 103 cursor[0..item.length] = item[]; 104 } 105 else 106 { 107 foreach (item; items) 108 static if (is(typeof(cursor[0] = item))) 109 *cursor++ = item; 110 else 111 static if (is(typeof(cursor[0..1] = item[0..1]))) 112 { 113 cursor[0..item.length] = item[]; 114 cursor += item.length; 115 } 116 } 117 } 118 119 /// Unsafe. Use together with preallocate(). 120 void uncheckedPut(U...)(U items) 121 if (CanPutAll!U) 122 { 123 auto cursor = this.cursor; 124 125 foreach (item; items) 126 static if (is(typeof(cursor[0] = item))) 127 *cursor++ = item; 128 else 129 static if (is(typeof(cursor[0..1] = item[0..1]))) 130 { 131 cursor[0..item.length] = item; 132 cursor += item.length; 133 } 134 135 this.cursor = cursor; 136 } 137 138 void preallocate(size_t len) 139 { 140 if (end - cursor < len) 141 reserve(len); 142 } 143 144 T[] allocate(size_t len) 145 { 146 auto cursorEnd = cursor + len; 147 if (cursorEnd > end) 148 { 149 reserve(len); 150 cursorEnd = cursor + len; 151 } 152 auto result = cursor[0..len]; 153 cursor = cursorEnd; 154 return result; 155 } 156 157 template CanPutAll(U...) 158 { 159 static if (U.length==0) 160 enum bool CanPutAll = true; 161 else 162 { 163 enum bool CanPutAll = 164 ( 165 is(typeof(cursor[0 ] = U[0].init )) || 166 is(typeof(cursor[0..1] = U[0].init[0..1])) 167 ) && CanPutAll!(U[1..$]); 168 } 169 } 170 171 void opOpAssign(string op, U)(U item) 172 if (op=="~" && is(typeof(put!U))) 173 { 174 put(item); 175 } 176 177 I[] get() 178 { 179 return cast(I[])start[0..cursor-start]; 180 } 181 182 @property size_t length() 183 { 184 return cursor-start; 185 } 186 187 static if (is(I == T)) // mutable types only 188 { 189 /// Does not resize. Use preallocate for that. 190 @property void length(size_t value) 191 { 192 cursor = start + value; 193 assert(cursor <= end); 194 } 195 196 /// Effectively empties the data, but preserves the storage for reuse. 197 /// Same as setting length to 0. 198 void clear() 199 { 200 cursor = start; 201 } 202 } 203 }