memory.op: Add findNullTerminated

This commit is contained in:
Eugen Wissner 2018-09-02 08:27:26 +02:00
parent 131675d0a8
commit 1f615301e5

View File

@ -305,7 +305,7 @@ do
* first occurrence of $(D_PARAM needle). If $(D_PARAM needle) * first occurrence of $(D_PARAM needle). If $(D_PARAM needle)
* couldn't be found, an empty `inout void[]` is returned. * couldn't be found, an empty `inout void[]` is returned.
*/ */
inout(void[]) find(return inout void[] haystack, const ubyte needle) inout(void[]) find(return inout void[] haystack, ubyte needle)
@nogc nothrow pure @trusted @nogc nothrow pure @trusted
in in
{ {
@ -326,19 +326,19 @@ do
{ {
return bytes[0 .. length]; return bytes[0 .. length];
} }
bytes++; ++bytes;
length--; --length;
} }
// Check if some of the words has the needle // Check if some of the words has the needle
auto words = cast(inout(size_t)*) bytes; auto words = cast(inout(size_t)*) bytes;
while (length >= size_t.sizeof) while (length >= size_t.sizeof)
{ {
if (((*words ^ needleWord) - highBits) & (~*words) & mask) if ((((*words ^ needleWord) - highBits) & (~*words) & mask) != 0)
{ {
break; break;
} }
words++; ++words;
length -= size_t.sizeof; length -= size_t.sizeof;
} }
@ -350,8 +350,8 @@ do
{ {
return bytes[0 .. length]; return bytes[0 .. length];
} }
bytes++; ++bytes;
length--; --length;
} }
return haystack[$ .. $]; return haystack[$ .. $];
@ -362,14 +362,89 @@ do
{ {
const ubyte[9] haystack = ['a', 'b', 'c', 'd', 'e', 'f', 'b', 'g', 'h']; const ubyte[9] haystack = ['a', 'b', 'c', 'd', 'e', 'f', 'b', 'g', 'h'];
assert(find(haystack, 'a') == haystack[]); assert(cmp(find(haystack, 'a'), haystack[]) == 0);
assert(find(haystack, 'b') == haystack[1 .. $]); assert(cmp(find(haystack, 'b'), haystack[1 .. $]) == 0);
assert(find(haystack, 'c') == haystack[2 .. $]); assert(cmp(find(haystack, 'c'), haystack[2 .. $]) == 0);
assert(find(haystack, 'd') == haystack[3 .. $]); assert(cmp(find(haystack, 'd'), haystack[3 .. $]) == 0);
assert(find(haystack, 'e') == haystack[4 .. $]); assert(cmp(find(haystack, 'e'), haystack[4 .. $]) == 0);
assert(find(haystack, 'f') == haystack[5 .. $]); assert(cmp(find(haystack, 'f'), haystack[5 .. $]) == 0);
assert(find(haystack, 'h') == haystack[8 .. $]); assert(cmp(find(haystack, 'h'), haystack[8 .. $]) == 0);
assert(find(haystack, 'i').length == 0); assert(find(haystack, 'i').length == 0);
assert(find(null, 'a').length == 0); assert(find(null, 'a').length == 0);
} }
/**
* Looks for `\0` in the $(D_PARAM haystack) and returns the part of the
* $(D_PARAM haystack) ahead of it.
*
* Returns $(D_KEYWORD null) if $(D_PARAM haystack) doesn't contain a null
* character.
*
* Params:
* haystack = Memory block.
*
* Returns: The subrange that spans all bytes before the null character or
* $(D_KEYWORD null) if the $(D_PARAM haystack) doesn't contain any.
*/
inout(char[]) findNullTerminated(return inout char[] haystack)
@nogc nothrow pure @trusted
in
{
assert(haystack.length == 0 || haystack.ptr !is null);
}
do
{
auto length = haystack.length;
enum size_t highBits = filledBytes!(0x01, 0);
enum size_t mask = filledBytes!(0x80, 0);
// Align
auto bytes = cast(inout(ubyte)*) haystack;
while (length > 0 && ((cast(size_t) bytes) & 3) != 0)
{
if (*bytes == '\0')
{
return haystack[0 .. haystack.length - length];
}
++bytes;
--length;
}
// Check if some of the words contains 0
auto words = cast(inout(size_t)*) bytes;
while (length >= size_t.sizeof)
{
if (((*words - highBits) & (~*words) & mask) != 0)
{
break;
}
++words;
length -= size_t.sizeof;
}
// Find the exact 0 position in the word
bytes = cast(inout(ubyte)*) words;
while (length > 0)
{
if (*bytes == '\0')
{
return haystack[0 .. haystack.length - length];
}
++bytes;
--length;
}
return null;
}
///
@nogc nothrow pure @safe unittest
{
assert(cmp(findNullTerminated("abcdef\0gh"), "abcdef") == 0);
assert(cmp(findNullTerminated("\0garbage"), "") == 0);
assert(cmp(findNullTerminated("\0"), "") == 0);
assert(cmp(findNullTerminated("cstring\0"), "cstring") == 0);
assert(findNullTerminated(null) is null);
assert(findNullTerminated("abcdef") is null);
}