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  * Set of operations on memory blocks.
7  *
8  * Copyright: Eugene Wissner 2017-2020.
9  * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
10  *                  Mozilla Public License, v. 2.0).
11  * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
12  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/op.d,
13  *                 tanya/memory/op.d)
14  */
15 module tanya.memory.op;
16 
17 import core.stdc..string;
18 
19 private enum alignMask = size_t.sizeof - 1;
20 
21 /**
22  * Copies $(D_PARAM source) into $(D_PARAM target).
23  *
24  * $(D_PARAM source) and $(D_PARAM target) shall not overlap so that
25  * $(D_PARAM source) points ahead of $(D_PARAM target).
26  *
27  * $(D_PARAM target) shall have enough space for $(D_INLINECODE source.length)
28  * elements.
29  *
30  * Params:
31  *  source = Memory to copy from.
32  *  target = Destination memory.
33  *
34  * See_Also: $(D_PSYMBOL copyBackward).
35  *
36  * Precondition: $(D_INLINECODE source.length <= target.length).
37  */
38 void copy(const void[] source, void[] target) @nogc nothrow pure @trusted
39 in
40 {
41     assert(source.length <= target.length);
42     assert(source.length == 0 || source.ptr !is null);
43     assert(target.length == 0 || target.ptr !is null);
44 }
45 do
46 {
47     memcpy(target.ptr, source.ptr, source.length);
48 }
49 
50 ///
51 @nogc nothrow pure @safe unittest
52 {
53     ubyte[9] source = [1, 2, 3, 4, 5, 6, 7, 8, 9];
54     ubyte[9] target;
55     source.copy(target);
56     assert(equal(source, target));
57 }
58 
59 /*
60  * size_t value each of which bytes is set to `Byte`.
61  */
62 private template filledBytes(ubyte Byte, ubyte I = 0)
63 {
64     static if (I == size_t.sizeof)
65     {
66         enum size_t filledBytes = Byte;
67     }
68     else
69     {
70         enum size_t filledBytes = (filledBytes!(Byte, I + 1) << 8) | Byte;
71     }
72 }
73 
74 /**
75  * Fills $(D_PARAM memory) with the single byte $(D_PARAM c).
76  *
77  * Param:
78  *  c      = The value to fill $(D_PARAM memory) with.
79  *  memory = Memory block.
80  */
81 void fill(ubyte c = 0)(void[] memory) @trusted
82 in
83 {
84     assert(memory.length == 0 || memory.ptr !is null);
85 }
86 do
87 {
88     memset(memory.ptr, c, memory.length);
89 }
90 
91 ///
92 @nogc nothrow pure @safe unittest
93 {
94     ubyte[9] memory = [1, 2, 3, 4, 5, 6, 7, 8, 9];
95     memory.fill!0();
96     foreach (ubyte v; memory)
97     {
98         assert(v == 0);
99     }
100 }
101 
102 /**
103  * Copies starting from the end of $(D_PARAM source) into the end of
104  * $(D_PARAM target).
105  *
106  * $(D_PSYMBOL copyBackward) copies the elements in reverse order, but the
107  * order of elements in the $(D_PARAM target) is exactly the same as in the
108  * $(D_PARAM source).
109  *
110  * $(D_PARAM source) and $(D_PARAM target) shall not overlap so that
111  * $(D_PARAM target) points ahead of $(D_PARAM source).
112  *
113  * $(D_PARAM target) shall have enough space for $(D_INLINECODE source.length)
114  * elements.
115  *
116  * Params:
117  *  source = Memory to copy from.
118  *  target = Destination memory.
119  *
120  * See_Also: $(D_PSYMBOL copy).
121  *
122  * Precondition: $(D_INLINECODE source.length <= target.length).
123  */
124 void copyBackward(const void[] source, void[] target) @nogc nothrow pure @trusted
125 in
126 {
127     assert(source.length <= target.length);
128     assert(source.length == 0 || source.ptr !is null);
129     assert(target.length == 0 || target.ptr !is null);
130 }
131 do
132 {
133     memmove(target.ptr, source.ptr, source.length);
134 }
135 
136 ///
137 @nogc nothrow pure @safe unittest
138 {
139     ubyte[6] mem = [ 'a', 'a', 'b', 'b', 'c', 'c' ];
140     ubyte[6] expected = [ 'a', 'a', 'a', 'a', 'b', 'b' ];
141 
142     copyBackward(mem[0 .. 4], mem[2 .. $]);
143     assert(equal(expected, mem));
144 }
145 
146 /**
147  * Finds the first occurrence of $(D_PARAM needle) in $(D_PARAM haystack) if
148  * any.
149  *
150  * Params:
151  *  haystack = Memory block.
152  *  needle   = A byte.
153  *
154  * Returns: The subrange of $(D_PARAM haystack) whose first element is the
155  *          first occurrence of $(D_PARAM needle). If $(D_PARAM needle)
156  *          couldn't be found, an empty `inout void[]` is returned.
157  */
158 inout(void[]) find(return inout void[] haystack, ubyte needle)
159 @nogc nothrow pure @trusted
160 in
161 {
162     assert(haystack.length == 0 || haystack.ptr !is null);
163 }
164 do
165 {
166     auto length = haystack.length;
167     const size_t needleWord = size_t.max * needle;
168     enum size_t highBits = filledBytes!(0x01, 0);
169     enum size_t mask = filledBytes!(0x80, 0);
170 
171     // Align
172     auto bytes = cast(inout(ubyte)*) haystack;
173     while (length > 0 && ((cast(size_t) bytes) & 3) != 0)
174     {
175         if (*bytes == needle)
176         {
177             return bytes[0 .. length];
178         }
179         ++bytes;
180         --length;
181     }
182 
183     // Check if some of the words has the needle
184     auto words = cast(inout(size_t)*) bytes;
185     while (length >= size_t.sizeof)
186     {
187         if ((((*words ^ needleWord) - highBits) & (~*words) & mask) != 0)
188         {
189             break;
190         }
191         ++words;
192         length -= size_t.sizeof;
193     }
194 
195     // Find the exact needle position in the word
196     bytes = cast(inout(ubyte)*) words;
197     while (length > 0)
198     {
199         if (*bytes == needle)
200         {
201             return bytes[0 .. length];
202         }
203         ++bytes;
204         --length;
205     }
206 
207     return haystack[$ .. $];
208 }
209 
210 ///
211 @nogc nothrow pure @safe unittest
212 {
213     const ubyte[9] haystack = ['a', 'b', 'c', 'd', 'e', 'f', 'b', 'g', 'h'];
214 
215     assert(equal(find(haystack, 'a'), haystack[]));
216     assert(equal(find(haystack, 'b'), haystack[1 .. $]));
217     assert(equal(find(haystack, 'c'), haystack[2 .. $]));
218     assert(equal(find(haystack, 'd'), haystack[3 .. $]));
219     assert(equal(find(haystack, 'e'), haystack[4 .. $]));
220     assert(equal(find(haystack, 'f'), haystack[5 .. $]));
221     assert(equal(find(haystack, 'h'), haystack[8 .. $]));
222     assert(find(haystack, 'i').length == 0);
223 
224     assert(find(null, 'a').length == 0);
225 }
226 
227 /**
228  * Looks for `\0` in the $(D_PARAM haystack) and returns the part of the
229  * $(D_PARAM haystack) ahead of it.
230  *
231  * Returns $(D_KEYWORD null) if $(D_PARAM haystack) doesn't contain a null
232  * character.
233  *
234  * Params:
235  *  haystack = Memory block.
236  *
237  * Returns: The subrange that spans all bytes before the null character or
238  *          $(D_KEYWORD null) if the $(D_PARAM haystack) doesn't contain any.
239  */
240 inout(char[]) findNullTerminated(return inout char[] haystack)
241 @nogc nothrow pure @trusted
242 in
243 {
244     assert(haystack.length == 0 || haystack.ptr !is null);
245 }
246 do
247 {
248     auto length = haystack.length;
249     enum size_t highBits = filledBytes!(0x01, 0);
250     enum size_t mask = filledBytes!(0x80, 0);
251 
252     // Align
253     auto bytes = cast(inout(ubyte)*) haystack;
254     while (length > 0 && ((cast(size_t) bytes) & 3) != 0)
255     {
256         if (*bytes == '\0')
257         {
258             return haystack[0 .. haystack.length - length];
259         }
260         ++bytes;
261         --length;
262     }
263 
264     // Check if some of the words contains 0
265     auto words = cast(inout(size_t)*) bytes;
266     while (length >= size_t.sizeof)
267     {
268         if (((*words - highBits) & (~*words) & mask) != 0)
269         {
270             break;
271         }
272         ++words;
273         length -= size_t.sizeof;
274     }
275 
276     // Find the exact 0 position in the word
277     bytes = cast(inout(ubyte)*) words;
278     while (length > 0)
279     {
280         if (*bytes == '\0')
281         {
282             return haystack[0 .. haystack.length - length];
283         }
284         ++bytes;
285         --length;
286     }
287 
288     return null;
289 }
290 
291 ///
292 @nogc nothrow pure @safe unittest
293 {
294     assert(equal(findNullTerminated("abcdef\0gh"), "abcdef"));
295     assert(equal(findNullTerminated("\0garbage"), ""));
296     assert(equal(findNullTerminated("\0"), ""));
297     assert(equal(findNullTerminated("cstring\0"), "cstring"));
298     assert(findNullTerminated(null) is null);
299     assert(findNullTerminated("abcdef") is null);
300 }
301 
302 /**
303  * Compares two memory areas $(D_PARAM r1) and $(D_PARAM r2) for equality.
304  *
305  * Params:
306  *  r1 = First memory block.
307  *  r2 = Second memory block.
308  *
309  * Returns: $(D_KEYWORD true) if $(D_PARAM r1) and $(D_PARAM r2) are equal,
310  *          $(D_KEYWORD false) otherwise.
311  */
312 bool equal(const void[] r1, const void[] r2) @nogc nothrow pure @trusted
313 in
314 {
315     assert(r1.length == 0 || r1.ptr !is null);
316     assert(r2.length == 0 || r2.ptr !is null);
317 }
318 do
319 {
320     return r1.length == r2.length && memcmp(r1.ptr, r2.ptr, r1.length) == 0;
321 }
322 
323 ///
324 @nogc nothrow pure @safe unittest
325 {
326     assert(equal("asdf", "asdf"));
327     assert(!equal("asd", "asdf"));
328     assert(!equal("asdf", "asd"));
329     assert(!equal("asdf", "qwer"));
330 }