1 module nxt.sso_appender;
2 
3 /** Small-Size-Optimized (SSO) `Appender`.
4  *
5  * See_Also: https://forum.dlang.org/post/ifspcvfkwsnvyrdfngpw@forum.dlang.org
6  */
7 struct SSOAppender(T, size_t smallCapacity)
8 if (smallCapacity >= 1)
9 {
10     import std.array : Appender;
11     import nxt.container.static_array : StaticArray;
12 	static if (!__traits(isPOD, T))
13 		import core.lifetime : move;
14 
15     void assureOneMoreCapacity() @trusted
16     {
17         if (!_isLarge &&
18             _small.full)
19         {
20             import std.algorithm.mutation : moveEmplaceAll;
21 
22             T[smallCapacity] tmp = void;
23             moveEmplaceAll(_small[], tmp[0 .. _small.length]);
24 
25             import core.lifetime : emplace;
26             emplace!Large(&_large);
27 
28             _large.put(tmp[]);
29             _isLarge = 1;
30         }
31     }
32 
33     void put(T x) @trusted
34     {
35         assureOneMoreCapacity();
36         if (_isLarge)
37         {
38             static if (__traits(isPOD, T))
39                 _large.put(x);
40             else
41                 _large.put(x.move);
42         }
43         else
44         {
45             static if (__traits(isPOD, T))
46                 _small.put(x);
47             else
48                 _small.put(x.move);
49         }
50     }
51 
52     inout(T)[] data() inout return scope @trusted
53     {
54         if (_isLarge)
55             return _large.data[];
56         else
57             return _small[];
58     }
59 
60 private:
61     alias Small = StaticArray!(T, smallCapacity);
62     alias Large = Appender!(T[]);
63     union
64     {
65         Small _small;
66         Large _large;
67     }
68     bool _isLarge;              // TODO: pack this into _small
69 }
70 
71 @safe pure nothrow unittest
72 {
73     alias A = SSOAppender!(int, 2);
74     A a;
75     a.put(11);
76     a.put(12);
77     assert(a.data[] == [11, 12]);
78     a.put(13);
79     assert(a.data[] == [11, 12, 13]);
80     static if (hasPreviewDIP1000)
81     {
82         auto f() @safe pure {
83             A a;
84             return a.data;   // errors with -dip1000
85         }
86         static assert(!__traits(compiles, {
87                     auto f() @safe pure {
88                         auto x = SmallAppender!(char)("alphas");
89                         auto y = x[];
90                         return y;   // errors with -dip1000
91                     }
92                 }));
93     }
94 }
95 
96 version(unittest)
97 {
98     import nxt.dip_traits : hasPreviewDIP1000;
99 }