Merge pull request #10732 from dlang/stable

Merge stable
This commit is contained in:
Dennis 2025-04-02 13:43:44 +02:00 committed by GitHub
commit ff612b867c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 119 additions and 261 deletions

View file

@ -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]));
---

View file

@ -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));
}
---

View file

@ -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.

View file

@ -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.

View file

@ -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));
-------

View file

@ -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.

View file

@ -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));
---

View file

@ -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.");
}
---

View file

@ -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
}
```

View file

@ -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;

View file

@ -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);
}
/**

View file

@ -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)
{
@ -296,6 +298,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 +838,33 @@ 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);
}
// 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.

View file

@ -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.

View file

@ -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 '-').

View file

@ -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);
}
}
@ -1651,13 +1639,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
@ -1666,12 +1654,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;
}
}

View file

@ -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,