1 /* 2 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 3 Authors: Adam Búš 4 */ 5 module btl..string.storage; 6 7 import std.traits : Select, isUnsigned; 8 9 import btl.internal.traits; 10 11 public template Storage(T, size_t N, L, bool allowHeap) 12 if(isUnsigned!L){ 13 14 struct Storage{ 15 union{ 16 static if(N > 0){ 17 private Inline _inline; 18 private Heap _heap; 19 } 20 else{ 21 private Heap _heap; 22 private Inline _inline; 23 } 24 } 25 26 public alias Inline = InlineImpl; 27 28 public alias Heap = HeapImpl; 29 30 public enum Length minimalCapacity = Inline.capacity; 31 32 public enum Length maximalCapacity = allowHeap 33 ? (L.max / T.sizeof) 34 : Inline.capacity; 35 36 //public enum Length heapFlag = _heapFlag; 37 38 pragma(inline, true){ 39 public static Length heapCapacity(Length new_capacity)pure nothrow @safe @nogc{ 40 new_capacity |= heapFlag; 41 assert(new_capacity > minimalCapacity); 42 assert(new_capacity <= maximalCapacity); 43 44 return new_capacity; 45 } 46 47 public void reset(Length len = 0)pure nothrow @trusted @nogc{ 48 assert(len <= minimalCapacity); 49 50 static if(N){ 51 this._inline.reset(); 52 this._inline.length = len; 53 } 54 else static if(allowHeap){ 55 assert(len == 0); 56 this._heap.capacity = 0; 57 this._heap.length = 0; 58 this._heap.ptr = null; 59 } 60 } 61 62 public @property ref inout(Inline) inline()inout pure nothrow @trusted @nogc{ 63 assert(!this.external); 64 return this._inline; 65 } 66 67 public @property ref inout(Heap) heap()inout pure nothrow @trusted @nogc{ 68 assert(this.external); 69 return this._heap; 70 } 71 public @property void heap(Heap h)pure nothrow @trusted @nogc{ 72 static if(allowHeap){ 73 assert(h.length <= h.capacity); 74 this._heap.capacity = h.capacity; 75 assert(this.external); 76 this._heap.length = h.length; 77 this._heap.ptr = h.ptr; 78 } 79 else{ 80 assert(0, "no impl"); 81 } 82 } 83 84 public @property Length capacity()const pure nothrow @trusted @nogc{ 85 return this.external 86 ? this.heap.capacity 87 : this.inline.capacity; 88 } 89 90 public @property Length length()const pure nothrow @trusted @nogc{ 91 return this.external 92 ? this.heap.length 93 : this.inline.length; 94 } 95 96 public @property void length(Length n)pure nothrow @trusted @nogc{ 97 if(this.external){ 98 assert(n <= this.heap.capacity); 99 100 static if(allowHeap) 101 this.heap.length = cast(L)n; 102 else 103 assert(0, "no impl"); 104 } 105 else{ 106 assert(n <= this.inline.capacity); 107 this.inline.length = cast(InlineLength)n; 108 } 109 } 110 111 public @property inout(T)* ptr()inout pure nothrow @trusted @nogc{ 112 return this.external 113 ? this.heap.ptr 114 : this.inline.ptr; 115 } 116 117 public @property inout(T)[] chars()inout pure nothrow @trusted @nogc{ 118 return this.external 119 ? this.heap.chars 120 : this.inline.chars; 121 } 122 123 public @property inout(T)[] allocatedChars()inout pure nothrow @trusted @nogc{ 124 return this.external 125 ? this.heap.allocatedChars 126 : this.inline.allocatedChars; 127 } 128 129 public @property bool external()const pure nothrow @safe @nogc{ 130 static if(allowHeap && N) 131 return (this._inline.header.length & heapFlag); 132 else 133 return allowHeap; 134 } 135 136 public @property bool small()const pure nothrow @trusted @nogc{ 137 static if(allowHeap){ 138 static if(N == 0) 139 return (this._heap.capacity == 0); 140 else 141 return !this.external; 142 } 143 else 144 return true; 145 } 146 } 147 } 148 149 enum L heapFlag = allowHeap 150 ? 0x1 //heap capacity must be odd 151 : 0; 152 153 enum size_t maxInlineLength(U) = (U.max & (cast(U)~heapFlag)); 154 155 template InlineLengthImpl(){ 156 static if(maxInlineLength!ubyte >= N) 157 alias InlineLengthImpl = ubyte; 158 else static if(maxInlineLength!ushort >= N) 159 alias InlineLengthImpl = ushort; 160 else static if(maxInlineLength!uint >= N) 161 alias InlineLengthImpl = uint; 162 else static if(maxInlineLength!ulong >= N) 163 alias InlineLengthImpl = ulong; 164 else 165 static assert(0, "no impl"); 166 } 167 168 alias InlineLength = InlineLengthImpl!(); 169 170 alias Length = Select!(L.sizeof > InlineLength.sizeof, L, InlineLength); 171 172 alias HeapImpl = .HeapStorage!(T, L, allowHeap); 173 174 align(max(T.sizeof, InlineLength.alignof)) 175 struct InlineHeader{ 176 private InlineLength length = 0; 177 178 private enum Length maxLength = maxInlineLength!InlineLength; 179 180 static assert(maxLength >= N); 181 } 182 183 enum inlineAlign = max(HeapImpl.alignof, InlineHeader.alignof); 184 185 size_t compute_inline_capacity(){ 186 if(N == 0) 187 return 0; 188 else{ 189 const size_t base_capacity = (HeapImpl.sizeof > InlineHeader.sizeof) 190 ? ((HeapImpl.sizeof - InlineHeader.sizeof) / T.sizeof) 191 : 0; 192 193 const size_t additional_base_capacity = (base_capacity >= N) 194 ? 0 195 : (N - base_capacity); 196 197 const size_t additional_base_size = additional_base_capacity * T.sizeof; 198 199 const size_t additional_align_size = (inlineAlign - (additional_base_size % inlineAlign)) % 8; 200 201 const size_t additional_capacity = (additional_base_size + additional_align_size) / T.sizeof; 202 203 const size_t final_capacity = base_capacity + additional_capacity; 204 205 const size_t max_inline_length = InlineHeader.maxLength; 206 207 return (max_inline_length < final_capacity) 208 ? max_inline_length 209 : final_capacity; 210 } 211 } 212 213 align(inlineAlign) 214 struct InlineImpl{ 215 private InlineHeader header; 216 217 public enum Length capacity = compute_inline_capacity(); 218 private T[capacity] elements; 219 220 pragma(inline, true){ 221 public void reset()pure nothrow @safe @nogc{ 222 this.header.length = 0; 223 } 224 225 public @property Length length()const pure nothrow @safe @nogc{ 226 static if(allowHeap){ 227 static if(InlineHeader.sizeof >= uint.sizeof) 228 return cast(Length)(header.length >> 1); 229 else 230 return cast(Length)((cast(size_t)this.header.length) >> 1); 231 } 232 else 233 return this.header.length; 234 235 } 236 237 public @property void length(Length n)pure nothrow @safe @nogc{ 238 assert(n <= capacity); 239 240 static if(allowHeap) 241 this.header.length = cast(InlineLength)(n << 1); 242 else 243 this.header.length = cast(InlineLength)n; 244 } 245 246 public @property inout(void)[] data()inout pure nothrow @trusted @nogc{ 247 return (cast(inout(void)*)this.elements.ptr)[0 .. this.capacity * T.sizeof]; 248 } 249 250 public @property inout(T)* ptr()inout pure nothrow @trusted @nogc{ 251 return this.elements.ptr; 252 } 253 254 public @property inout(T)[] chars()inout pure nothrow @trusted @nogc{ 255 return this.ptr[0 .. length]; 256 } 257 258 public @property inout(T)[] allocatedChars()inout pure nothrow @trusted @nogc{ 259 return this.ptr[0 .. capacity]; 260 } 261 } 262 } 263 264 265 } 266 267 struct HeapStorage(T, L, bool allowHeap){ 268 269 static if(allowHeap){ 270 public L capacity; 271 public L length; 272 public T* ptr; 273 } 274 else{ 275 public @property L capacity()const pure nothrow @safe @nogc{return 0;} 276 public @property void capacity(size_t)pure nothrow @safe @nogc{assert(0, "no impl");} 277 278 public @property L length()const pure nothrow @safe @nogc{return 0;} 279 public @property void length(size_t)pure nothrow @safe @nogc{assert(0, "no impl");} 280 281 public @property inout(T)* ptr()inout pure nothrow @safe @nogc{return null;} 282 public @property void ptr(T*)pure nothrow @safe @nogc{assert(0, "no impl");} 283 } 284 285 pragma(inline, true){ 286 287 public @property inout(void)[] data()inout pure nothrow @trusted @nogc{ 288 return (cast(inout(void)*)ptr)[0 .. capacity * T.sizeof]; 289 } 290 291 public @property inout(T)[] chars()inout pure nothrow @trusted @nogc{ 292 return ptr[0 .. length]; 293 } 294 295 public @property inout(T)[] allocatedChars()inout pure nothrow @trusted @nogc{ 296 return this.ptr[0 .. capacity]; 297 } 298 } 299 }