Merge pull request #6471 from schveiguy/putstrings

char[] and wchar[] should be output ranges
merged-on-behalf-of: Nathan Sashihara <n8sh@users.noreply.github.com>
This commit is contained in:
The Dlang Bot 2018-06-29 10:04:28 +02:00 committed by GitHub
commit 0e8722abbd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 111 additions and 22 deletions

View file

@ -391,7 +391,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
* The number of times the output range's `put` method was invoked.
*/
size_t encode(E, R)(scope const(E)[] source, auto ref R range)
if (is(E : ubyte) && isOutputRange!(R, char))
if (is(E : ubyte) && isOutputRange!(R, char) && !is(R == char[]))
out(result)
{
assert(result == encodeLength(source.length), "The number of put is different from the length of Base64");

View file

@ -620,6 +620,14 @@ uint formattedWrite(Writer, Char, A...)(auto ref Writer w, in Char[] fmt, A args
assert(w.data == "@safe/pure 42");
}
@safe pure unittest
{
char[20] buf;
auto w = buf[];
formattedWrite(w, "%s %d", "@safe/pure", 42);
assert(buf[0 .. $ - w.length] == "@safe/pure 42");
}
/**
Reads characters from $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
`r`, converts them according to `fmt`, and writes them to `args`.

View file

@ -253,7 +253,7 @@ enum bool isInputRange(R) =
puts the whole raw element `e` into `r`. doPut will not attempt to
iterate, slice or transcode `e` in any way shape or form. It will $(B only)
call the correct primitive (`r.put(e)`, $(D r.front = e) or
`r(0)` once.
`r(e)` once.
This can be important when `e` needs to be placed in `r` unchanged.
Furthermore, it can be useful when working with `InputRange`s, as doPut
@ -272,6 +272,20 @@ private void doPut(R, E)(ref R r, auto ref E e)
"Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ ".");
r.put(e);
}
else static if (isNarrowString!R && is(const(E) == const(typeof(r[0]))))
{
// one character, we can put it
r[0] = e;
r = r[1 .. $];
}
else static if (isNarrowString!R && isNarrowString!E && is(typeof(r[] = e)))
{
// slice assign. Note that this is a duplicate from put, but because
// putChar uses doPut exclusively, we have to copy it here.
immutable len = e.length;
r[0 .. len] = e;
r = r[len .. $];
}
else static if (isInputRange!R)
{
static assert(is(typeof(r.front = e)),
@ -308,7 +322,7 @@ private void doPut(R, E)(ref R r, auto ref E e)
static assert( isNativeOutputRange!(int[4][], int)); //Scary!
static assert( isNativeOutputRange!(int[4][], int[4]));
static assert(!isNativeOutputRange!( char[], char));
static assert( isNativeOutputRange!( char[], char));
static assert(!isNativeOutputRange!( char[], dchar));
static assert( isNativeOutputRange!(dchar[], char));
static assert( isNativeOutputRange!(dchar[], dchar));
@ -453,22 +467,32 @@ void put(R, E)(ref R r, E e)
}
/**
* Because of auto-decoding, the `front` of a `string` is a `dchar`,
* so using `put` with `char` arrays is disallowed. In order to fill
* any `char` type array, use $(REF byCodeUnit, std, utf).
* It's also possible to `put` any width strings or characters into narrow
* strings -- put does the conversion for you.
*
* Note that putting the same width character as the target buffer type is
* `nothrow`, but transcoding can throw a $(REF UTFException, std, utf).
*/
@safe pure nothrow unittest
@safe pure unittest
{
import std.utf : byCodeUnit;
// the elements must be mutable, so using string or const(char)[]
// won't compile
char[] s1 = new char[13];
auto r1 = s1.byCodeUnit;
auto r1 = s1;
put(r1, "Hello, World!"w);
assert(s1 == "Hello, World!");
}
@safe pure nothrow unittest
{
// same thing, just using same character width.
char[] s1 = new char[13];
auto r1 = s1;
put(r1, "Hello, World!");
assert(s1 == "Hello, World!");
}
@safe pure nothrow @nogc unittest
{
static struct R() { void put(in char[]) {} }
@ -486,9 +510,9 @@ if (isSomeChar!E)
ref const(wchar)[] wstringInit();
ref const(dchar)[] dstringInit();
enum csCond = !isDynamicArray!R && is(typeof(doPut(r, cstringInit())));
enum wsCond = !isDynamicArray!R && is(typeof(doPut(r, wstringInit())));
enum dsCond = !isDynamicArray!R && is(typeof(doPut(r, dstringInit())));
enum csCond = is(typeof(doPut(r, cstringInit())));
enum wsCond = is(typeof(doPut(r, wstringInit())));
enum dsCond = is(typeof(doPut(r, dstringInit())));
//Use "max" to avoid static type demotion
enum ccCond = is(typeof(doPut(r, char.max)));
@ -585,9 +609,64 @@ pure @safe unittest
char[] a = new char[10];
static assert(!__traits(compiles, put(a, 1.0L)));
static assert(!__traits(compiles, put(a, 1)));
// char[] is NOT output range.
static assert(!__traits(compiles, put(a, 'a')));
static assert(!__traits(compiles, put(a, "ABC")));
//char[] is now an output range for char, wchar, dchar, and ranges of such.
static assert(__traits(compiles, putChar(a, 'a')));
static assert(__traits(compiles, put(a, wchar('a'))));
static assert(__traits(compiles, put(a, dchar('a'))));
static assert(__traits(compiles, put(a, "ABC")));
static assert(__traits(compiles, put(a, "ABC"w)));
static assert(__traits(compiles, put(a, "ABC"d)));
}
@safe unittest
{
// attempt putting into narrow strings by transcoding
char[] a = new char[10];
auto b = a;
put(a, "ABC"w);
assert(b[0 .. 3] == "ABC");
assert(a.length == 7);
a = b; // reset
put(a, 'λ');
assert(b[0 .. 2] == "λ");
assert(a.length == 8);
a = b; // reset
put(a, "ABC"d);
assert(b[0 .. 3] == "ABC");
assert(a.length == 7);
a = b; // reset
put(a, '𐐷');
assert(b[0 .. 4] == "𐐷");
assert(a.length == 6);
wchar[] aw = new wchar[10];
auto bw = aw;
put(aw, "ABC");
assert(bw[0 .. 3] == "ABC"w);
assert(aw.length == 7);
aw = bw; // reset
put(aw, 'λ');
assert(bw[0 .. 1] == "λ"w);
assert(aw.length == 9);
aw = bw; // reset
put(aw, "ABC"d);
assert(bw[0 .. 3] == "ABC"w);
assert(aw.length == 7);
aw = bw; // reset
put(aw, '𐐷');
assert(bw[0 .. 2] == "𐐷"w);
assert(aw.length == 8);
aw = bw; // reset
put(aw, "𐐷"); // try transcoding from char[]
assert(bw[0 .. 2] == "𐐷"w);
assert(aw.length == 8);
}
@safe unittest
@ -833,7 +912,7 @@ enum bool isOutputRange(R, E) =
void myprint(in char[] s) { }
static assert(isOutputRange!(typeof(&myprint), char));
static assert(!isOutputRange!(char[], char));
static assert( isOutputRange!(char[], char));
static assert( isOutputRange!(dchar[], wchar));
static assert( isOutputRange!(dchar[], dchar));
}
@ -848,7 +927,7 @@ enum bool isOutputRange(R, E) =
static assert( isOutputRange!(Appender!string, string));
static assert( isOutputRange!(Appender!string*, string));
static assert(!isOutputRange!(Appender!string, int));
static assert(!isOutputRange!(wchar[], wchar));
static assert( isOutputRange!(wchar[], wchar));
static assert( isOutputRange!(dchar[], char));
static assert( isOutputRange!(dchar[], string));
static assert( isOutputRange!(dchar[], wstring));

View file

@ -880,13 +880,15 @@ public struct UUID
const uint lo = (entry) & 0x0F;
result[pos+1] = toChar!char(lo);
}
foreach (i, c; result)
static if (!__traits(compiles, put(sink, result[])) || isSomeString!Writer)
{
static if (__traits(compiles, put(sink, c)))
put(sink, c);
else
foreach (i, c; result)
sink[i] = cast(typeof(sink[i]))c;
}
else
{
put(sink, result[]);
}
}
/**