aboutsummaryrefslogtreecommitdiff
path: root/gcc/d/dmd/root/string.d
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/d/dmd/root/string.d')
-rw-r--r--gcc/d/dmd/root/string.d237
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);
+}