diff options
Diffstat (limited to 'gcc/d/dmd/root/string.d')
-rw-r--r-- | gcc/d/dmd/root/string.d | 237 |
1 files changed, 234 insertions, 3 deletions
diff --git a/gcc/d/dmd/root/string.d b/gcc/d/dmd/root/string.d index e82b0d2..369a79b 100644 --- a/gcc/d/dmd/root/string.d +++ b/gcc/d/dmd/root/string.d @@ -1,15 +1,18 @@ /** * Contains various string related functions. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/string.d, root/_string.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/string.d, root/_string.d) * Documentation: https://dlang.org/phobos/dmd_root_string.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/string.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/string.d */ module dmd.root.string; +import core.stdc.string; +import dmd.root.rmem; + /// Slices a `\0`-terminated C-string, excluding the terminator inout(char)[] toDString (inout(char)* s) pure nothrow @nogc { @@ -17,6 +20,27 @@ inout(char)[] toDString (inout(char)* s) pure nothrow @nogc return s ? s[0 .. strlen(s)] : null; } +private struct FTuple(T...) +{ + T expand; +} + +/// Returns: a (length, ptr) tuple for passing a D string to `printf`-style functions with the format string `%.*s` +auto fTuple(const(char)[] str) +{ + return FTuple!(int, const(char)*)(cast(int) str.length, str.ptr); +} + +/// +unittest +{ + import core.stdc.stdio: snprintf; + char[6] buf = '.'; + const(char)[] str = "cutoff"[0..4]; + snprintf(buf.ptr, buf.length, "%.*s", str.fTuple.expand); + assert(buf[] == "cuto\0."); +} + /** Compare two slices for equality, in a case-insensitive way @@ -87,6 +111,23 @@ unittest assert(null.toCStringThen!((v) => v == "\0")); } +/********************************************* + * Convert a D string to a C string by allocating memory, + * copying it, and adding a terminating 0. + * Params: + * s = string to copy + * Result: + * 0-terminated copy of s + */ +char[] toCString(scope const(char)[] s) nothrow +{ + const length = s.length; + char* p = cast(char*)mem.xmalloc_noscan(length + 1); + memcpy(p, s.ptr, length); + p[length] = 0; + return p[0 .. length]; +} + /** * Strips one leading line terminator of the given string. * @@ -274,6 +315,15 @@ do return true; } +///ditto +nothrow @nogc pure @safe +bool startsWith(scope const(char)[] str, scope const(char)[] prefix) +{ + if (str.length < prefix.length) + return false; + return str[0 .. prefix.length] == prefix; +} + /// @system pure nothrow @nogc unittest @@ -286,3 +336,184 @@ unittest assert(ptr.startsWith("123")); assert(!ptr.startsWith("1234")); } + +/********************************** + * Take `text` and turn it into an InputRange that emits + * slices into `text` for each line. + * Params: + * text = array of characters + * Returns: + * InputRange accessing `text` as a sequence of lines + * Reference: + * `std.string.splitLines()` + */ +auto splitLines(const char[] text) +{ + struct Range + { + @safe: + @nogc: + nothrow: + pure: + private: + + const char[] text; + size_t index; // index of start of line + size_t eolIndex; // index of end of line before newline characters + size_t nextIndex; // index past end of line + + public this(const char[] text) + { + this.text = text; + this.index = 0; + this.eolIndex = 0; + this.nextIndex = 0; + } + + public bool empty() { advance(); return index >= text.length; } + + public void popFront() { advance(); index = nextIndex; } + + public const(char)[] front() + { + advance(); + if (index > eolIndex || index >= text.length) + return ""; + + return text[index .. eolIndex]; + } + + private void advance() + { + if (index != nextIndex) // if already advanced + return; + + for (size_t i = index; i < text.length; ++i) + { + switch (text[i]) + { + case '\v', '\f', '\n': + eolIndex = i; + nextIndex = i + 1; + return; + + case '\r': + if (i + 1 < text.length && text[i + 1] == '\n') // decode "\r\n" + { + eolIndex = i; + nextIndex = i + 2; + return; + } + eolIndex = i; + nextIndex = i + 1; + return; + + /* Manually decode: + * NEL is C2 85 + */ + case 0xC2: + if (i + 1 < text.length && text[i + 1] == 0x85) + { + eolIndex = i; + nextIndex = i + 2; + return; + } + break; + + /* Manually decode: + * lineSep is E2 80 A8 + * paraSep is E2 80 A9 + */ + case 0xE2: + if (i + 2 < text.length && + text[i + 1] == 0x80 && + (text[i + 2] == 0xA8 || text[i + 2] == 0xA9) + ) + { + eolIndex = i; + nextIndex = i + 3; + return; + } + break; + + default: + break; + } + } + + // No newline found; set indices to the end of the text + eolIndex = text.length; + nextIndex = text.length; + } + } + + return Range(text); +} + +private struct FindSplit +{ +@nogc nothrow pure @safe: + const(char)[][3] elem; + + ref const(char)[] opIndex(size_t i) scope return { return elem[i]; } + bool opCast() const scope { return elem[1].length > 0; } +} + +/** +Find a substring in a string and split the string into before and after parts. +Params: + str = string to look into + needle = substring to find in str (must not be empty) +Returns: + a `FindSplit` object that casts to `true` iff `needle` was found inside `str`. + In that case, `split[1]` is the needle, and `split[0]`/`split[2]` are before/after the needle. +*/ +FindSplit findSplit(return scope const(char)[] str, scope const(char)[] needle) @safe +{ + if (needle.length > str.length) + return FindSplit([str, null, null]); + + foreach (i; 0 .. str.length - needle.length + 1) + { + if (str[i .. i+needle.length] == needle[]) + return FindSplit([ str[0 .. i], str[i .. i+needle.length], str[i+needle.length .. $] ]); + } + return FindSplit([str, null, null]); +} + +unittest +{ + auto s = findSplit("a b c", "c"); + assert(s[0] == "a b "); + assert(s[1] == "c"); + assert(s[2] == ""); + auto s1 = findSplit("a b c", "b"); + assert(s1[0] == "a "); + assert(s1[1] == "b"); + assert(s1[2] == " c"); + assert(!findSplit("a b c", "d")); + assert(!findSplit("", "d")); +} + +/** +Find a string inbetween two substrings +Params: + str = string to look into + l = substring to find on the left + r = substring to find on the right +Returns: + substring of `str` inbetween `l` and `r` +*/ +const(char)[] findBetween(const(char)[] str, const(char)[] l, const(char)[] r) @safe +{ + if (auto s0 = str.findSplit(l)) + if (auto s1 = s0[2].findSplit(r)) + return s1[0]; + return null; +} + +unittest +{ + assert(findBetween("a b c", "a ", " c") == "b"); + assert(findBetween("a b c", "a ", " d") == null); +} |