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 }