From cafe8645336bfc60be03b5a558164b3bc7df79ef Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 17 Mar 2025 15:24:14 +0100 Subject: [PATCH 1/8] std.json: Avoid setting union members, set the whole union instead (#10683) To work around https://github.com/dlang/dmd/issues/20675. --- std/json.d | 44 ++++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/std/json.d b/std/json.d index 7182f6ee8..eb08de8f0 100644 --- a/std/json.d +++ b/std/json.d @@ -562,8 +562,7 @@ struct JSONValue else static if (is(T : string)) { type_tag = JSONType.string; - string t = arg; - () @trusted { store.str = t; }(); + store = Store(str: arg); } // https://issues.dlang.org/show_bug.cgi?id=15884 else static if (isSomeString!T) @@ -572,7 +571,7 @@ struct JSONValue // FIXME: std.Array.Array(Range) is not deduced as 'pure' () @trusted { import std.utf : byUTF; - store.str = cast(immutable)(arg.byUTF!char.array); + store = Store(str: cast(immutable)(arg.byUTF!char.array)); }(); } else static if (is(T : bool)) @@ -582,17 +581,17 @@ struct JSONValue else static if (is(T : ulong) && isUnsigned!T) { type_tag = JSONType.uinteger; - store.uinteger = arg; + store = Store(uinteger: arg); } else static if (is(T : long)) { type_tag = JSONType.integer; - store.integer = arg; + store = Store(integer: arg); } else static if (isFloatingPoint!T) { type_tag = JSONType.float_; - store.floating = arg; + store = Store(floating: arg); } else static if (is(T : Value[Key], Key, Value)) { @@ -600,45 +599,34 @@ struct JSONValue type_tag = JSONType.object; static if (is(Value : JSONValue)) { - JSONValue[string] t = arg; - () @trusted { - store.object.isOrdered = false; - store.object.unordered = t; - }(); + store = Store(object: Store.Object(false, unordered: arg)); } else { JSONValue[string] aa; foreach (key, value; arg) aa[key] = JSONValue(value); - () @trusted { - store.object.isOrdered = false; - store.object.unordered = aa; - }(); + store = Store(object: Store.Object(false, unordered: aa)); } } else static if (is(T : OrderedObjectMember[])) { type_tag = JSONType.object; - () @trusted { - store.object.isOrdered = true; - store.object.ordered = arg; - }(); + store = Store(object: Store.Object(true, ordered: arg)); } else static if (isArray!T) { type_tag = JSONType.array; static if (is(ElementEncodingType!T : JSONValue)) { - JSONValue[] t = arg; - () @trusted { store.array = t; }(); + store = Store(array: arg); } else { JSONValue[] new_arg = new JSONValue[arg.length]; foreach (i, e; arg) new_arg[i] = JSONValue(e); - () @trusted { store.array = new_arg; }(); + store = Store(array: new_arg); } } else static if (is(T : JSONValue)) @@ -658,14 +646,14 @@ struct JSONValue type_tag = JSONType.array; static if (is(ElementEncodingType!T : JSONValue)) { - store.array = arg; + store = Store(array: arg); } else { JSONValue[] new_arg = new JSONValue[arg.length]; foreach (i, e; arg) new_arg[i] = JSONValue(e); - store.array = new_arg; + store = Store(array: new_arg); } } @@ -1616,13 +1604,13 @@ if (isSomeFiniteCharInputRange!T) if (isFloat) { value.type_tag = JSONType.float_; - value.store.floating = parse!double(data); + value.store = JSONValue.Store(floating: parse!double(data)); } else { if (isNegative) { - value.store.integer = parse!long(data); + value.store = JSONValue.Store(integer: parse!long(data)); value.type_tag = JSONType.integer; } else @@ -1631,12 +1619,12 @@ if (isSomeFiniteCharInputRange!T) ulong u = parse!ulong(data); if (u & (1UL << 63)) { - value.store.uinteger = u; + value.store = JSONValue.Store(uinteger: u); value.type_tag = JSONType.uinteger; } else { - value.store.integer = u; + value.store = JSONValue.Store(integer: u); value.type_tag = JSONType.integer; } } From 79cbde1ab69bae9372f310d663edfc43166095e3 Mon Sep 17 00:00:00 2001 From: Steven Schveighoffer Date: Mon, 17 Mar 2025 18:38:36 -0400 Subject: [PATCH 2/8] Fix #10680 - Instead of passing receiver into the conversion function, (#10684) just return the value and assign it to the receiver. Renamed the conversion function and also cleaned up all the `typeof` calls, which were very verbose. --- std/getopt.d | 73 ++++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/std/getopt.d b/std/getopt.d index 1a9072207..fc5cdac73 100644 --- a/std/getopt.d +++ b/std/getopt.d @@ -610,14 +610,14 @@ private template optionValidator(A...) alias optionValidator = message; } -private void handleConversion(R)(string option, string value, R* receiver, +private auto getoptTo(R)(string option, string value, size_t idx, string file = __FILE__, size_t line = __LINE__) { import std.conv : to, ConvException; import std.format : format; try { - *receiver = to!(typeof(*receiver))(value); + return to!R(value); } catch (ConvException e) { @@ -876,12 +876,18 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, // (and potentially args[i + 1] too, but that comes later) args = args[0 .. i] ~ args[i + 1 .. $]; - static if (is(typeof(*receiver) == bool)) + static if (is(typeof(*receiver))) + alias Target = typeof(*receiver); + else + // delegate + alias Target = void; + + static if (is(Target == bool)) { if (val.length) { // parse '--b=true/false' - handleConversion(option, val, receiver, i); + *receiver = getoptTo!(Target)(option, val, i); } else { @@ -894,23 +900,23 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, import std.exception : enforce; // non-boolean option, which might include an argument enum isCallbackWithLessThanTwoParameters = - (is(typeof(receiver) == delegate) || is(typeof(*receiver) == function)) && + (is(R == delegate) || is(Target == function)) && !is(typeof(receiver("", ""))); if (!isCallbackWithLessThanTwoParameters && !(val.length) && !incremental) { // Eat the next argument too. Check to make sure there's one // to be eaten first, though. enforce!GetOptException(i < args.length, - "Missing value for argument " ~ a ~ "."); + "Missing value for argument " ~ a ~ "."); val = args[i]; args = args[0 .. i] ~ args[i + 1 .. $]; } - static if (is(typeof(*receiver) == enum) || - is(typeof(*receiver) == string)) + static if (is(Target == enum) || + is(Target == string)) { - handleConversion(option, val, receiver, i); + *receiver = getoptTo!Target(option, val, i); } - else static if (is(typeof(*receiver) : real)) + else static if (is(Target : real)) { // numeric receiver if (incremental) @@ -919,16 +925,16 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, } else { - handleConversion(option, val, receiver, i); + *receiver = getoptTo!Target(option, val, i); } } - else static if (is(typeof(*receiver) == string)) + else static if (is(Target == string)) { // string receiver - *receiver = to!(typeof(*receiver))(val); + *receiver = getoptTo!(Target)(option, val, i); } - else static if (is(typeof(receiver) == delegate) || - is(typeof(*receiver) == function)) + else static if (is(R == delegate) || + is(Target == function)) { static if (is(typeof(receiver("", "")) : void)) { @@ -952,29 +958,25 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, receiver(); } } - else static if (isArray!(typeof(*receiver))) + else static if (isArray!(Target)) { // array receiver import std.range : ElementEncodingType; - alias E = ElementEncodingType!(typeof(*receiver)); + alias E = ElementEncodingType!(Target); if (arraySep == "") { - E tmp; - handleConversion(option, val, &tmp, i); - *receiver ~= tmp; + *receiver ~= getoptTo!E(option, val, i); } else { foreach (elem; val.splitter(arraySep)) { - E tmp; - handleConversion(option, elem, &tmp, i); - *receiver ~= tmp; + *receiver ~= getoptTo!E(option, elem, i); } } } - else static if (isAssociativeArray!(typeof(*receiver))) + else static if (isAssociativeArray!(Target)) { // hash receiver alias K = typeof(receiver.keys[0]); @@ -991,14 +993,7 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, ~ to!string(assignChar) ~ "' in argument '" ~ input ~ "'."); auto key = input[0 .. j]; auto value = input[j + 1 .. $]; - - K k; - handleConversion("", key, &k, 0); - - V v; - handleConversion("", value, &v, 0); - - return tuple(k,v); + return tuple(getoptTo!K("", key, 0), getoptTo!V("", value, 0)); } static void setHash(Range)(R receiver, Range range) @@ -1013,7 +1008,7 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, setHash(receiver, val.splitter(arraySep)); } else - static assert(false, "getopt does not know how to handle the type " ~ typeof(receiver).stringof); + static assert(false, "getopt does not know how to handle the type " ~ R.stringof); } } @@ -1099,6 +1094,18 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, assert(values == ["foo":0, "bar":1, "baz":2], to!string(values)); } +// https://github.com/dlang/phobos/issues/10680 +@safe unittest +{ + arraySep = ","; + scope(exit) arraySep = ""; + const(string)[] s; + string[] args = ["program.name", "-s", "a", "-s", "b", "-s", "c,d,e"]; + getopt(args, "values|s", &s); + assert(s == ["a", "b", "c", "d", "e"]); +} + + /** The option character (default '-'). From 0f3185e9541f05c55995219a4b8ee64725a56e83 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Thu, 20 Mar 2025 15:11:25 +0100 Subject: [PATCH 3/8] std.numeric: Fix IEEE typo in link --- std/numeric.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/numeric.d b/std/numeric.d index 9966b1ce7..918984fd5 100644 --- a/std/numeric.d +++ b/std/numeric.d @@ -37,7 +37,7 @@ public enum CustomFloatFlags * Store values in normalized form by default. The actual precision of the * significand is extended by 1 bit by assuming an implicit leading bit of 1 * instead of 0. i.e. `1.nnnn` instead of `0.nnnn`. - * True for all $(LINK2 https://en.wikipedia.org/wiki/IEEE_floating_point, IEE754) types + * True for all $(LINK2 https://en.wikipedia.org/wiki/IEEE_floating_point, IEEE754) types */ storeNormalized = 2, From b41bcd93df0c722d6cc111c87b97fe704ce5dcd3 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 23 Mar 2025 00:51:23 +0100 Subject: [PATCH 4/8] std.experimental.allocator.building_blocks.allocator_list: Fix unittests on macOS arm64 (#10704) Otherwise hitting a consistently failing assertion for LDC CI. --- .../allocator/building_blocks/allocator_list.d | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/std/experimental/allocator/building_blocks/allocator_list.d b/std/experimental/allocator/building_blocks/allocator_list.d index c6d5fca3d..9d30b5e02 100644 --- a/std/experimental/allocator/building_blocks/allocator_list.d +++ b/std/experimental/allocator/building_blocks/allocator_list.d @@ -1140,7 +1140,6 @@ template SharedAllocatorList(alias factoryFunction, import std.algorithm.comparison : max; import std.typecons : Ternary; - enum pageSize = 4096; enum numPages = 2; static void testrw(void[] b) @@ -1269,8 +1268,6 @@ template SharedAllocatorList(alias factoryFunction, import std.algorithm.comparison : max; import std.typecons : Ternary; - enum pageSize = 4096; - static void testrw(void[] b) { ubyte* buf = cast(ubyte*) b.ptr; @@ -1283,17 +1280,17 @@ template SharedAllocatorList(alias factoryFunction, enum numPages = 5; AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a; - auto b = a.alignedAllocate(1, pageSize * 2); + auto b = a.alignedAllocate(1, cast(uint) (pageSize * 2)); assert(b.length == 1); - assert(a.expand(b, 4095)); - assert(b.ptr.alignedAt(2 * 4096)); - assert(b.length == 4096); + assert(a.expand(b, pageSize - 1)); + assert(b.ptr.alignedAt(cast(uint) (pageSize * 2))); + assert(b.length == pageSize); - b = a.allocate(4096); - assert(b.length == 4096); + b = a.allocate(pageSize); + assert(b.length == pageSize); assert(a.allocators.length == 1); - assert(a.allocate(4096 * 5).length == 4096 * 5); + assert(a.allocate(pageSize * 5).length == pageSize * 5); assert(a.allocators.length == 2); assert(a.deallocateAll()); @@ -1339,7 +1336,6 @@ template SharedAllocatorList(alias factoryFunction, import std.algorithm.comparison : max; enum numThreads = 100; - enum pageSize = 4096; enum numPages = 10; SharedAllocatorList!((n) => SharedAscendingPageAllocator(max(n, pageSize * numPages)), Mallocator) a; From c64232d8139dacaf40cae46a27a1fd8927949df1 Mon Sep 17 00:00:00 2001 From: "Richard (Rikki) Andrew Cattermole" Date: Tue, 25 Mar 2025 21:10:34 +1300 Subject: [PATCH 5/8] Fix #10713 - std.format string positions affect all further format specifiers (#10714) --- std/format/spec.d | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/std/format/spec.d b/std/format/spec.d index e5564c99d..74243becf 100644 --- a/std/format/spec.d +++ b/std/format/spec.d @@ -296,6 +296,8 @@ if (is(Unqual!Char == Char)) } width = 0; + indexStart = 0; + indexEnd = 0; precision = UNSPECIFIED; nested = null; // Parse the spec (we assume we're past '%' already) @@ -834,6 +836,21 @@ if (is(Unqual!Char == Char)) == "$ expected after '*10' in format string"); } +// https://github.com/dlang/phobos/issues/10713 +@safe pure unittest +{ + import std.array : appender; + auto f = FormatSpec!char("%3$d%d"); + + auto w = appender!(char[])(); + f.writeUpToNextSpec(w); + assert(f.indexStart == 3); + + f.writeUpToNextSpec(w); + assert(w.data.length == 0); + assert(f.indexStart == 0); +} + /** Helper function that returns a `FormatSpec` for a single format specifier. From 530660bd9b057e6fdbf0302e7e76a2dd6f9bce95 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Thu, 27 Mar 2025 00:31:01 +0100 Subject: [PATCH 6/8] std.allocator: Comment out broken class instance size test (#10717) This can be reverted after the fix for dlang issue 21065 is merged Refs: dlang/dmd#21065 --- std/experimental/allocator/common.d | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/std/experimental/allocator/common.d b/std/experimental/allocator/common.d index b06fb627d..245157258 100644 --- a/std/experimental/allocator/common.d +++ b/std/experimental/allocator/common.d @@ -63,7 +63,8 @@ unittest class C2 { char c; } static assert(stateSize!C2 == 4 * size_t.sizeof); static class C3 { char c; } - static assert(stateSize!C3 == 2 * size_t.sizeof + char.sizeof); + // Uncomment test after dmd issue closed https://github.com/dlang/dmd/issues/21065 + //static assert(stateSize!C3 == 3 * size_t.sizeof); } /** From 205256abb1f86faf986f8c789cb733ca4137246e Mon Sep 17 00:00:00 2001 From: "Richard (Rikki) Andrew Cattermole" Date: Fri, 28 Mar 2025 21:49:06 +1300 Subject: [PATCH 7/8] Fix #10699 - std.format with position with omitted second number does not work (#10716) --- std/format/spec.d | 18 ++++++++++++++++-- std/format/write.d | 23 ++++++++++++++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/std/format/spec.d b/std/format/spec.d index 74243becf..b828bb65b 100644 --- a/std/format/spec.d +++ b/std/format/spec.d @@ -127,14 +127,16 @@ if (is(Unqual!Char == Char)) Counting starts with `1`. Set to `0` if not used. Default: `0`. */ - ubyte indexStart; + ushort indexStart; /** Index of the last argument for positional parameter ranges. Counting starts with `1`. Set to `0` if not used. Default: `0`. + + The maximum value of this field is used as a sentinel to indicate the arguments' length. */ - ubyte indexEnd; + ushort indexEnd; version (StdDdoc) { @@ -851,6 +853,18 @@ if (is(Unqual!Char == Char)) assert(f.indexStart == 0); } +// https://github.com/dlang/phobos/issues/10699 +@safe pure unittest +{ + import std.array : appender; + auto f = FormatSpec!char("%1:$d"); + auto w = appender!(char[])(); + + f.writeUpToNextSpec(w); + assert(f.indexStart == 1); + assert(f.indexEnd == ushort.max); +} + /** Helper function that returns a `FormatSpec` for a single format specifier. diff --git a/std/format/write.d b/std/format/write.d index 078fa786e..d704c14f5 100644 --- a/std/format/write.d +++ b/std/format/write.d @@ -648,9 +648,16 @@ uint formattedWrite(Writer, Char, Args...)(auto ref Writer w, const scope Char[] break SWITCH; } default: - throw new FormatException( - text("Positional specifier %", spec.indexStart, '$', spec.spec, - " index exceeds ", Args.length)); + if (spec.indexEnd == spec.indexEnd.max) + break; + else if (spec.indexEnd == spec.indexStart) + throw new FormatException( + text("Positional specifier %", spec.indexStart, '$', spec.spec, + " index exceeds ", Args.length)); + else + throw new FormatException( + text("Positional specifier %", spec.indexStart, ":", spec.indexEnd, '$', spec.spec, + " index exceeds ", Args.length)); } } return currentArg; @@ -1199,6 +1206,16 @@ if (isSomeString!(typeof(fmt))) formattedWrite(stream, "%s", aa); } +// https://github.com/dlang/phobos/issues/10699 +@safe pure unittest +{ + import std.array : appender; + auto w = appender!(char[])(); + + formattedWrite(w, "%1:$d", 1, 2, 3); + assert(w.data == "123"); +} + /** Formats a value of any type according to a format specifier and writes the result to an output range. From 60034b56e2a036a66fa78cbc0ec0290956423684 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Mon, 31 Mar 2025 13:33:59 +0200 Subject: [PATCH 8/8] purge changelog --- changelog/bit-cast.dd | 14 --------- changelog/formatted_read_tuple_return.dd | 30 ------------------ changelog/fromhexstring.dd | 15 --------- changelog/odbc-4.dd | 11 ------- changelog/pop-grapheme.dd | 38 ----------------------- changelog/readfln.dd | 6 ---- changelog/shared-allocator-list.dd | 13 -------- changelog/sumtype_procedural_api.dd | 39 ------------------------ changelog/unicode-16.dd | 16 ---------- 9 files changed, 182 deletions(-) delete mode 100644 changelog/bit-cast.dd delete mode 100644 changelog/formatted_read_tuple_return.dd delete mode 100644 changelog/fromhexstring.dd delete mode 100644 changelog/odbc-4.dd delete mode 100644 changelog/pop-grapheme.dd delete mode 100644 changelog/readfln.dd delete mode 100644 changelog/shared-allocator-list.dd delete mode 100644 changelog/sumtype_procedural_api.dd delete mode 100644 changelog/unicode-16.dd diff --git a/changelog/bit-cast.dd b/changelog/bit-cast.dd deleted file mode 100644 index 11333d2b9..000000000 --- a/changelog/bit-cast.dd +++ /dev/null @@ -1,14 +0,0 @@ -Added `std.conv.bitCast` - -This convenience function allows reinterpreting casts to be written in a more -readable way. - ---- -uint n = 0xDEADBEEF; - -// Before -writeln("Bytes of n are: ", *cast(const ubyte[4]*) &n); - -// After -writeln("Bytes of n are: ", n.bitCast!(const ubyte[4])); ---- diff --git a/changelog/formatted_read_tuple_return.dd b/changelog/formatted_read_tuple_return.dd deleted file mode 100644 index 03fb23781..000000000 --- a/changelog/formatted_read_tuple_return.dd +++ /dev/null @@ -1,30 +0,0 @@ -Extend the functionality of formattedRead to permit a std.file.slurp like execution. - -Template argument types can now be passed to formattedRead along with a -format string to parse and read the input range as a Tuple of those arguments. -All arguments must be read successfully, otherwise, and unlike std.file.slurp -which has non exhaustive option for partial reads, it'll throw a std.format.FormatException. - ---- -import std.exception : assertThrown; -import std.format : FormatException; -import std.typecons : tuple; - -@safe pure unittest -{ - auto complete = "hello!34.5:124".formattedRead!(string, double, int)("%s!%s:%s"); - assert(complete == tuple("hello", 34.5, 124)); - - assertThrown!FormatException("hello!34.5:".formattedRead!(string, double, int)("%s!%s:%s")); -} - -/// The format string can be checked at compile-time: -@safe pure unittest -{ - auto expected = tuple("hello", 124, 34.5); - auto result = "hello!124:34.5".formattedRead!("%s!%s:%s", string, int, double); - assert(result == expected); - - assertThrown!FormatException("hello!34.5:".formattedRead!("%s!%s:%s", string, double, int)); -} ---- diff --git a/changelog/fromhexstring.dd b/changelog/fromhexstring.dd deleted file mode 100644 index 152be72d6..000000000 --- a/changelog/fromhexstring.dd +++ /dev/null @@ -1,15 +0,0 @@ -Added fromHexString and fromHexStringAsRange functions to std.digest. - -This new function enables the converion of a hex string to a range of bytes. -Unlike the template $(REF hexString, std, conv) that was designed to supersede -a language feature, this function is usable with runtime input. - -The `std.conv` module lacks facilities to conveniently transform the input -to a series of bytes directly. Both $(REF parse, std, conv) and $(REF to, std, -conv) can only handle the conversion for a single value of the requested target -integer type. Furthermore, said functions would allocate a new buffer for the -result, while `fromHexStringAsRange` operates lazily by implementing a forward -range. - -For further convenience, a validation function $(REF isHexString, std, digest) -was added as well. diff --git a/changelog/odbc-4.dd b/changelog/odbc-4.dd deleted file mode 100644 index 19d72e9a3..000000000 --- a/changelog/odbc-4.dd +++ /dev/null @@ -1,11 +0,0 @@ -ODBC Bindings in `etc.c.odbc` have been updated to ODBC 4.0. - -ODBC 4.0, via these new bindings, adds the following functionality: - -1. Support for semi-structured data, such as JSON. -2. Collection valued columns. -3. Web-based Authorization flows. - -A full list of new features can be found here: https://github.com/Microsoft/ODBC-Specification/blob/master/ODBC%204.0.md - -Additionally these modules add support for 64-bit ODBC interfaces. diff --git a/changelog/pop-grapheme.dd b/changelog/pop-grapheme.dd deleted file mode 100644 index 48c93d6fc..000000000 --- a/changelog/pop-grapheme.dd +++ /dev/null @@ -1,38 +0,0 @@ -Added popGrapheme function to std.uni. - -The new function is a cross between the existing $(REF graphemeStride, std, -uni) and $(REF decodeGrapheme, std, uni) functions. The new function both -supports `@safe pure nothrow @nogc` like `graphemeStride` does as long as you -don't rely on autodecoding (side node: `@nogc` support for `graphemeStride` -added in this release), and works with any non-array ranges just like -`decodeGrapheme` does. - -Example: - -------- -import std.uni; - -// Two Union Jacks of the Great Britain in each -string s = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; -wstring ws = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; -dstring ds = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; - -// String pop length in code units, not points. -assert(s.popGrapheme() == 8); -assert(ws.popGrapheme() == 4); -assert(ds.popGrapheme() == 2); - -assert(s == "\U0001F1EC\U0001F1E7"); -assert(ws == "\U0001F1EC\U0001F1E7"); -assert(ds == "\U0001F1EC\U0001F1E7"); - -import std.algorithm.comparison : equal; -import std.algorithm.iteration : filter; - -// Also works for non-random access ranges as long as the -// character type is 32-bit. -auto testPiece = "\r\nhello!"d.filter!(x => !x.isAlpha); -// Windows-style line ending is two code point in a single grapheme. -assert(testPiece.popGrapheme() == 2); -assert(testPiece.equal("!"d)); -------- diff --git a/changelog/readfln.dd b/changelog/readfln.dd deleted file mode 100644 index c1f8afdfe..000000000 --- a/changelog/readfln.dd +++ /dev/null @@ -1,6 +0,0 @@ -Added `readfln` and `File.readfln` to `std.stdio` - -These functions read a single line of input and parse it using a format string. -Unlike `readf`, they will not accidentally read multiple lines if the user -forgets to include a line terminator in the format string—a common mistake for -beginners. diff --git a/changelog/shared-allocator-list.dd b/changelog/shared-allocator-list.dd deleted file mode 100644 index 56d77b126..000000000 --- a/changelog/shared-allocator-list.dd +++ /dev/null @@ -1,13 +0,0 @@ -Added the `SharedAllocatorList`, as the thread-safe version of the regular `AllocatorList`. - -The new $(REF SharedAllocatorList, std,experimental,allocator,building_blocks,allocator_list) has the same semantics as the regular `AllocatorList`. -Just as the regular `AllocatorList`, if the `BookkeepingAllocator` is `NullAllocator`, the `SharedAllocatorList` will switch to `ouroboros` mode, -allocationg memory for its own metadata. - ---- -SharedAllocatorList!((n) => SharedAscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a; -auto b = a.allocate(100); -assert(b.length == 100); - -assert(a.deallocate(b)); ---- diff --git a/changelog/sumtype_procedural_api.dd b/changelog/sumtype_procedural_api.dd deleted file mode 100644 index b11cfc4eb..000000000 --- a/changelog/sumtype_procedural_api.dd +++ /dev/null @@ -1,39 +0,0 @@ -New procedural API for `std.sumtype` - -`std.sumtype` has three new convenience functions for querying and retrieving -the value of a `SumType` object. - -* `has!T` returns `true` if the `SumType` object has a value of type `T`. -* `get!T` returns the value if its type is `T`, or asserts if it is not. -* `tryGet!T` returns the value if its type is `T`, or throws an exception if it - is not. - -These functions make it easier to write code using `SumType` in a procedural -style, as opposed to the functional style encouraged by `match`. - -Example: - ---- -import std.sumtype; -import std.stdio; - -SumType!(string, double) example = "hello"; - -if (example.has!string) -{ - writeln("string: ", example.get!string); -} -else if (example.has!double) -{ - writeln("double: ", example.get!double); -} - -try -{ - writeln("double: ", example.tryGet!double); -} -catch (MatchException e) -{ - writeln("Couldn't get a double."); -} ---- diff --git a/changelog/unicode-16.dd b/changelog/unicode-16.dd deleted file mode 100644 index 84455fb93..000000000 --- a/changelog/unicode-16.dd +++ /dev/null @@ -1,16 +0,0 @@ -std.uni has been upgraded from Unicode 15.1.0 to 16.0.0 - -This Unicode update was released September 10, 2024, and adds new blocks with characters. -See: https://www.unicode.org/versions/Unicode16.0.0/ - -``` -import std; - -void main() -{ - const alphaCount = iota(0, dchar.max).filter!(std.uni.isAlpha).walkLength; - writeln(alphaCount); - // formerly: 138387 - // now: 142759 -} -```