Merge pull request #5547 from MartinNowak/merge_stable

Merge remote-tracking branch 'upstream/stable' into merge_stable
merged-on-behalf-of: Jack Stouffer <jack@jackstouffer.com>
This commit is contained in:
The Dlang Bot 2017-07-08 19:09:17 +02:00 committed by GitHub
commit 0b6b743b5b
2 changed files with 152 additions and 45 deletions

View file

@ -460,9 +460,9 @@ interface ISharedAllocator
} }
private shared ISharedAllocator _processAllocator; private shared ISharedAllocator _processAllocator;
IAllocator _threadAllocator; private IAllocator _threadAllocator;
static this() private IAllocator setupThreadAllocator() nothrow @nogc @safe
{ {
/* /*
Forwards the `_threadAllocator` calls to the `processAllocator` Forwards the `_threadAllocator` calls to the `processAllocator`
@ -538,7 +538,8 @@ static this()
assert(!_threadAllocator); assert(!_threadAllocator);
import std.conv : emplace; import std.conv : emplace;
static ulong[stateSize!(ThreadAllocator).divideRoundUp(ulong.sizeof)] _threadAllocatorState; static ulong[stateSize!(ThreadAllocator).divideRoundUp(ulong.sizeof)] _threadAllocatorState;
_threadAllocator = emplace!(ThreadAllocator)(_threadAllocatorState[]); _threadAllocator = () @trusted { return emplace!(ThreadAllocator)(_threadAllocatorState[]); } ();
return _threadAllocator;
} }
/** /**
@ -550,7 +551,8 @@ in turn uses the garbage collected heap.
*/ */
nothrow @safe @nogc @property IAllocator theAllocator() nothrow @safe @nogc @property IAllocator theAllocator()
{ {
return _threadAllocator; auto p = _threadAllocator;
return p !is null ? p : setupThreadAllocator();
} }
/// Ditto /// Ditto

View file

@ -704,15 +704,20 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
{ {
import std.ascii : isWhite, isDigit, isHexDigit, toUpper, toLower; import std.ascii : isWhite, isDigit, isHexDigit, toUpper, toLower;
import std.typecons : Yes; import std.typecons : Yes;
import std.utf : encode;
JSONValue root; JSONValue root;
root.type_tag = JSON_TYPE.NULL; root.type_tag = JSON_TYPE.NULL;
// Avoid UTF decoding when possible, as it is unnecessary when
// processing JSON.
static if (is(T : const(char)[]))
alias Char = char;
else
alias Char = Unqual!(ElementType!T);
if (json.empty) return root; if (json.empty) return root;
int depth = -1; int depth = -1;
dchar next = 0; Char next = 0;
int line = 1, pos = 0; int line = 1, pos = 0;
void error(string msg) void error(string msg)
@ -720,11 +725,19 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
throw new JSONException(msg, line, pos); throw new JSONException(msg, line, pos);
} }
dchar popChar() Char popChar()
{ {
if (json.empty) error("Unexpected end of data."); if (json.empty) error("Unexpected end of data.");
dchar c = json.front; static if (is(T : const(char)[]))
{
Char c = json[0];
json = json[1..$];
}
else
{
Char c = json.front;
json.popFront(); json.popFront();
}
if (c == '\n') if (c == '\n')
{ {
@ -739,7 +752,7 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
return c; return c;
} }
dchar peekChar() Char peekChar()
{ {
if (!next) if (!next)
{ {
@ -754,11 +767,11 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
while (isWhite(peekChar())) next = 0; while (isWhite(peekChar())) next = 0;
} }
dchar getChar(bool SkipWhitespace = false)() Char getChar(bool SkipWhitespace = false)()
{ {
static if (SkipWhitespace) skipWhitespace(); static if (SkipWhitespace) skipWhitespace();
dchar c; Char c;
if (next) if (next)
{ {
c = next; c = next;
@ -791,8 +804,24 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
return true; return true;
} }
wchar parseWChar()
{
wchar val = 0;
foreach_reverse (i; 0 .. 4)
{
auto hex = toUpper(getChar());
if (!isHexDigit(hex)) error("Expecting hex character");
val += (isDigit(hex) ? hex - '0' : hex - ('A' - 10)) << (4 * i);
}
return val;
}
string parseString() string parseString()
{ {
import std.ascii : isControl;
import std.uni : isSurrogateHi, isSurrogateLo;
import std.utf : encode, decode;
auto str = appender!string(); auto str = appender!string();
Next: Next:
@ -816,13 +845,27 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
case 'r': str.put('\r'); break; case 'r': str.put('\r'); break;
case 't': str.put('\t'); break; case 't': str.put('\t'); break;
case 'u': case 'u':
dchar val = 0; wchar wc = parseWChar();
foreach_reverse (i; 0 .. 4) dchar val;
// Non-BMP characters are escaped as a pair of
// UTF-16 surrogate characters (see RFC 4627).
if (isSurrogateHi(wc))
{ {
auto hex = toUpper(getChar()); wchar[2] pair;
if (!isHexDigit(hex)) error("Expecting hex character"); pair[0] = wc;
val += (isDigit(hex) ? hex - '0' : hex - ('A' - 10)) << (4 * i); if (getChar() != '\\') error("Expected escaped low surrogate after escaped high surrogate");
if (getChar() != 'u') error("Expected escaped low surrogate after escaped high surrogate");
pair[1] = parseWChar();
size_t index = 0;
val = decode(pair[], index);
if (index != 2) error("Invalid escaped surrogate pair");
} }
else
if (isSurrogateLo(wc))
error(text("Unexpected low surrogate"));
else
val = wc;
char[4] buf; char[4] buf;
immutable len = encode!(Yes.useReplacementDchar)(buf, val); immutable len = encode!(Yes.useReplacementDchar)(buf, val);
str.put(buf[0 .. len]); str.put(buf[0 .. len]);
@ -834,8 +877,12 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
goto Next; goto Next;
default: default:
// RFC 7159 states that control characters U+0000 through
// U+001F must not appear unescaped in a JSON string.
auto c = getChar(); auto c = getChar();
appendJSONChar(str, c, options, &error); if (isControl(c))
error("Illegal control character.");
str.put(c);
goto Next; goto Next;
} }
@ -1096,11 +1143,11 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o
{ {
auto json = appender!string(); auto json = appender!string();
void toString(string str) @safe void toStringImpl(Char)(string str) @safe
{ {
json.put('"'); json.put('"');
foreach (dchar c; str) foreach (Char c; str)
{ {
switch (c) switch (c)
{ {
@ -1113,14 +1160,54 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o
case '\r': json.put("\\r"); break; case '\r': json.put("\\r"); break;
case '\t': json.put("\\t"); break; case '\t': json.put("\\t"); break;
default: default:
appendJSONChar(json, c, options, {
(msg) { throw new JSONException(msg); }); import std.ascii : isControl;
import std.utf : encode;
// Make sure we do UTF decoding iff we want to
// escape Unicode characters.
assert(((options & JSONOptions.escapeNonAsciiChars) != 0)
== is(Char == dchar));
with (JSONOptions) if (isControl(c) ||
((options & escapeNonAsciiChars) >= escapeNonAsciiChars && c >= 0x80))
{
// Ensure non-BMP characters are encoded as a pair
// of UTF-16 surrogate characters, as per RFC 4627.
wchar[2] wchars; // 1 or 2 UTF-16 code units
size_t wNum = encode(wchars, c); // number of UTF-16 code units
foreach (wc; wchars[0 .. wNum])
{
json.put("\\u");
foreach_reverse (i; 0 .. 4)
{
char ch = (wc >>> (4 * i)) & 0x0f;
ch += ch < 10 ? '0' : 'A' - 10;
json.put(ch);
}
}
}
else
{
json.put(c);
}
}
} }
} }
json.put('"'); json.put('"');
} }
void toString(string str) @safe
{
// Avoid UTF decoding when possible, as it is unnecessary when
// processing JSON.
if (options & JSONOptions.escapeNonAsciiChars)
toStringImpl!dchar(str);
else
toStringImpl!char(str);
}
void toValue(ref in JSONValue value, ulong indentLevel) @safe void toValue(ref in JSONValue value, ulong indentLevel) @safe
{ {
void putTabs(ulong additionalIndent = 0) void putTabs(ulong additionalIndent = 0)
@ -1281,28 +1368,6 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o
return json.data; return json.data;
} }
private void appendJSONChar(ref Appender!string dst, dchar c, JSONOptions opts,
scope void delegate(string) error) @safe
{
import std.uni : isControl;
with (JSONOptions) if (isControl(c) ||
((opts & escapeNonAsciiChars) >= escapeNonAsciiChars && c >= 0x80))
{
dst.put("\\u");
foreach_reverse (i; 0 .. 4)
{
char ch = (c >>> (4 * i)) & 0x0f;
ch += ch < 10 ? '0' : 'A' - 10;
dst.put(ch);
}
}
else
{
dst.put(c);
}
}
@safe unittest // bugzilla 12897 @safe unittest // bugzilla 12897
{ {
JSONValue jv0 = JSONValue("test测试"); JSONValue jv0 = JSONValue("test测试");
@ -1737,3 +1802,43 @@ pure nothrow @safe unittest // issue 15884
assert(test(minSub)); assert(test(minSub));
assert(test(3*minSub)); assert(test(3*minSub));
} }
@safe unittest // issue 17555
{
import std.exception : assertThrown;
assertThrown!JSONException(parseJSON("\"a\nb\""));
}
@safe unittest // issue 17556
{
auto v = JSONValue("\U0001D11E");
auto j = toJSON(v, false, JSONOptions.escapeNonAsciiChars);
assert(j == `"\uD834\uDD1E"`);
}
@safe unittest // issue 5904
{
string s = `"\uD834\uDD1E"`;
auto j = parseJSON(s);
assert(j.str == "\U0001D11E");
}
@safe unittest // issue 17557
{
assert(parseJSON("\"\xFF\"").str == "\xFF");
assert(parseJSON("\"\U0001D11E\"").str == "\U0001D11E");
}
@safe unittest // issue 17553
{
auto v = JSONValue("\xFF");
assert(toJSON(v) == "\"\xFF\"");
}
@safe unittest
{
import std.utf;
assert(parseJSON("\"\xFF\"".byChar).str == "\xFF");
assert(parseJSON("\"\U0001D11E\"".byChar).str == "\U0001D11E");
}