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