1 /// @safe @nogc arrays
2 module mecca.containers.arrays;
3 
4 // Licensed under the Boost license. Full copyright information in the AUTHORS file
5 
6 import std.traits;
7 import mecca.lib.reflection: CapacityType;
8 import mecca.lib.exception;
9 
10 /**
11  * A variable size array with a fixed maximal capacity
12  */
13 struct FixedArray(T, size_t N, bool InitializeMembers = true) {
14     /// Alias for querying the array's element type
15     alias ElementType = T;
16 
17 private:
18     alias L = CapacityType!N;
19     static if (InitializeMembers) {
20         T[N] data;
21     } else {
22         T[N] data = void;
23     }
24     L _length;
25 
26 public:
27     /// Return the maximal capacity of the array
28     ///
29     /// Returns:
30     /// The capacity of the array, using the smallest unsigned type that can hold it.
31     @property L capacity() const nothrow pure @nogc {
32         return N;
33     }
34 
35     /// Return the length of the array.
36     ///
37     /// This returns the same value as `length`, except it uses the narrowest type in which the length is guaranteed to
38     /// fit.
39     @property L len() const nothrow pure @safe @nogc {
40         return _length;
41     }
42 
43     /// Property for getting and setting the length of the array.
44     @property size_t length() const nothrow pure @safe @nogc {
45         return len;
46     }
47 
48     /// ditto
49     @property void length(size_t newLen) {
50         assert (newLen <= N);
51         static if (InitializeMembers) {
52             while (_length > newLen) {
53                 destroy(data[_length-1]);
54                 _length--;
55             }
56         }
57 
58         _length = cast(L)newLen;
59     }
60 
61     /// Returns a standard slice pointing at the array's data
62     @property inout(T)[] array() inout nothrow pure @safe @nogc {
63         return data[0 .. _length];
64     }
65 
66     auto ref opOpAssign(string op: "~", U)(U val) nothrow @safe @nogc if (is(Unqual!U == T) || isAssignable!(T, U)) {
67         ASSERT!"FixedArray is full. Capacity is %s"( _length < capacity, capacity );
68         data[_length] = val;
69         ++_length;
70         return this;
71     }
72 
73     /// FixedArray is implicitly convertible to its underlying array
74     alias array this;
75 
76     static if( isAssignable!(T, const(T)) ) {
77         // If the type supports assignment from const(T) to T
78 
79         /// Set the FixedArray to whatever fits from the beginning of arr2
80         void safeSetPrefix(const(T)[] arr2) {
81             length = arr2.length <= N ? arr2.length : N;
82             data[0 .. _length][] = arr2[0.._length][];
83         }
84         /// Set the FixedArray to whatever fits from the end of arr2
85         void safeSetSuffix(const(T)[] arr2) {
86             length = arr2.length <= N ? arr2.length : N;
87             data[0 .. _length] = arr2[$ - _length .. $];
88         }
89     } else {
90         // Only allow assignement from mutable types
91 
92         /// Set the FixedArray to whatever fits from the beginning of arr2
93         void safeSetPrefix(T[] arr2) {
94             length = arr2.length <= N ? arr2.length : N;
95             data[0 .. _length][] = arr2[0.._length][];
96         }
97         /// Set the FixedArray to whatever fits from the end of arr2
98         void safeSetSuffix(T[] arr2) {
99             length = arr2.length <= N ? arr2.length : N;
100             data[0 .. _length] = arr2[$ - _length .. $];
101         }
102     }
103 }
104 
105 /// A nogc mutable char array (string)
106 alias FixedString(size_t N) = FixedArray!(char, N, false);
107 
108 /// Shorten a null terminated `FixedString` to be the size of the actual string (without the null)
109 ///
110 /// Will throw a RangeError if the string is not null terminated
111 void setStringzLength(size_t N)(ref FixedArray!(char, N, false) str) pure nothrow @safe @nogc {
112     import std..string : indexOf;
113     str.length = str.array.indexOf('\0');
114 }
115 
116 unittest {
117     FixedArray!(uint, 8) fa;
118     assert (fa.length == 0);
119 
120     fa.length = 3;
121     fa[0] = 8;
122     fa[1] = 9;
123     fa[2] = 10;
124     assert (fa.length == 3);
125 }
126 
127 unittest {
128     // Make sure values are initialized
129     FixedArray!(uint, 8) fa;
130 
131     fa.length = 4;
132     assert (fa[2] == 0);
133     fa[3] = 4;
134     assert (fa[3] == 4);
135     fa.length = 1;
136     fa.length = 5;
137     assert (fa[3] == 0);
138 }
139 
140 unittest {
141     // Make sure destructors are called
142     static struct S {
143         static uint count;
144         uint value = 3;
145 
146         ~this() {
147             count++;
148         }
149     }
150 
151     {
152         FixedArray!(S, 5) fa;
153 
154         fa.length = 3;
155 
156         assert (S.count==0);
157 
158         assert(fa[2].value == 3);
159         fa[2].value = 2;
160         fa.length = 5;
161         assert (S.count==0);
162         assert(fa[2].value == 2);
163 
164         fa.length = 1;
165         assert (S.count==4);
166         fa.length = 3;
167         assert (S.count==4);
168         assert(fa[2].value == 3);
169     }
170 }
171 
172 unittest {
173     FixedString!5 str;
174 
175     str.safeSetSuffix("123456789");
176     assert(str[]=="56789");
177     str.safeSetPrefix("123456789");
178     assert(str[]=="12345");
179 }