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 pure nothrow @safe unittest {
72 	alias A = SSOAppender!(int, 2);
73 	A a;
74 	a.put(11);
75 	a.put(12);
76 	assert(a.data[] == [11, 12]);
77 	a.put(13);
78 	assert(a.data[] == [11, 12, 13]);
79 	static if (hasPreviewDIP1000)
80 	{
81 		auto f() @safe pure {
82 			A a;
83 			return a.data;   // errors with -dip1000
84 		}
85 		static assert(!__traits(compiles, {
86 					auto f() @safe pure {
87 						auto x = SmallAppender!(char)("alphas");
88 						auto y = x[];
89 						return y;   // errors with -dip1000
90 					}
91 				}));
92 	}
93 }
94 
95 version (unittest)
96 {
97 	import nxt.dip_traits : hasPreviewDIP1000;
98 }