1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /**
6  * $(D_PSYMBOL tanya.range.array) implements range primitives for built-in arrays.
7  *
8  * This module is a submodule of
9  * $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/package.d, tanya.range).
10  *
11  * After importing of
12  * $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/array.d, tanya/range/array.d)
13  * built-in arrays can act as bidirectional ranges. For that to work the module
14  * defines a set of functions that accept a built-in array of any type as their
15  * first argument, so thanks to UFCS (Uniform Function Call Syntax) they can be
16  * called as if they were array member functions. For example the arrays the
17  * `.length`-property, but no `.empty` property. So here can be find the
18  * $(D_PSYMBOL empty) function. Since $(D_INLINECODE empty(array)) and
19  * $(D_INLINECODE array.empty) are equal for the arrays, arrays get a faked
20  * property `empty`.
21  *
22  * The functions in this module don't change array elements or its underlying
23  * storage, but some functions alter the slice. Each array maintains a pointer
24  * to its data and the length and there can be several pointers which point to
25  * the same data. Array pointer can be advanced and the length can be reduced
26  * without changing the underlying storage. So slices offer the possibility to
27  * have multiple views into the same array, point to different positions inside
28  * it.
29  *
30  * Strings ($(D_INLINECODE char[]), (D_INLINECODE wchar[]) and
31  * (D_INLINECODE dchar[])) are treated as any other normal array, they aren't
32  * auto-decoded.
33  *
34  * Copyright: Eugene Wissner 2017-2020.
35  * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
36  *                  Mozilla Public License, v. 2.0).
37  * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
38  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/array.d,
39  *                 tanya/range/array.d)
40  */
41 module tanya.range.array;
42 
43 /**
44  * Returns the first element of the $(D_PARAM array).
45  *
46  * The element is returned by reference, so $(D_PSYMBOL front) can be also used
47  * to change the first element of $(D_PARAM array) if it is mutable.
48  *
49  * Params:
50  *  T     = Element type of $(D_PARAM array).
51  *  array = Built-in array.
52  *
53  * Returns: First element.
54  *
55  * Precondition: $(D_INLINECODE array.length > 0).
56  */
57 @property ref inout(T) front(T)(return scope inout(T)[] array)
58 in
59 {
60     assert(array.length > 0);
61 }
62 do
63 {
64     return array[0];
65 }
66 
67 ///
68 @nogc nothrow pure @safe unittest
69 {
70     string s = "Wenn die Wunde nicht mehr wehtut, schmerzt die Narbe";
71     static assert(is(typeof(s.front) == immutable char));
72     assert(s.front == 'W');
73 
74     wstring w = "Волны несутся, гремя и сверкая";
75     static assert(is(typeof(w.front) == immutable wchar));
76     assert(w.front == 'В');
77 
78     dstring d = "Для писателя память - это почти все";
79     static assert(is(typeof(d.front) == immutable dchar));
80     assert(d.front == 'Д');
81 }
82 
83 /**
84  * Returns the last element of the $(D_PARAM array).
85  *
86  * The element is returned by reference, so $(D_PSYMBOL back) can be also used
87  * to change the last element of $(D_PARAM array) if it is mutable.
88  *
89  * Params:
90  *  T     = Element type of $(D_PARAM array).
91  *  array = Built-in array.
92  *
93  * Returns: Last element.
94  *
95  * Precondition: $(D_INLINECODE array.length > 0).
96  */
97 @property ref inout(T) back(T)(return scope inout(T)[] array)
98 in
99 {
100     assert(array.length > 0);
101 }
102 do
103 {
104     return array[$ - 1];
105 }
106 
107 ///
108 @nogc nothrow pure @safe unittest
109 {
110     string s = "Brecht";
111     static assert(is(typeof(s.back) == immutable char));
112     assert(s.back == 't');
113 
114     wstring w = "Тютчев";
115     static assert(is(typeof(w.back) == immutable wchar));
116     assert(w.back == 'в');
117 
118     dstring d = "Паустовский";
119     static assert(is(typeof(d.back) == immutable dchar));
120     assert(d.back == 'й');
121 }
122 
123 /**
124  * $(D_PSYMBOL popFront) and $(D_PSYMBOL popBack) advance the $(D_PARAM array)
125  * and remove one element from its back, respectively.
126  *
127  * $(D_PSYMBOL popFront) and $(D_PSYMBOL popBack) don't alter the array
128  * storage, they only narrow the view into the array.
129  *
130  * Params:
131  *  T     = Element type of $(D_PARAM array).
132  *  array = Built-in array.
133  *
134  * Precondition: $(D_INLINECODE array.length > 0).
135  */
136 void popFront(T)(ref inout(T)[] array)
137 in
138 {
139     assert(array.length > 0);
140 }
141 do
142 {
143     array = array[1 .. $];
144 }
145 
146 /// ditto
147 void popBack(T)(ref inout(T)[] array)
148 in
149 {
150     assert(array.length > 0);
151 }
152 do
153 {
154     array = array[0 .. $ - 1];
155 }
156 
157 ///
158 @nogc nothrow pure @safe unittest
159 {
160     wstring array = "Der finstere Ozean der Metaphysik. Nietzsche";
161 
162     array.popFront();
163     assert(array.length == 43);
164     assert(array.front == 'e');
165 
166     array.popBack();
167     assert(array.length == 42);
168     assert(array.back == 'h');
169 }
170 
171 /**
172  * Tests whether $(D_PARAM array) is empty.
173  *
174  * Params:
175  *  T     = Element type of $(D_PARAM array).
176  *  array = Built-in array.
177  *
178  * Returns: $(D_KEYWORD true) if $(D_PARAM array) has no elements,
179  *          $(D_KEYWORD false) otherwise.
180  */
181 @property bool empty(T)(scope const T[] array)
182 {
183     return array.length == 0;
184 }
185 
186 ///
187 @nogc nothrow pure @safe unittest
188 {
189     int[1] array;
190     assert(!array.empty);
191     assert(array[1 .. 1].empty);
192 }
193 
194 /**
195  * Returns a copy of the slice $(D_PARAM array).
196  *
197  * $(D_PSYMBOL save) doesn't copy the array itself, but only the data pointer
198  * and the length.
199  *
200  * Params:
201  *  T     = Element type of $(D_PARAM array).
202  *  array = Built-in array.
203  *
204  * Returns: A copy of the slice $(D_PARAM array).
205  */
206 @property inout(T)[] save(T)(return scope inout(T)[] array)
207 {
208     return array;
209 }
210 
211 ///
212 @nogc nothrow pure @safe unittest
213 {
214     ubyte[8] array;
215     auto slice = array.save;
216 
217     assert(slice.length == array.length);
218     slice.popFront();
219     assert(slice.length < array.length);
220 }