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 }