Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Hackerpilot 2013-07-13 19:37:11 +00:00
commit 6f9c22bfa9
10 changed files with 686 additions and 407 deletions

View file

@ -455,7 +455,7 @@ private struct MapResult(alias fun, Range)
}
}
static if (hasLength!R || isSomeString!R)
static if (hasLength!R)
{
@property auto length()
{
@ -9949,10 +9949,7 @@ assert(!all!"a & 1"([1, 2, 3, 5, 7, 9]));
bool all(alias pred, R)(R range)
if (isInputRange!R && is(typeof(unaryFun!pred(range.front))))
{
// dmd @@@BUG9578@@@ workaround
// return find!(not!(unaryFun!pred))(range).empty;
bool notPred(ElementType!R a) { return !unaryFun!pred(a); }
return find!notPred(range).empty;
return find!(not!(unaryFun!pred))(range).empty;
}
unittest

View file

@ -1577,6 +1577,9 @@ unittest
assert(tv["foobar"] == b.data); a.clear(); b.clear();
}
// @@@9543@@@ These tests were disabled because they actually relied on the input range having length.
// The implementation (currently) doesn't support encoding/decoding from a length-less source.
version(none)
{ // with InputRange
// InputRange to ubyte[] or char[]
auto encoded = Base64.encode(map!(to!(ubyte))(["20", "251", "156", "3", "217", "126"]));

View file

@ -3911,3 +3911,130 @@ unittest
toTextRange(-1, result);
assert(result.data == "-1");
}
/**
Returns the corresponding unsigned value for $(D x) (e.g. if $(D x) has type
$(D int), it returns $(D cast(uint) x)). The advantage compared to the cast
is that you do not need to rewrite the cast if $(D x) later changes type
(e.g from $(D int) to $(D long)).
Note that the result is always mutable even if the original type was const
or immutable. In order to retain the constness, use $(XREF traits, Unsigned).
*/
auto unsigned(T)(T x) if (isIntegral!T)
{
return cast(Unqual!(Unsigned!T))x;
}
///
unittest
{
uint s = 42;
auto u1 = unsigned(s); //not qualified
Unsigned!(typeof(s)) u2 = unsigned(s); //same qualification
immutable u3 = unsigned(s); //totally qualified
}
unittest
{
foreach(T; TypeTuple!(byte, ubyte))
{
static assert(is(typeof(unsigned(cast(T)1)) == ubyte));
static assert(is(typeof(unsigned(cast(const T)1)) == ubyte));
static assert(is(typeof(unsigned(cast(immutable T)1)) == ubyte));
}
foreach(T; TypeTuple!(short, ushort))
{
static assert(is(typeof(unsigned(cast(T)1)) == ushort));
static assert(is(typeof(unsigned(cast(const T)1)) == ushort));
static assert(is(typeof(unsigned(cast(immutable T)1)) == ushort));
}
foreach(T; TypeTuple!(int, uint))
{
static assert(is(typeof(unsigned(cast(T)1)) == uint));
static assert(is(typeof(unsigned(cast(const T)1)) == uint));
static assert(is(typeof(unsigned(cast(immutable T)1)) == uint));
}
foreach(T; TypeTuple!(long, ulong))
{
static assert(is(typeof(unsigned(cast(T)1)) == ulong));
static assert(is(typeof(unsigned(cast(const T)1)) == ulong));
static assert(is(typeof(unsigned(cast(immutable T)1)) == ulong));
}
}
auto unsigned(T)(T x) if (isSomeChar!T)
{
// All characters are unsigned
static assert(T.min == 0);
return cast(Unqual!T) x;
}
unittest
{
foreach(T; TypeTuple!(char, wchar, dchar))
{
static assert(is(typeof(unsigned(cast(T)'A')) == T));
static assert(is(typeof(unsigned(cast(const T)'A')) == T));
static assert(is(typeof(unsigned(cast(immutable T)'A')) == T));
}
}
/**
Returns the corresponding signed value for $(D x) (e.g. if $(D x) has type
$(D uint), it returns $(D cast(int) x)). The advantage compared to the cast
is that you do not need to rewrite the cast if $(D x) later changes type
(e.g from $(D uint) to $(D ulong)).
Note that the result is always mutable even if the original type was const
or immutable. In order to retain the constness, use $(XREF traits, Signed).
*/
auto signed(T)(T x) if (isIntegral!T)
{
return cast(Unqual!(Signed!T))x;
}
///
unittest
{
uint u = 42;
auto s1 = unsigned(u); //not qualified
Unsigned!(typeof(u)) s2 = unsigned(u); //same qualification
immutable s3 = unsigned(u); //totally qualified
}
unittest
{
foreach(T; TypeTuple!(byte, ubyte))
{
static assert(is(typeof(signed(cast(T)1)) == byte));
static assert(is(typeof(signed(cast(const T)1)) == byte));
static assert(is(typeof(signed(cast(immutable T)1)) == byte));
}
foreach(T; TypeTuple!(short, ushort))
{
static assert(is(typeof(signed(cast(T)1)) == short));
static assert(is(typeof(signed(cast(const T)1)) == short));
static assert(is(typeof(signed(cast(immutable T)1)) == short));
}
foreach(T; TypeTuple!(int, uint))
{
static assert(is(typeof(signed(cast(T)1)) == int));
static assert(is(typeof(signed(cast(const T)1)) == int));
static assert(is(typeof(signed(cast(immutable T)1)) == int));
}
foreach(T; TypeTuple!(long, ulong))
{
static assert(is(typeof(signed(cast(T)1)) == long));
static assert(is(typeof(signed(cast(const T)1)) == long));
static assert(is(typeof(signed(cast(immutable T)1)) == long));
}
}

View file

@ -154,7 +154,7 @@ $(I Integer):
$(I Digit):
$(B '0')|$(B '1')|$(B '2')|$(B '3')|$(B '4')|$(B '5')|$(B '6')|$(B '7')|$(B '8')|$(B '9')
$(I FormatChar):
$(B 's')|$(B 'b')|$(B 'd')|$(B 'o')|$(B 'x')|$(B 'X')|$(B 'e')|$(B 'E')|$(B 'f')|$(B 'F')|$(B 'g')|$(B 'G')|$(B 'a')|$(B 'A')
$(B 's')|$(B 'c')|$(B 'b')|$(B 'd')|$(B 'o')|$(B 'x')|$(B 'X')|$(B 'e')|$(B 'E')|$(B 'f')|$(B 'F')|$(B 'g')|$(B 'G')|$(B 'a')|$(B 'A')
)
$(BOOKTABLE Flags affect formatting depending on the specifier as

View file

@ -801,8 +801,8 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType
if(isUnsigned!UIntType)
{
static assert(bits == 32 || bits == 64 || bits == 96 || bits == 128 || bits == 160 || bits == 192,
"Supporting bits are 32, 64, 96, 128, 160 and 192. " ~ to!string(bits) ~ " is not supported.");
"Xorshift supports only 32, 64, 96, 128, 160 and 192 bit versions. "
~ to!string(bits) ~ " is not supported.");
public:
///Mark this as a Rng
@ -828,11 +828,16 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType
UIntType[size] seeds_ = [123456789, 362436069, 521288629, 88675123];
else static if (bits == 160)
UIntType[size] seeds_ = [123456789, 362436069, 521288629, 88675123, 5783321];
else
{ // 192bits
else static if (bits == 192)
{
UIntType[size] seeds_ = [123456789, 362436069, 521288629, 88675123, 5783321, 6615241];
UIntType value_;
}
else
{
static assert(false, "Phobos Error: Xorshift has no instantiation rule for "
~ to!string(bits) ~ " bits.");
}
public:
@ -887,7 +892,7 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType
static if (bits == 32)
{
temp = seeds_[0] ^ (seeds_[0] << a);
temp = temp >> b;
temp = temp ^ (temp >> b);
seeds_[0] = temp ^ (temp << c);
}
else static if (bits == 64)
@ -913,15 +918,15 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType
}
else static if (bits == 160)
{
temp = seeds_[0] ^ (seeds_[0] >> a);
temp = seeds_[0] ^ (seeds_[0] << a);
seeds_[0] = seeds_[1];
seeds_[1] = seeds_[2];
seeds_[2] = seeds_[3];
seeds_[3] = seeds_[4];
seeds_[4] = seeds_[4] ^ (seeds_[4] >> c) ^ temp ^ (temp >> b);
}
else
{ // 192bits
else static if (bits == 192)
{
temp = seeds_[0] ^ (seeds_[0] >> a);
seeds_[0] = seeds_[1];
seeds_[1] = seeds_[2];
@ -930,6 +935,11 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType
seeds_[4] = seeds_[4] ^ (seeds_[4] << c) ^ temp ^ (temp << b);
value_ = seeds_[4] + (seeds_[5] += 362437);
}
else
{
static assert(false, "Phobos Error: Xorshift has no popFront() update for "
~ to!string(bits) ~ " bits.");
}
}
@ -994,7 +1004,7 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType
* num = rnd.front; // different across runs
* -----
*/
alias XorshiftEngine!(uint, 32, 13, 17, 5) Xorshift32;
alias XorshiftEngine!(uint, 32, 13, 17, 15) Xorshift32;
alias XorshiftEngine!(uint, 64, 10, 13, 10) Xorshift64; /// ditto
alias XorshiftEngine!(uint, 96, 10, 5, 26) Xorshift96; /// ditto
alias XorshiftEngine!(uint, 128, 11, 8, 19) Xorshift128; /// ditto
@ -1013,11 +1023,11 @@ unittest
// Result from reference implementation.
auto checking = [
[2463534242UL, 267649, 551450, 53765, 108832, 215250, 435468, 860211, 660133, 263375],
[2463534242UL, 901999875, 3371835698, 2675058524, 1053936272, 3811264849, 472493137, 3856898176, 2131710969, 2312157505],
[362436069UL, 2113136921, 19051112, 3010520417, 951284840, 1213972223, 3173832558, 2611145638, 2515869689, 2245824891],
[521288629UL, 1950277231, 185954712, 1582725458, 3580567609, 2303633688, 2394948066, 4108622809, 1116800180, 3357585673],
[88675123UL, 3701687786, 458299110, 2500872618, 3633119408, 516391518, 2377269574, 2599949379, 717229868, 137866584],
[5783321UL, 93724048, 491642011, 136638118, 246438988, 238186808, 140181925, 533680092, 285770921, 462053907],
[5783321UL, 393427209, 1947109840, 565829276, 1006220149, 971147905, 1436324242, 2800460115, 1484058076, 3823330032],
[0UL, 246875399, 3690007200, 1264581005, 3906711041, 1866187943, 2481925219, 2464530826, 1604040631, 3653403911]
];
@ -1131,7 +1141,7 @@ take $(D urng) uses the default generator $(D rndGen).
Example:
----
Random gen(unpredictableSeed);
auto gen = Random(unpredictableSeed);
// Generate an integer in [0, 1023]
auto a = uniform(0, 1024, gen);
// Generate a float in [0, 1$(RPAREN)

View file

@ -1987,6 +1987,70 @@ public struct Regex(Char)
+/
@property bool empty() const nothrow { return ir is null; }
/++
A range of all the named captures in the regex.
Example:
----
import std.range;
import std.algorithm;
auto re = regex(`(?P<name>\w+) = (?P<var>\d+)`);
auto nc = re.namedCaptures;
static assert(isRandomAccessRange!(typeof(nc)));
assert(!nc.empty);
assert(nc.length == 2);
assert(nc.equal(["name", "var"]));
assert(nc[0] == "name");
assert(nc[1..$].equal(["var"]));
----
+/
@property auto namedCaptures()
{
static struct NamedGroupRange
{
private:
NamedGroup[] groups;
size_t start;
size_t end;
public:
this(NamedGroup[] g, size_t s, size_t e)
{
assert(s <= e);
assert(e <= g.length);
groups = g;
start = s;
end = e;
}
@property string front() { return groups[start].name; };
@property string back() { return groups[end-1].name; }
@property bool empty() { return start >= end; }
@property size_t length() { return end - start; }
alias length opDollar;
@property NamedGroupRange save()
{
return NamedGroupRange(groups, start, end);
};
void popFront() { assert(!empty); start++; }
void popBack() { assert(!empty); end--; }
string opIndex()(size_t i)
{
assert(start + i < end,
"Requested named group is out of range.");
return groups[start+i].name;
}
NamedGroupRange opSlice(size_t low, size_t high) {
assert(low <= high);
assert(start + high <= end);
return NamedGroupRange(groups, start + low, start + high);
}
NamedGroupRange opSlice() { return this.save; }
}
return NamedGroupRange(dict, 0, dict.length);
}
///
private:
NamedGroup[] dict; //maps name -> user group number
uint ngroup; //number of internal groups
@ -2154,6 +2218,38 @@ private:
}
}
unittest
{
auto re = regex(`(?P<name>\w+) = (?P<var>\d+)`);
auto nc = re.namedCaptures;
static assert(isRandomAccessRange!(typeof(nc)));
assert(!nc.empty);
assert(nc.length == 2);
assert(nc.equal(["name", "var"]));
assert(nc[0] == "name");
assert(nc[1..$].equal(["var"]));
re = regex(`(\w+) (?P<named>\w+) (\w+)`);
nc = re.namedCaptures;
assert(nc.length == 1);
assert(nc[0] == "named");
assert(nc.front == "named");
assert(nc.back == "named");
re = regex(`(\w+) (\w+)`);
nc = re.namedCaptures;
assert(nc.empty);
re = regex(`(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/`);
nc = re.namedCaptures;
auto cp = nc.save;
assert(nc.equal(cp));
nc.popFront();
assert(nc.equal(cp[1..$]));
nc.popBack();
assert(nc.equal(cp[1..$-1]));
}
//
@trusted uint lookupNamedGroup(String)(NamedGroup[] dict, String name)
{//equal is @system?

View file

@ -810,30 +810,32 @@ Throws: $(D Exception) if the file is not opened.
}
/**
Read line from stream $(D fp) and return it as a specified type.
Read line from the file handle and return it as a specified type.
This version manages its own read buffer, which means one memory allocation per call. If you are not
retaining a reference to the read data, consider the $(D File.readln(buf)) version, which may offer
better performance as it reuses its read buffer.
better performance as it can reuse its read buffer.
Params:
S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to $(D string)
S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to $(D string).
terminator = line terminator (by default, '\n')
Returns:
The line that was read, including the line terminator character.
Throws:
$(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
Example:
---
// Reads $(D stdin) and writes it to $(D stdout).
import std.stdio;
int main()
void main()
{
string buf;
while ((buf = readln()) !is null)
write(buf);
return 0;
string line;
while ((line = stdin.readln()) !is null)
write(line);
}
---
*/
@ -842,7 +844,7 @@ int main()
{
Unqual!(ElementEncodingType!S)[] buf;
readln(buf, terminator);
return assumeUnique(buf);
return cast(S)buf;
}
unittest
@ -850,13 +852,13 @@ int main()
auto deleteme = testFilename();
std.file.write(deleteme, "hello\nworld\n");
scope(exit) std.file.remove(deleteme);
foreach (C; Tuple!(char, wchar, dchar).Types)
foreach (String; TypeTuple!(string, char[], wstring, wchar[], dstring, dchar[]))
{
auto witness = [ "hello\n", "world\n" ];
auto f = File(deleteme);
uint i = 0;
immutable(C)[] buf;
while ((buf = f.readln!(typeof(buf))()).length)
String buf;
while ((buf = f.readln!String()).length)
{
assert(i < witness.length);
assert(equal(buf, witness[i++]));
@ -866,17 +868,17 @@ int main()
}
/**
Read line from stream $(D fp) and write it to $(D buf[]), including
Read line from the file handle and write it to $(D buf[]), including
terminating character.
This is often faster than $(D buf = File.readln()) because the buffer
is reused each call. Note that reusing the buffer means that the
previous contents of it has to be copied if needed.
This can be faster than $(D line = File.readln()) because you can reuse
the buffer for each call. Note that reusing the buffer means that you
must copy the previous contents if you wish to retain them.
Params:
fp = input stream
buf = buffer used to store the resulting line data. buf is
resized as necessary.
terminator = line terminator (by default, '\n')
Returns:
0 for end of file, otherwise number of characters read
@ -886,35 +888,31 @@ conversion error.
Example:
---
// Reads $(D stdin) into a buffer
// Dumps the buffer to $(D stdout) when it gets a "q"
// Read lines from $(D stdin) into a string
// Ignore lines starting with '#'
// Write the string to $(D stdout)
int main()
void main()
{
string[] outBuf;
string buf;
string output;
char[] buf;
while (stdin.readln(buf))
{
if (buf[0] == 'q')
break;
if (buf[0] == '#')
continue;
outBuf ~= buf.idup;
output ~= buf;
}
foreach (line; outBuf)
{
write(line);
}
return 0;
write(output);
}
---
This method is more efficient than the one in the previous example
This method can be more efficient than the one in the previous example
because $(D stdin.readln(buf)) reuses (if possible) memory allocated
by $(D buf), whereas $(D buf = stdin.readln()) makes a new memory allocation
with every line.
for $(D buf), whereas $(D line = stdin.readln()) makes a new memory allocation
for every line.
*/
size_t readln(C)(ref C[] buf, dchar terminator = '\n')
if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
@ -1048,42 +1046,10 @@ Returns the file number corresponding to this object.
return .fileno(cast(FILE*) _p.handle);
}
/**
/*
Range that reads one line at a time. Returned by $(LREF byLine).
Allows to directly use range operations on lines of a file.
Example:
----
import std.algorithm, std.string, std.stdio;
// Count words in a file using ranges.
void main()
{
auto file = File("file.txt"); // Open for reading
const wordCount = file.byLine() // Read lines
.map!split // Split into words
.map!(a => a.length) // Count words per line
.reduce!((a, b) => a + b); // Total word count
writeln(wordCount);
}
----
Example:
----
import std.stdio;
// Count lines in file using a foreach
void main()
{
auto file = File("file.txt"); // open for reading
ulong lineCount = 0;
foreach (line; file.byLine())
{
++lineCount;
}
writeln("Lines in file: ", lineCount);
}
----
*/
struct ByLine(Char, Terminator)
{
@ -1155,13 +1121,56 @@ void main()
}
/**
Convenience function that returns the $(D LinesReader) corresponding
to this file. */
ByLine!(Char, Terminator) byLine(Terminator = char, Char = char)
Returns an input range set up to read from the file handle one line
at a time.
The element type for the range will be $(D Char[]).
Params:
Char = Character type for each line, defaulting to $(D char). If
Char is mutable then each $(D front) will not persist after $(D
popFront) is called, so the caller must copy its contents (e.g. by
calling $(D to!string)) if retention is needed.
keepTerminator = Use $(D KeepTerminator.yes) to include the
terminator at the end of each line.
terminator = Line separator ($(D '\n') by default).
Example:
----
import std.algorithm, std.stdio, std.string;
// Count words in a file using ranges.
void main()
{
auto file = File("file.txt"); // Open for reading
const wordCount = file.byLine() // Read lines
.map!split // Split into words
.map!(a => a.length) // Count words per line
.reduce!((a, b) => a + b); // Total word count
writeln(wordCount);
}
----
Example:
----
import std.stdio;
// Count lines in file using a foreach
void main()
{
auto file = File("file.txt"); // open for reading
ulong lineCount = 0;
foreach (line; file.byLine())
{
++lineCount;
}
writeln("Lines in file: ", lineCount);
}
----
*/
auto byLine(Terminator = char, Char = char)
(KeepTerminator keepTerminator = KeepTerminator.no,
Terminator terminator = '\n')
{
return typeof(return)(this, keepTerminator, terminator);
return ByLine!(Char, Terminator)(this, keepTerminator, terminator);
}
unittest
@ -1972,35 +1981,65 @@ unittest
}
/**********************************
* Read line from stream $(D fp).
* Read line from $(D stdin).
*
* This version manages its own read buffer, which means one memory allocation per call. If you are not
* retaining a reference to the read data, consider the $(D readln(buf)) version, which may offer
* better performance as it can reuse its read buffer.
*
* Returns:
* $(D null) for end of file,
* $(D char[]) for line read from $(D fp), including terminating character
* The line that was read, including the line terminator character.
* Params:
* $(D fp) = input stream
* $(D terminator) = line terminator, '\n' by default
* S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to $(D string).
* terminator = line terminator (by default, '\n')
* Throws:
* $(D StdioException) on error
* $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
* Example:
* Reads $(D stdin) and writes it to $(D stdout).
---
import std.stdio;
int main()
void main()
{
string buf;
while ((buf = stdin.readln()) !is null)
write(buf);
return 0;
string line;
while ((line = readln()) !is null)
write(line);
}
---
*/
S readln(S = string)(dchar terminator = '\n')
if (isSomeString!S)
{
return stdin.readln(terminator);
return stdin.readln!S(terminator);
}
/** ditto */
/**********************************
* Read line from $(D stdin) and write it to buf[], including terminating character.
*
* This can be faster than $(D line = readln()) because you can reuse
* the buffer for each call. Note that reusing the buffer means that you
* must copy the previous contents if you wish to retain them.
*
* Returns:
* $(D size_t) 0 for end of file, otherwise number of characters read
* Params:
* buf = Buffer used to store the resulting line data. buf is resized as necessary.
* terminator = line terminator (by default, '\n')
* Throws:
* $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
* Example:
* Reads $(D stdin) and writes it to $(D stdout).
---
import std.stdio;
void main()
{
char[] buf;
while (readln(buf))
write(buf);
}
---
*/
size_t readln(C)(ref C[] buf, dchar terminator = '\n')
if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
{
@ -2015,6 +2054,29 @@ if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
return stdin.readln(buf, terminator);
}
unittest
{
//we can't actually test readln, so at the very least,
//we test compilability
void foo()
{
readln();
readln('\t');
foreach (String; TypeTuple!(string, char[], wstring, wchar[], dstring, dchar[]))
{
readln!String();
readln!String('\t');
}
foreach (String; TypeTuple!(char[], wchar[], dchar[]))
{
String buf;
readln(buf);
readln(buf, '\t');
readln(buf, "<br />");
}
}
}
/*
* Convenience function that forwards to $(D core.stdc.stdio.fopen)
* (to $(D _wfopen) on Windows)

View file

@ -5396,10 +5396,11 @@ template Unqual(T)
}
else // workaround
{
static if (is(T U == shared(const U))) alias U Unqual;
static if (is(T U == shared(inout U))) alias U Unqual;
else static if (is(T U == shared(const U))) alias U Unqual;
else static if (is(T U == inout U )) alias U Unqual;
else static if (is(T U == const U )) alias U Unqual;
else static if (is(T U == immutable U )) alias U Unqual;
else static if (is(T U == inout U )) alias U Unqual;
else static if (is(T U == shared U )) alias U Unqual;
else alias T Unqual;
}
@ -5407,12 +5408,13 @@ template Unqual(T)
unittest
{
static assert(is(Unqual!int == int));
static assert(is(Unqual!(const int) == int));
static assert(is(Unqual!(immutable int) == int));
static assert(is(Unqual!(inout int) == int));
static assert(is(Unqual!(shared int) == int));
static assert(is(Unqual!(shared(const int)) == int));
static assert(is(Unqual!( int) == int));
static assert(is(Unqual!( const int) == int));
static assert(is(Unqual!( inout int) == int));
static assert(is(Unqual!( immutable int) == int));
static assert(is(Unqual!( shared int) == int));
static assert(is(Unqual!(shared const int) == int));
static assert(is(Unqual!(shared inout int) == int));
alias immutable(int[]) ImmIntArr;
static assert(is(Unqual!ImmIntArr == immutable(int)[]));
}
@ -5420,7 +5422,9 @@ unittest
// [For internal use]
private template ModifyTypePreservingSTC(alias Modifier, T)
{
static if (is(T U == shared(const U))) alias shared(const Modifier!U) ModifyTypePreservingSTC;
static if (is(T U == shared(inout U))) alias shared(inout Modifier!U) ModifyTypePreservingSTC;
else static if (is(T U == shared(const U))) alias shared(const Modifier!U) ModifyTypePreservingSTC;
else static if (is(T U == inout U )) alias inout(Modifier!U) ModifyTypePreservingSTC;
else static if (is(T U == const U )) alias const(Modifier!U) ModifyTypePreservingSTC;
else static if (is(T U == immutable U )) alias immutable(Modifier!U) ModifyTypePreservingSTC;
else static if (is(T U == shared U )) alias shared(Modifier!U) ModifyTypePreservingSTC;
@ -5429,10 +5433,13 @@ private template ModifyTypePreservingSTC(alias Modifier, T)
unittest
{
static assert(is(ModifyTypePreservingSTC!(Intify, const real) == const int));
static assert(is(ModifyTypePreservingSTC!(Intify, immutable real) == immutable int));
static assert(is(ModifyTypePreservingSTC!(Intify, shared real) == shared int));
static assert(is(ModifyTypePreservingSTC!(Intify, shared(const real)) == shared(const int)));
static assert(is(ModifyTypePreservingSTC!(Intify, real) == int));
static assert(is(ModifyTypePreservingSTC!(Intify, shared real) == shared int));
static assert(is(ModifyTypePreservingSTC!(Intify, immutable real) == immutable int));
static assert(is(ModifyTypePreservingSTC!(Intify, const real) == const int));
static assert(is(ModifyTypePreservingSTC!(Intify, inout real) == inout int));
static assert(is(ModifyTypePreservingSTC!(Intify, shared const real) == shared const int));
static assert(is(ModifyTypePreservingSTC!(Intify, shared inout real) == shared inout int));
}
version (unittest) private template Intify(T) { alias int Intify; }
@ -5652,72 +5659,14 @@ unittest
static assert(is(S3 == immutable(int)));
}
/**
* Returns the corresponding unsigned value for $(D x), e.g. if $(D x)
* has type $(D int), returns $(D cast(uint) x). The advantage
* compared to the cast is that you do not need to rewrite the cast if
* $(D x) later changes type to e.g. $(D long).
*/
auto unsigned(T)(T x) if (isIntegral!T)
{
static if (is(Unqual!T == byte )) return cast(ubyte ) x;
else static if (is(Unqual!T == short)) return cast(ushort) x;
else static if (is(Unqual!T == int )) return cast(uint ) x;
else static if (is(Unqual!T == long )) return cast(ulong ) x;
else
{
static assert(T.min == 0, "Bug in either unsigned or isIntegral");
return cast(Unqual!T) x;
}
}
unittest
{
foreach(T; TypeTuple!(byte, ubyte))
{
static assert(is(typeof(unsigned(cast(T)1)) == ubyte));
static assert(is(typeof(unsigned(cast(const T)1)) == ubyte));
static assert(is(typeof(unsigned(cast(immutable T)1)) == ubyte));
}
// Remove import when unsigned is removed.
import std.conv;
foreach(T; TypeTuple!(short, ushort))
{
static assert(is(typeof(unsigned(cast(T)1)) == ushort));
static assert(is(typeof(unsigned(cast(const T)1)) == ushort));
static assert(is(typeof(unsigned(cast(immutable T)1)) == ushort));
}
// Purposefully undocumented. Will be removed in June 2014.
deprecated("unsigned has been moved to std.conv. Please adjust your imports accordingly.")
alias std.conv.unsigned unsigned;
foreach(T; TypeTuple!(int, uint))
{
static assert(is(typeof(unsigned(cast(T)1)) == uint));
static assert(is(typeof(unsigned(cast(const T)1)) == uint));
static assert(is(typeof(unsigned(cast(immutable T)1)) == uint));
}
foreach(T; TypeTuple!(long, ulong))
{
static assert(is(typeof(unsigned(cast(T)1)) == ulong));
static assert(is(typeof(unsigned(cast(const T)1)) == ulong));
static assert(is(typeof(unsigned(cast(immutable T)1)) == ulong));
}
}
auto unsigned(T)(T x) if (isSomeChar!T)
{
// All characters are unsigned
static assert(T.min == 0);
return cast(Unqual!T) x;
}
unittest
{
foreach(T; TypeTuple!(char, wchar, dchar))
{
static assert(is(typeof(unsigned(cast(T)'A')) == T));
static assert(is(typeof(unsigned(cast(const T)'A')) == T));
static assert(is(typeof(unsigned(cast(immutable T)'A')) == T));
}
}
/**
Returns the most negative value of the numeric type T.

487
std/uri.d
View file

@ -26,21 +26,21 @@
*/
module std.uri;
//debug=uri; // uncomment to turn on debugging printf's
//debug=uri; // uncomment to turn on debugging writefln's
debug(uri) private import std.stdio;
/* ====================== URI Functions ================ */
private import std.ascii;
private import std.c.stdlib;
private import std.utf;
private import std.stdio;
import std.exception;
class URIerror : Error
{
this()
{
super("URI error");
super("URI error");
}
}
@ -60,19 +60,18 @@ __gshared ubyte[128] uri_flags; // indexed by character
shared static this()
{
// Initialize uri_flags[]
static void helper(immutable char[] p, uint flags)
{ int i;
for (i = 0; i < p.length; i++)
uri_flags[p[i]] |= flags;
{
for (int i = 0; i < p.length; i++)
uri_flags[p[i]] |= flags;
}
uri_flags['#'] |= URI_Hash;
for (int i = 'A'; i <= 'Z'; i++)
{ uri_flags[i] |= URI_Alpha;
uri_flags[i + 0x20] |= URI_Alpha; // lowercase letters
{
uri_flags[i] |= URI_Alpha;
uri_flags[i + 0x20] |= URI_Alpha; // lowercase letters
}
helper("0123456789", URI_Digit);
helper(";/?:@&=+$,", URI_Reserved);
@ -101,110 +100,118 @@ private string URI_Encode(dstring string, uint unescapedSet)
for (k = 0; k != len; k++)
{
C = string[k];
// if (C in unescapedSet)
if (C < uri_flags.length && uri_flags[C] & unescapedSet)
{
if (Rlen == Rsize)
{ char* R2;
C = string[k];
// if (C in unescapedSet)
if (C < uri_flags.length && uri_flags[C] & unescapedSet)
{
if (Rlen == Rsize)
{
char* R2;
Rsize *= 2;
if (Rsize > 1024)
R2 = (new char[Rsize]).ptr;
Rsize *= 2;
if (Rsize > 1024) {
R2 = (new char[Rsize]).ptr;
}
else
{
R2 = cast(char *)alloca(Rsize * char.sizeof);
if (!R2)
goto LthrowURIerror;
}
R2[0..Rlen] = R[0..Rlen];
R = R2;
}
R[Rlen] = cast(char)C;
Rlen++;
}
else
{ R2 = cast(char *)alloca(Rsize * char.sizeof);
if (!R2)
goto LthrowURIerror;
}
R2[0..Rlen] = R[0..Rlen];
R = R2;
}
R[Rlen] = cast(char)C;
Rlen++;
}
else
{ char[6] Octet;
uint L;
{
char[6] Octet;
uint L;
V = C;
V = C;
// Transform V into octets
if (V <= 0x7F)
{
Octet[0] = cast(char) V;
L = 1;
}
else if (V <= 0x7FF)
{
Octet[0] = cast(char)(0xC0 | (V >> 6));
Octet[1] = cast(char)(0x80 | (V & 0x3F));
L = 2;
}
else if (V <= 0xFFFF)
{
Octet[0] = cast(char)(0xE0 | (V >> 12));
Octet[1] = cast(char)(0x80 | ((V >> 6) & 0x3F));
Octet[2] = cast(char)(0x80 | (V & 0x3F));
L = 3;
}
else if (V <= 0x1FFFFF)
{
Octet[0] = cast(char)(0xF0 | (V >> 18));
Octet[1] = cast(char)(0x80 | ((V >> 12) & 0x3F));
Octet[2] = cast(char)(0x80 | ((V >> 6) & 0x3F));
Octet[3] = cast(char)(0x80 | (V & 0x3F));
L = 4;
}
/+
else if (V <= 0x3FFFFFF)
{
Octet[0] = cast(char)(0xF8 | (V >> 24));
Octet[1] = cast(char)(0x80 | ((V >> 18) & 0x3F));
Octet[2] = cast(char)(0x80 | ((V >> 12) & 0x3F));
Octet[3] = cast(char)(0x80 | ((V >> 6) & 0x3F));
Octet[4] = cast(char)(0x80 | (V & 0x3F));
L = 5;
}
else if (V <= 0x7FFFFFFF)
{
Octet[0] = cast(char)(0xFC | (V >> 30));
Octet[1] = cast(char)(0x80 | ((V >> 24) & 0x3F));
Octet[2] = cast(char)(0x80 | ((V >> 18) & 0x3F));
Octet[3] = cast(char)(0x80 | ((V >> 12) & 0x3F));
Octet[4] = cast(char)(0x80 | ((V >> 6) & 0x3F));
Octet[5] = cast(char)(0x80 | (V & 0x3F));
L = 6;
}
+/
else
{ goto LthrowURIerror; // undefined UTF-32 code
}
// Transform V into octets
if (V <= 0x7F)
{
Octet[0] = cast(char) V;
L = 1;
}
else if (V <= 0x7FF)
{
Octet[0] = cast(char)(0xC0 | (V >> 6));
Octet[1] = cast(char)(0x80 | (V & 0x3F));
L = 2;
}
else if (V <= 0xFFFF)
{
Octet[0] = cast(char)(0xE0 | (V >> 12));
Octet[1] = cast(char)(0x80 | ((V >> 6) & 0x3F));
Octet[2] = cast(char)(0x80 | (V & 0x3F));
L = 3;
}
else if (V <= 0x1FFFFF)
{
Octet[0] = cast(char)(0xF0 | (V >> 18));
Octet[1] = cast(char)(0x80 | ((V >> 12) & 0x3F));
Octet[2] = cast(char)(0x80 | ((V >> 6) & 0x3F));
Octet[3] = cast(char)(0x80 | (V & 0x3F));
L = 4;
}
/+
else if (V <= 0x3FFFFFF)
{
Octet[0] = cast(char)(0xF8 | (V >> 24));
Octet[1] = cast(char)(0x80 | ((V >> 18) & 0x3F));
Octet[2] = cast(char)(0x80 | ((V >> 12) & 0x3F));
Octet[3] = cast(char)(0x80 | ((V >> 6) & 0x3F));
Octet[4] = cast(char)(0x80 | (V & 0x3F));
L = 5;
}
else if (V <= 0x7FFFFFFF)
{
Octet[0] = cast(char)(0xFC | (V >> 30));
Octet[1] = cast(char)(0x80 | ((V >> 24) & 0x3F));
Octet[2] = cast(char)(0x80 | ((V >> 18) & 0x3F));
Octet[3] = cast(char)(0x80 | ((V >> 12) & 0x3F));
Octet[4] = cast(char)(0x80 | ((V >> 6) & 0x3F));
Octet[5] = cast(char)(0x80 | (V & 0x3F));
L = 6;
}
+/
else
{
goto LthrowURIerror; // undefined UTF-32 code
}
if (Rlen + L * 3 > Rsize)
{ char *R2;
if (Rlen + L * 3 > Rsize)
{
char *R2;
Rsize = 2 * (Rlen + L * 3);
if (Rsize > 1024)
R2 = (new char[Rsize]).ptr;
else
{ R2 = cast(char *)alloca(Rsize * char.sizeof);
if (!R2)
goto LthrowURIerror;
}
R2[0..Rlen] = R[0..Rlen];
R = R2;
}
Rsize = 2 * (Rlen + L * 3);
if (Rsize > 1024) {
R2 = (new char[Rsize]).ptr;
}
else
{
R2 = cast(char *)alloca(Rsize * char.sizeof);
if (!R2)
goto LthrowURIerror;
}
R2[0..Rlen] = R[0..Rlen];
R = R2;
}
for (j = 0; j < L; j++)
{
R[Rlen] = '%';
R[Rlen + 1] = hex2ascii[Octet[j] >> 4];
R[Rlen + 2] = hex2ascii[Octet[j] & 15];
for (j = 0; j < L; j++)
{
R[Rlen] = '%';
R[Rlen + 1] = hex2ascii[Octet[j] >> 4];
R[Rlen + 2] = hex2ascii[Octet[j] & 15];
Rlen += 3;
Rlen += 3;
}
}
}
}
return R[0..Rlen].idup;
@ -215,8 +222,8 @@ LthrowURIerror:
uint ascii2hex(dchar c)
{
return (c <= '9') ? c - '0' :
(c <= 'F') ? c - 'A' + 10 :
c - 'a' + 10;
(c <= 'F') ? c - 'A' + 10 :
c - 'a' + 10;
}
private dstring URI_Decode(string string, uint reservedSet)
@ -226,8 +233,6 @@ private dstring URI_Decode(string string, uint reservedSet)
uint V;
dchar C;
//printf("URI_Decode('%.*s')\n", string);
// Result array, allocated on stack
dchar* R;
uint Rlen;
@ -237,86 +242,91 @@ private dstring URI_Decode(string string, uint reservedSet)
// Preallocate result buffer R guaranteed to be large enough for result
auto Rsize = len;
if (Rsize > 1024 / dchar.sizeof)
R = (new dchar[Rsize]).ptr;
if (Rsize > 1024 / dchar.sizeof) {
R = (new dchar[Rsize]).ptr;
}
else
{ R = cast(dchar *)alloca(Rsize * dchar.sizeof);
if (!R)
goto LthrowURIerror;
{
R = cast(dchar *)alloca(Rsize * dchar.sizeof);
if (!R)
goto LthrowURIerror;
}
Rlen = 0;
for (k = 0; k != len; k++)
{ char B;
uint start;
C = s[k];
if (C != '%')
{ R[Rlen] = C;
Rlen++;
continue;
}
start = k;
if (k + 2 >= len)
goto LthrowURIerror;
if (!isHexDigit(s[k + 1]) || !isHexDigit(s[k + 2]))
goto LthrowURIerror;
B = cast(char)((ascii2hex(s[k + 1]) << 4) + ascii2hex(s[k + 2]));
k += 2;
if ((B & 0x80) == 0)
{
C = B;
}
else
{ uint n;
char B;
uint start;
for (n = 1; ; n++)
C = s[k];
if (C != '%')
{
if (n > 4)
goto LthrowURIerror;
if (((B << n) & 0x80) == 0)
{
if (n == 1)
goto LthrowURIerror;
break;
R[Rlen] = C;
Rlen++;
continue;
}
}
// Pick off (7 - n) significant bits of B from first byte of octet
V = B & ((1 << (7 - n)) - 1); // (!!!)
if (k + (3 * (n - 1)) >= len)
goto LthrowURIerror;
for (j = 1; j != n; j++)
{
k++;
if (s[k] != '%')
start = k;
if (k + 2 >= len)
goto LthrowURIerror;
if (!isHexDigit(s[k + 1]) || !isHexDigit(s[k + 2]))
goto LthrowURIerror;
B = cast(char)((ascii2hex(s[k + 1]) << 4) + ascii2hex(s[k + 2]));
if ((B & 0xC0) != 0x80)
goto LthrowURIerror;
k += 2;
V = (V << 6) | (B & 0x3F);
if ((B & 0x80) == 0)
{
C = B;
}
else
{
uint n;
for (n = 1; ; n++)
{
if (n > 4)
goto LthrowURIerror;
if (((B << n) & 0x80) == 0)
{
if (n == 1)
goto LthrowURIerror;
break;
}
}
// Pick off (7 - n) significant bits of B from first byte of octet
V = B & ((1 << (7 - n)) - 1); // (!!!)
if (k + (3 * (n - 1)) >= len)
goto LthrowURIerror;
for (j = 1; j != n; j++)
{
k++;
if (s[k] != '%')
goto LthrowURIerror;
if (!isHexDigit(s[k + 1]) || !isHexDigit(s[k + 2]))
goto LthrowURIerror;
B = cast(char)((ascii2hex(s[k + 1]) << 4) + ascii2hex(s[k + 2]));
if ((B & 0xC0) != 0x80)
goto LthrowURIerror;
k += 2;
V = (V << 6) | (B & 0x3F);
}
if (V > 0x10FFFF)
goto LthrowURIerror;
C = V;
}
if (C < uri_flags.length && uri_flags[C] & reservedSet)
{
// R ~= s[start .. k + 1];
int width = (k + 1) - start;
for (int ii = 0; ii < width; ii++)
R[Rlen + ii] = s[start + ii];
Rlen += width;
}
else
{
R[Rlen] = C;
Rlen++;
}
if (V > 0x10FFFF)
goto LthrowURIerror;
C = V;
}
if (C < uri_flags.length && uri_flags[C] & reservedSet)
{
// R ~= s[start .. k + 1];
int width = (k + 1) - start;
for (int ii = 0; ii < width; ii++)
R[Rlen + ii] = s[start + ii];
Rlen += width;
}
else
{
R[Rlen] = C;
Rlen++;
}
}
assert(Rlen <= Rsize); // enforce our preallocation size guarantee
@ -392,39 +402,42 @@ size_t uriLength(string s)
size_t i;
if (s.length <= 4)
goto Lno;
goto Lno;
//writefln("isURL(%s)", s);
if (s.length > 7 && std.string.icmp(s[0 .. 7], "http://") == 0)
i = 7;
else if (s.length > 8 && std.string.icmp(s[0 .. 8], "https://") == 0)
i = 8;
// if (icmp(s[0 .. 4], "www.") == 0)
// i = 4;
if (s.length > 7 && std.string.icmp(s[0 .. 7], "http://") == 0) {
i = 7;
}
else
goto Lno;
{
if (s.length > 8 && std.string.icmp(s[0 .. 8], "https://") == 0)
i = 8;
else
goto Lno;
}
// if (icmp(s[0 .. 4], "www.") == 0)
// i = 4;
size_t lastdot;
for (; i < s.length; i++)
{
auto c = s[i];
if (isAlphaNum(c))
continue;
if (c == '-' || c == '_' || c == '?' ||
c == '=' || c == '%' || c == '&' ||
c == '/' || c == '+' || c == '#' ||
c == '~' || c == '$')
continue;
if (c == '.')
{
lastdot = i;
continue;
}
break;
auto c = s[i];
if (isAlphaNum(c))
continue;
if (c == '-' || c == '_' || c == '?' ||
c == '=' || c == '%' || c == '&' ||
c == '/' || c == '+' || c == '#' ||
c == '~' || c == '$')
continue;
if (c == '.')
{
lastdot = i;
continue;
}
break;
}
//if (!lastdot || (i - lastdot != 3 && i - lastdot != 4))
if (!lastdot)
goto Lno;
goto Lno;
return i;
@ -432,6 +445,15 @@ Lno:
return -1;
}
unittest
{
string s1 = "http://www.digitalmars.com/~fred/fredsRX.html#foo end!";
assert (uriLength(s1) == 49);
string s2 = "no uri here";
assert (uriLength(s2) == -1);
}
/***************************
* Does string s[] start with an email address?
* Returns:
@ -441,46 +463,46 @@ Lno:
* RFC2822
*/
size_t emailLength(string s)
{ size_t i;
{
size_t i;
if (!isAlpha(s[0]))
goto Lno;
goto Lno;
for (i = 1; 1; i++)
{
if (i == s.length)
goto Lno;
auto c = s[i];
if (isAlphaNum(c))
continue;
if (c == '-' || c == '_' || c == '.')
continue;
if (c != '@')
goto Lno;
i++;
break;
if (i == s.length)
goto Lno;
auto c = s[i];
if (isAlphaNum(c))
continue;
if (c == '-' || c == '_' || c == '.')
continue;
if (c != '@')
goto Lno;
i++;
break;
}
//writefln("test1 '%s'", s[0 .. i]);
/* Now do the part past the '@'
*/
size_t lastdot;
for (; i < s.length; i++)
{
auto c = s[i];
if (isAlphaNum(c))
continue;
if (c == '-' || c == '_')
continue;
if (c == '.')
{
lastdot = i;
continue;
}
break;
auto c = s[i];
if (isAlphaNum(c))
continue;
if (c == '-' || c == '_')
continue;
if (c == '.')
{
lastdot = i;
continue;
}
break;
}
if (!lastdot || (i - lastdot != 3 && i - lastdot != 4))
goto Lno;
goto Lno;
return i;
@ -488,34 +510,41 @@ Lno:
return -1;
}
unittest
{
string s1 = "my.e-mail@www.example-domain.com with garbage added";
assert (emailLength(s1) == 32);
string s2 = "no email address here";
assert (emailLength(s2) == -1);
}
unittest
{
debug(uri) printf("uri.encodeURI.unittest\n");
debug(uri) writeln("uri.encodeURI.unittest");
string s = "http://www.digitalmars.com/~fred/fred's RX.html#foo";
string t = "http://www.digitalmars.com/~fred/fred's%20RX.html#foo";
auto r = encode(s);
debug(uri) printf("r = '%.*s'\n", r);
debug(uri) writefln("r = '%s'", r);
assert(r == t);
r = decode(t);
debug(uri) printf("r = '%.*s'\n", r);
debug(uri) writefln("r = '%s'", r);
assert(r == s);
r = encode( decode("%E3%81%82%E3%81%82") );
assert(r == "%E3%81%82%E3%81%82");
r = encodeComponent("c++");
//printf("r = '%.*s'\n", r);
assert(r == "c%2B%2B");
auto str = new char[10_000_000];
str[] = 'A';
r = encodeComponent(assumeUnique(str));
foreach (char c; r)
assert(c == 'A');
assert(c == 'A');
r = decode("%41%42%43");
debug(uri) writefln(r);
debug(uri) writeln(r);
}

View file

@ -787,6 +787,12 @@ public:
cast(void*) &temp);
}
// workaround for bug 10567 fix
int opCmp(ref const VariantN rhs) const
{
return (cast()this).opCmp!(VariantN)(cast()rhs);
}
/**
* Ordering comparison used by the "<", "<=", ">", and ">="
* operators. In case comparison is not sensible between the held