Got rid of most UTF-8 decoding that took a lot of time.

Removed unnecessary calls to Stream.available(),
again for grat speed gain. Also various small optimizations.
Overall, improved loading speed about 400%.
This commit is contained in:
Ferdinand Majerech 2011-10-24 00:46:35 +02:00
parent 9d68b6fa9a
commit 97693b4417
7 changed files with 159 additions and 164 deletions

View file

@ -344,9 +344,11 @@ YAMLMerge constructMerge(Mark start, Mark end, ref Node node)
///Construct a boolean node. ///Construct a boolean node.
bool constructBool(Mark start, Mark end, ref Node node) bool constructBool(Mark start, Mark end, ref Node node)
{ {
static yes = ["yes", "true", "on"];
static no = ["no", "false", "off"];
string value = node.as!string().toLower(); string value = node.as!string().toLower();
if(["yes", "true", "on"].canFind(value)) {return true;} if(yes.canFind(value)){return true;}
if(["no", "false", "off"].canFind(value)){return false;} if(no.canFind(value)) {return false;}
throw new Error("Unable to parse boolean value: " ~ value, start, end); throw new Error("Unable to parse boolean value: " ~ value, start, end);
} }

View file

@ -14,6 +14,7 @@ module dyaml.emitter;
import std.algorithm; import std.algorithm;
import std.array; import std.array;
import std.ascii; import std.ascii;
import std.container;
import std.conv; import std.conv;
import std.exception; import std.exception;
import std.format; import std.format;
@ -78,7 +79,7 @@ struct Emitter
Encoding encoding_ = Encoding.UTF_8; Encoding encoding_ = Encoding.UTF_8;
///Stack of states. ///Stack of states.
void delegate()[] states_; Array!(void delegate()) states_;
///Current state. ///Current state.
void delegate() state_; void delegate() state_;
@ -155,6 +156,7 @@ struct Emitter
in{assert(stream.writeable, "Can't emit YAML to a non-writable stream");} in{assert(stream.writeable, "Can't emit YAML to a non-writable stream");}
body body
{ {
states_.reserve(32);
stream_ = stream; stream_ = stream;
canonical_ = canonical; canonical_ = canonical;
state_ = &expectStreamStart; state_ = &expectStreamStart;
@ -171,7 +173,6 @@ struct Emitter
{ {
stream_ = null; stream_ = null;
clear(states_); clear(states_);
states_ = null;
clear(events_); clear(events_);
clear(indents_); clear(indents_);
indents_ = null; indents_ = null;
@ -201,8 +202,8 @@ struct Emitter
{ {
enforce(states_.length > 0, enforce(states_.length > 0,
new YAMLException("Emitter: Need to pop a state but there are no states left")); new YAMLException("Emitter: Need to pop a state but there are no states left"));
const result = states_.back(); const result = states_.back;
states_.popBack; states_.length = states_.length - 1;
return result; return result;
} }
@ -266,20 +267,12 @@ struct Emitter
while(!events_.iterationOver()) while(!events_.iterationOver())
{ {
immutable event = events_.next(); immutable event = events_.next();
if([EventID.DocumentStart, EventID.SequenceStart, static starts = [EventID.DocumentStart, EventID.SequenceStart, EventID.MappingStart];
EventID.MappingStart].canFind(event.id)) static ends = [EventID.DocumentEnd, EventID.SequenceEnd, EventID.MappingEnd];
{ if(starts.canFind(event.id)) {++level;}
++level; else if(ends.canFind(event.id)) {--level;}
} else if(event.id == EventID.StreamStart){level = -1;}
else if([EventID.DocumentEnd, EventID.SequenceEnd,
EventID.MappingEnd].canFind(event.id))
{
--level;
}
else if(event.id == EventID.StreamStart)
{
level = -1;
}
if(level < 0) if(level < 0)
{ {
return false; return false;
@ -692,8 +685,8 @@ struct Emitter
uint length = 0; uint length = 0;
const id = event_.id; const id = event_.id;
const scalar = id == EventID.Scalar; const scalar = id == EventID.Scalar;
const collectionStart = [EventID.MappingStart, const collectionStart = id == EventID.MappingStart ||
EventID.SequenceStart].canFind(id); id == EventID.SequenceStart;
if((id == EventID.Alias || scalar || collectionStart) if((id == EventID.Alias || scalar || collectionStart)
&& !event_.anchor.isNull()) && !event_.anchor.isNull())
@ -815,8 +808,8 @@ struct Emitter
if(analysis_.flags.isNull){analysis_ = analyzeScalar(event_.value);} if(analysis_.flags.isNull){analysis_ = analyzeScalar(event_.value);}
const style = event_.scalarStyle; const style = event_.scalarStyle;
const invalidOrPlain = [ScalarStyle.Invalid, ScalarStyle.Plain].canFind(style); const invalidOrPlain = style == ScalarStyle.Invalid || style == ScalarStyle.Plain;
const block = [ScalarStyle.Literal, ScalarStyle.Folded].canFind(style); const block = style == ScalarStyle.Literal || style == ScalarStyle.Folded;
const singleQuoted = style == ScalarStyle.SingleQuoted; const singleQuoted = style == ScalarStyle.SingleQuoted;
const doubleQuoted = style == ScalarStyle.DoubleQuoted; const doubleQuoted = style == ScalarStyle.DoubleQuoted;
@ -880,7 +873,7 @@ struct Emitter
if(handle.length > 1) foreach(const dchar c; handle[1 .. $ - 1]) if(handle.length > 1) foreach(const dchar c; handle[1 .. $ - 1])
{ {
enforce(isAlphaNum(c) || "-_".canFind(c), enforce(isAlphaNum(c) || "-_"d.canFind(c),
new Error("Invalid character: " ~ to!string(c) ~ new Error("Invalid character: " ~ to!string(c) ~
" in tag handle " ~ handle)); " in tag handle " ~ handle));
} }
@ -901,7 +894,7 @@ struct Emitter
foreach(const size_t i, const dchar c; prefix) foreach(const size_t i, const dchar c; prefix)
{ {
const size_t idx = i + offset; const size_t idx = i + offset;
if(isAlphaNum(c) || "-;/?:@&=+$,_.!~*\'()[]%".canFind(c)) if(isAlphaNum(c) || "-;/?:@&=+$,_.!~*\'()[]%"d.canFind(c))
{ {
end = idx + 1; end = idx + 1;
continue; continue;
@ -947,7 +940,7 @@ struct Emitter
size_t end = 0; size_t end = 0;
foreach(const dchar c; suffix) foreach(const dchar c; suffix)
{ {
if(isAlphaNum(c) || "-;/?:@&=+$,_.~*\'()[]".canFind(c) || if(isAlphaNum(c) || "-;/?:@&=+$,_.~*\'()[]"d.canFind(c) ||
(c == '!' && handle != "!")) (c == '!' && handle != "!"))
{ {
++end; ++end;
@ -973,7 +966,7 @@ struct Emitter
const str = anchor.get; const str = anchor.get;
foreach(const dchar c; str) foreach(const dchar c; str)
{ {
enforce(isAlphaNum(c) || "-_".canFind(c), enforce(isAlphaNum(c) || "-_"d.canFind(c),
new Error("Invalid character: " ~ to!string(c) ~ " in anchor: " ~ str)); new Error("Invalid character: " ~ to!string(c) ~ " in anchor: " ~ str));
} }
return str; return str;
@ -1017,7 +1010,7 @@ struct Emitter
//Last character or followed by a whitespace. //Last character or followed by a whitespace.
bool followedByWhitespace = scalar.length == 1 || bool followedByWhitespace = scalar.length == 1 ||
" \t\0\n\r\u0085\u2028\u2029".canFind(scalar[1]); " \t\0\n\r\u0085\u2028\u2029"d.canFind(scalar[1]);
//The previous character is a space/break (false by default). //The previous character is a space/break (false by default).
bool previousSpace, previousBreak; bool previousSpace, previousBreak;
@ -1028,11 +1021,11 @@ struct Emitter
if(index == 0) if(index == 0)
{ {
//Leading indicators are special characters. //Leading indicators are special characters.
if("#,[]{}&*!|>\'\"%@`".canFind(c)) if("#,[]{}&*!|>\'\"%@`"d.canFind(c))
{ {
flowIndicators = blockIndicators = true; flowIndicators = blockIndicators = true;
} }
if("?:".canFind(c)) if("?:"d.canFind(c))
{ {
flowIndicators = true; flowIndicators = true;
if(followedByWhitespace){blockIndicators = true;} if(followedByWhitespace){blockIndicators = true;}
@ -1045,7 +1038,7 @@ struct Emitter
else else
{ {
//Some indicators cannot appear within a scalar as well. //Some indicators cannot appear within a scalar as well.
if(",?[]{}".canFind(c)){flowIndicators = true;} if(",?[]{}"d.canFind(c)){flowIndicators = true;}
if(c == ':') if(c == ':')
{ {
flowIndicators = true; flowIndicators = true;
@ -1058,7 +1051,7 @@ struct Emitter
} }
//Check for line breaks, special, and unicode characters. //Check for line breaks, special, and unicode characters.
if("\n\u0085\u2028\u2029".canFind(c)){lineBreaks = true;} if("\n\u0085\u2028\u2029"d.canFind(c)){lineBreaks = true;}
if(!(c == '\n' || (c >= '\x20' && c <= '\x7E')) && if(!(c == '\n' || (c >= '\x20' && c <= '\x7E')) &&
!((c == '\u0085' || (c >= '\xA0' && c <= '\uD7FF') || !((c == '\u0085' || (c >= '\xA0' && c <= '\uD7FF') ||
(c >= '\uE000' && c <= '\uFFFD')) && c != '\uFEFF')) (c >= '\uE000' && c <= '\uFFFD')) && c != '\uFEFF'))
@ -1075,7 +1068,7 @@ struct Emitter
previousSpace = true; previousSpace = true;
previousBreak = false; previousBreak = false;
} }
else if("\n\u0085\u2028\u2029".canFind(c)) else if("\n\u0085\u2028\u2029"d.canFind(c))
{ {
if(index == 0){leadingBreak = true;} if(index == 0){leadingBreak = true;}
if(index == scalar.length - 1){trailingBreak = true;} if(index == scalar.length - 1){trailingBreak = true;}
@ -1089,9 +1082,9 @@ struct Emitter
} }
//Prepare for the next character. //Prepare for the next character.
preceededByWhitespace = "\0\n\r\u0085\u2028\u2029 \t".canFind(c); preceededByWhitespace = "\0\n\r\u0085\u2028\u2029 \t"d.canFind(c);
followedByWhitespace = index + 2 >= scalar.length || followedByWhitespace = index + 2 >= scalar.length ||
"\0\n\r\u0085\u2028\u2029 \t".canFind(scalar[index + 2]); "\0\n\r\u0085\u2028\u2029 \t"d.canFind(scalar[index + 2]);
} }
with(analysis.flags) with(analysis.flags)
@ -1317,14 +1310,14 @@ struct ScalarWriter
} }
else if(breaks_) else if(breaks_)
{ {
if(!"\n\u0085\u2028\u2029".canFind(c)) if(!"\n\u0085\u2028\u2029"d.canFind(c))
{ {
writeStartLineBreak(); writeStartLineBreak();
writeLineBreaks(); writeLineBreaks();
emitter_.writeIndent(); emitter_.writeIndent();
} }
} }
else if((c == dcharNone || "\' \n\u0085\u2028\u2029".canFind(c)) else if((c == dcharNone || "\' \n\u0085\u2028\u2029"d.canFind(c))
&& startChar_ < endChar_) && startChar_ < endChar_)
{ {
writeCurrentRange(Flag!"UpdateColumn".yes); writeCurrentRange(Flag!"UpdateColumn".yes);
@ -1350,7 +1343,7 @@ struct ScalarWriter
{ {
const dchar c = nextChar(); const dchar c = nextChar();
//handle special characters //handle special characters
if(c == dcharNone || "\"\\\u0085\u2028\u2029\uFEFF".canFind(c) || if(c == dcharNone || "\"\\\u0085\u2028\u2029\uFEFF"d.canFind(c) ||
!((c >= '\x20' && c <= '\x7E') || !((c >= '\x20' && c <= '\x7E') ||
((c >= '\xA0' && c <= '\uD7FF') || (c >= '\uE000' && c <= '\uFFFD')))) ((c >= '\xA0' && c <= '\uD7FF') || (c >= '\uE000' && c <= '\uFFFD'))))
{ {
@ -1417,7 +1410,7 @@ struct ScalarWriter
const dchar c = nextChar(); const dchar c = nextChar();
if(breaks_) if(breaks_)
{ {
if(!"\n\u0085\u2028\u2029".canFind(c)) if(!"\n\u0085\u2028\u2029"d.canFind(c))
{ {
if(!leadingSpace && c != dcharNone && c != ' ') if(!leadingSpace && c != dcharNone && c != ' ')
{ {
@ -1440,7 +1433,7 @@ struct ScalarWriter
writeCurrentRange(Flag!"UpdateColumn".yes); writeCurrentRange(Flag!"UpdateColumn".yes);
} }
} }
else if(c == dcharNone || " \n\u0085\u2028\u2029".canFind(c)) else if(c == dcharNone || " \n\u0085\u2028\u2029"d.canFind(c))
{ {
writeCurrentRange(Flag!"UpdateColumn".yes); writeCurrentRange(Flag!"UpdateColumn".yes);
if(c == dcharNone){emitter_.writeLineBreak();} if(c == dcharNone){emitter_.writeLineBreak();}
@ -1461,13 +1454,13 @@ struct ScalarWriter
const dchar c = nextChar(); const dchar c = nextChar();
if(breaks_) if(breaks_)
{ {
if(!"\n\u0085\u2028\u2029".canFind(c)) if(!"\n\u0085\u2028\u2029"d.canFind(c))
{ {
writeLineBreaks(); writeLineBreaks();
if(c != dcharNone){emitter_.writeIndent();} if(c != dcharNone){emitter_.writeIndent();}
} }
} }
else if(c == dcharNone || "\n\u0085\u2028\u2029".canFind(c)) else if(c == dcharNone || "\n\u0085\u2028\u2029"d.canFind(c))
{ {
writeCurrentRange(Flag!"UpdateColumn".no); writeCurrentRange(Flag!"UpdateColumn".no);
if(c == dcharNone){emitter_.writeLineBreak();} if(c == dcharNone){emitter_.writeLineBreak();}
@ -1507,14 +1500,14 @@ struct ScalarWriter
} }
else if(breaks_) else if(breaks_)
{ {
if(!"\n\u0085\u2028\u2029".canFind(c)) if(!"\n\u0085\u2028\u2029"d.canFind(c))
{ {
writeStartLineBreak(); writeStartLineBreak();
writeLineBreaks(); writeLineBreaks();
writeIndent(Flag!"ResetSpace".yes); writeIndent(Flag!"ResetSpace".yes);
} }
} }
else if(c == dcharNone || " \n\u0085\u2028\u2029".canFind(c)) else if(c == dcharNone || " \n\u0085\u2028\u2029"d.canFind(c))
{ {
writeCurrentRange(Flag!"UpdateColumn".yes); writeCurrentRange(Flag!"UpdateColumn".yes);
} }
@ -1562,16 +1555,16 @@ struct ScalarWriter
const last = lastChar(text_, end); const last = lastChar(text_, end);
const secondLast = end > 0 ? lastChar(text_, end) : 0; const secondLast = end > 0 ? lastChar(text_, end) : 0;
if(" \n\u0085\u2028\u2029".canFind(text_[0])) if(" \n\u0085\u2028\u2029"d.canFind(text_[0]))
{ {
hints[hintsIdx++] = cast(char)('0' + bestIndent); hints[hintsIdx++] = cast(char)('0' + bestIndent);
} }
if(!"\n\u0085\u2028\u2029".canFind(last)) if(!"\n\u0085\u2028\u2029"d.canFind(last))
{ {
hints[hintsIdx++] = '-'; hints[hintsIdx++] = '-';
} }
else if(std.utf.count(text_) == 1 || else if(std.utf.count(text_) == 1 ||
"\n\u0085\u2028\u2029".canFind(secondLast)) "\n\u0085\u2028\u2029"d.canFind(secondLast))
{ {
hints[hintsIdx++] = '+'; hints[hintsIdx++] = '+';
} }
@ -1643,7 +1636,7 @@ struct ScalarWriter
void updateBreaks(in dchar c, in Flag!"UpdateSpaces" updateSpaces) void updateBreaks(in dchar c, in Flag!"UpdateSpaces" updateSpaces)
{ {
if(c == dcharNone){return;} if(c == dcharNone){return;}
breaks_ = "\n\u0085\u2028\u2029".canFind(c); breaks_ = "\n\u0085\u2028\u2029"d.canFind(c);
if(updateSpaces){spaces_ = c == ' ';} if(updateSpaces){spaces_ = c == ' ';}
} }

View file

@ -127,7 +127,7 @@ struct Loader
/** /**
* Construct a Loader to load YAML from a _stream. * Construct a Loader to load YAML from a _stream.
* *
* Params: stream = Stream to read from. Must be readable. * Params: stream = Stream to read from. Must be readable and seekable.
* *
* Throws: YAMLException if stream could not be read. * Throws: YAMLException if stream could not be read.
*/ */

View file

@ -666,11 +666,11 @@ struct Node
Node k3 = Node("13"); Node k3 = Node("13");
Node k4 = Node("14"); Node k4 = Node("14");
Node narray = Node(Value([n1, n2, n3, n4])); Node narray = Node([n1, n2, n3, n4]);
Node nmap = Node(Value([Pair(k1, n1), Node nmap = Node([Pair(k1, n1),
Pair(k2, n2), Pair(k2, n2),
Pair(k3, n3), Pair(k3, n3),
Pair(k4, n4)])); Pair(k4, n4)]);
assert(narray[0].as!int == 11); assert(narray[0].as!int == 11);
assert(null !is collectException(narray[42])); assert(null !is collectException(narray[42]));
@ -793,7 +793,7 @@ struct Node
Node n2 = Node(Value(cast(long)12)); Node n2 = Node(Value(cast(long)12));
Node n3 = Node(Value(cast(long)13)); Node n3 = Node(Value(cast(long)13));
Node n4 = Node(Value(cast(long)14)); Node n4 = Node(Value(cast(long)14));
Node narray = Node(Value([n1, n2, n3, n4])); Node narray = Node([n1, n2, n3, n4]);
int[] array, array2; int[] array, array2;
foreach(int value; narray) foreach(int value; narray)
@ -858,20 +858,20 @@ struct Node
alias Node.Value Value; alias Node.Value Value;
alias Node.Pair Pair; alias Node.Pair Pair;
Node n1 = Node(Value(cast(long)11)); Node n1 = Node(cast(long)11);
Node n2 = Node(Value(cast(long)12)); Node n2 = Node(cast(long)12);
Node n3 = Node(Value(cast(long)13)); Node n3 = Node(cast(long)13);
Node n4 = Node(Value(cast(long)14)); Node n4 = Node(cast(long)14);
Node k1 = Node(Value("11")); Node k1 = Node("11");
Node k2 = Node(Value("12")); Node k2 = Node("12");
Node k3 = Node(Value("13")); Node k3 = Node("13");
Node k4 = Node(Value("14")); Node k4 = Node("14");
Node nmap1 = Node(Value([Pair(k1, n1), Node nmap1 = Node([Pair(k1, n1),
Pair(k2, n2), Pair(k2, n2),
Pair(k3, n3), Pair(k3, n3),
Pair(k4, n4)])); Pair(k4, n4)]);
int[string] expected = ["11" : 11, int[string] expected = ["11" : 11,
"12" : 12, "12" : 12,
@ -884,10 +884,10 @@ struct Node
} }
assert(array == expected); assert(array == expected);
Node nmap2 = Node(Value([Pair(k1, Node(Value(cast(long)5))), Node nmap2 = Node([Pair(k1, Node(cast(long)5)),
Pair(k2, Node(Value(true))), Pair(k2, Node(true)),
Pair(k3, Node(Value(cast(real)1.0))), Pair(k3, Node(cast(real)1.0)),
Pair(k4, Node(Value("yarly")))])); Pair(k4, Node("yarly"))]);
foreach(string key, Node value; nmap2) foreach(string key, Node value; nmap2)
{ {
@ -1073,7 +1073,7 @@ struct Node
auto idx = findPair(index); auto idx = findPair(index);
if(idx >= 0) if(idx >= 0)
{ {
auto pairs = as!(Node.Pair[])(); auto pairs = get!(Node.Pair[])();
moveAll(pairs[idx + 1 .. $], pairs[idx .. $ - 1]); moveAll(pairs[idx + 1 .. $], pairs[idx .. $ - 1]);
pairs.length = pairs.length - 1; pairs.length = pairs.length - 1;
value_ = Value(pairs); value_ = Value(pairs);

View file

@ -122,7 +122,7 @@ final class Parser
///YAML version string. ///YAML version string.
string YAMLVersion_ = null; string YAMLVersion_ = null;
///Tag handle shortcuts and replacements. ///Tag handle shortcuts and replacements.
tagDirective[] tagHandles_; tagDirective[] tagDirectives_;
///Stack of states. ///Stack of states.
Array!(Event delegate()) states_; Array!(Event delegate()) states_;
@ -146,8 +146,8 @@ final class Parser
~this() ~this()
{ {
clear(currentEvent_); clear(currentEvent_);
clear(tagHandles_); clear(tagDirectives_);
tagHandles_ = null; tagDirectives_ = null;
clear(states_); clear(states_);
clear(marks_); clear(marks_);
} }
@ -266,7 +266,7 @@ final class Parser
if(!scanner_.checkToken(TokenID.Directive, TokenID.DocumentStart, if(!scanner_.checkToken(TokenID.Directive, TokenID.DocumentStart,
TokenID.StreamEnd)) TokenID.StreamEnd))
{ {
tagHandles_ = defaultTagDirectives_; tagDirectives_ = defaultTagDirectives_;
immutable token = scanner_.peekToken(); immutable token = scanner_.peekToken();
states_ ~= &parseDocumentEnd; states_ ~= &parseDocumentEnd;
@ -339,7 +339,7 @@ final class Parser
{ {
//Destroy version and tag handles from previous document. //Destroy version and tag handles from previous document.
YAMLVersion_ = null; YAMLVersion_ = null;
tagHandles_.length = 0; tagDirectives_.length = 0;
//Process directives. //Process directives.
while(scanner_.checkToken(TokenID.Directive)) while(scanner_.checkToken(TokenID.Directive))
@ -363,7 +363,7 @@ final class Parser
assert(parts.length == 3, "Tag directive stored incorrectly in a token"); assert(parts.length == 3, "Tag directive stored incorrectly in a token");
auto handle = parts[1]; auto handle = parts[1];
foreach(ref pair; tagHandles_) foreach(ref pair; tagDirectives_)
{ {
//handle //handle
auto h = pair[0]; auto h = pair[0];
@ -371,17 +371,17 @@ final class Parser
enforce(h != handle, new Error("Duplicate tag handle: " ~ handle, enforce(h != handle, new Error("Duplicate tag handle: " ~ handle,
token.startMark)); token.startMark));
} }
tagHandles_ ~= tagDirective(handle, parts[2]); tagDirectives_ ~= tagDirective(handle, parts[2]);
} }
} }
TagDirectives value = tagHandles_.length == 0 ? TagDirectives() : TagDirectives(tagHandles_); TagDirectives value = tagDirectives_.length == 0 ? TagDirectives() : TagDirectives(tagDirectives_);
//Add any default tag handles that haven't been overridden. //Add any default tag handles that haven't been overridden.
foreach(ref defaultPair; defaultTagDirectives_) foreach(ref defaultPair; defaultTagDirectives_)
{ {
bool found = false; bool found = false;
foreach(ref pair; tagHandles_) foreach(ref pair; tagDirectives_)
{ {
if(defaultPair[0] == pair[0] ) if(defaultPair[0] == pair[0] )
{ {
@ -389,7 +389,7 @@ final class Parser
break; break;
} }
} }
if(!found){tagHandles_ ~= defaultPair;} if(!found){tagDirectives_ ~= defaultPair;}
} }
return value; return value;
@ -541,7 +541,7 @@ final class Parser
if(handle.length > 0) if(handle.length > 0)
{ {
string replacement = null; string replacement = null;
foreach(ref pair; tagHandles_) foreach(ref pair; tagDirectives_)
{ {
//pair[0] is handle, pair[1] replacement. //pair[0] is handle, pair[1] replacement.
if(pair[0] == handle) if(pair[0] == handle)
@ -550,7 +550,7 @@ final class Parser
break; break;
} }
} }
//handle must be in tagHandles_ //handle must be in tagDirectives_
enforce(replacement !is null, enforce(replacement !is null,
new Error("While parsing a node", startMark, new Error("While parsing a node", startMark,
"found undefined tag handle: " ~ handle, tagMark)); "found undefined tag handle: " ~ handle, tagMark));

View file

@ -51,6 +51,8 @@ final class Reader
uint line_; uint line_;
///Current column in file. ///Current column in file.
uint column_; uint column_;
///Number of bytes still available (not read) in the stream.
size_t available_;
///Capacity of raw buffers. ///Capacity of raw buffers.
static immutable bufferLength8_ = 8; static immutable bufferLength8_ = 8;
@ -60,9 +62,9 @@ final class Reader
union union
{ {
///Buffer to hold UTF-8 data before decoding. ///Buffer to hold UTF-8 data before decoding.
char[bufferLength8_] rawBuffer8_; char[bufferLength8_ + 1] rawBuffer8_;
///Buffer to hold UTF-16 data before decoding. ///Buffer to hold UTF-16 data before decoding.
wchar[bufferLength16_] rawBuffer16_; wchar[bufferLength16_ + 1] rawBuffer16_;
} }
///Number of elements held in the used raw buffer. ///Number of elements held in the used raw buffer.
uint rawUsed_ = 0; uint rawUsed_ = 0;
@ -71,18 +73,23 @@ final class Reader
/** /**
* Construct a Reader. * Construct a Reader.
* *
* Params: stream = Input stream. Must be readable. * Params: stream = Input stream. Must be readable and seekable.
* *
* Throws: ReaderException if the stream is invalid. * Throws: ReaderException if the stream is invalid.
*/ */
this(Stream stream) this(Stream stream)
in{assert(stream.readable, "Can't read YAML from a non-readable stream");} in
{
assert(stream.readable && stream.seekable,
"Can't read YAML from a stream that is not readable and seekable");
}
body body
{ {
stream_ = new EndianStream(stream); stream_ = new EndianStream(stream);
available_ = stream_.available;
//handle files short enough not to have a BOM //handle files short enough not to have a BOM
if(stream_.available < 2) if(available_ < 2)
{ {
encoding_ = Encoding.UTF_8; encoding_ = Encoding.UTF_8;
return; return;
@ -104,16 +111,17 @@ final class Reader
encoding_ = Encoding.UTF_16; encoding_ = Encoding.UTF_16;
rawBuffer16_[0] = stream_.getcw(); rawBuffer16_[0] = stream_.getcw();
rawUsed_ = 1; rawUsed_ = 1;
enforce(stream_.available % 2 == 0, enforce(available_ % 2 == 0,
new ReaderException("Odd byte count in an UTF-16 stream")); new ReaderException("Odd byte count in an UTF-16 stream"));
break; break;
case 3, 4: case 3, 4:
enforce(stream_.available % 4 == 0, enforce(available_ % 4 == 0,
new ReaderException("Byte count in an UTF-32 stream not divisible by 4")); new ReaderException("Byte count in an UTF-32 stream not divisible by 4"));
encoding_ = Encoding.UTF_32; encoding_ = Encoding.UTF_32;
break; break;
default: assert(false, "Unknown UTF BOM"); default: assert(false, "Unknown UTF BOM");
} }
available_ = stream_.available;
} }
///Destroy the Reader. ///Destroy the Reader.
@ -209,7 +217,7 @@ final class Reader
void forward(size_t length = 1) void forward(size_t length = 1)
{ {
//This is here due to optimization. //This is here due to optimization.
static newlines = "\n\u0085\u2028\u2029"; static newlines = "\n\u0085\u2028\u2029"d;
updateBuffer(length + 1); updateBuffer(length + 1);
while(length > 0) while(length > 0)
@ -294,16 +302,10 @@ final class Reader
* if nonprintable characters are detected, or * if nonprintable characters are detected, or
* if there is an error reading from the stream. * if there is an error reading from the stream.
*/ */
void loadChars(uint chars) void loadChars(size_t chars)
{ {
/** ///Get next character from the stream.
* Get next character from the stream. dchar getDChar()
*
* Params: available = Bytes available in the stream.
*
* Returns: Next character in the stream.
*/
dchar getDChar(in size_t available)
{ {
switch(encoding_) switch(encoding_)
{ {
@ -317,13 +319,14 @@ final class Reader
const dchar result = rawBuffer8_[0]; const dchar result = rawBuffer8_[0];
--rawUsed_; --rawUsed_;
//Move the data. //Move the data.
temp[0 .. rawUsed_] = rawBuffer8_[1 .. rawUsed_ + 1]; *(cast(ulong*)temp.ptr) = *(cast(ulong*)(rawBuffer8_.ptr + 1));
rawBuffer8_[0 .. rawUsed_] = temp[0 .. rawUsed_]; *(cast(ulong*)rawBuffer8_.ptr) = *(cast(ulong*)temp.ptr);
return result; return result;
} }
//Bytes to read. //Bytes to read.
const readBytes = min(available, bufferLength8_ - rawUsed_); const readBytes = min(available_, bufferLength8_ - rawUsed_);
available_ -= readBytes;
//Length of data in rawBuffer8_ after reading. //Length of data in rawBuffer8_ after reading.
const len = rawUsed_ + readBytes; const len = rawUsed_ + readBytes;
//Read the data. //Read the data.
@ -342,7 +345,8 @@ final class Reader
//Temp buffer for moving data in rawBuffer8_. //Temp buffer for moving data in rawBuffer8_.
wchar[bufferLength16_] temp; wchar[bufferLength16_] temp;
//Words to read. //Words to read.
size_t readWords = min(available / 2, bufferLength16_ - rawUsed_); size_t readWords = min(available_ / 2, bufferLength16_ - rawUsed_);
available_ -= readWords * 2;
//Length of data in rawBuffer16_ after reading. //Length of data in rawBuffer16_ after reading.
size_t len = rawUsed_; size_t len = rawUsed_;
//Read the data. //Read the data.
@ -365,6 +369,7 @@ final class Reader
return result; return result;
case Encoding.UTF_32: case Encoding.UTF_32:
dchar result; dchar result;
available_ -= 4;
stream_.read(result); stream_.read(result);
return result; return result;
default: assert(false); default: assert(false);
@ -374,13 +379,11 @@ final class Reader
const oldLength = buffer_.length; const oldLength = buffer_.length;
const oldPosition = stream_.position; const oldPosition = stream_.position;
//Preallocating memory to limit GC reallocations. //Preallocating memory to limit GC reallocations.
buffer_.length = buffer_.length + chars; buffer_.length = buffer_.length + chars;
scope(exit) scope(exit)
{ {
buffer_.length = buffer_.length - chars; buffer_.length = buffer_.length - chars;
enforce(printable(buffer_[oldLength .. $]), enforce(printable(buffer_[oldLength .. $]),
new ReaderException("Special unicode characters are not allowed")); new ReaderException("Special unicode characters are not allowed"));
} }
@ -388,8 +391,7 @@ final class Reader
try for(uint c = 0; chars; --chars, ++c) try for(uint c = 0; chars; --chars, ++c)
{ {
if(done){break;} if(done){break;}
const available = stream_.available; buffer_[oldLength + c] = getDChar();
buffer_[oldLength + c] = getDChar(available);
} }
catch(UtfException e) catch(UtfException e)
{ {
@ -428,7 +430,7 @@ final class Reader
///Are we done reading? ///Are we done reading?
@property bool done() @property bool done()
{ {
return (stream_.available == 0 && return (available_ == 0 &&
((encoding_ == Encoding.UTF_8 && rawUsed_ == 0) || ((encoding_ == Encoding.UTF_8 && rawUsed_ == 0) ||
(encoding_ == Encoding.UTF_16 && rawUsed_ == 0) || (encoding_ == Encoding.UTF_16 && rawUsed_ == 0) ||
encoding_ == Encoding.UTF_32)); encoding_ == Encoding.UTF_32));

View file

@ -690,7 +690,7 @@ final class Scanner
return reader_.column == 0 && return reader_.column == 0 &&
reader_.peek() == '-' && reader_.peek() == '-' &&
reader_.prefix(3) == "---" && reader_.prefix(3) == "---" &&
" \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek(3)); " \t\0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek(3));
} }
///Check if the next token is DOCUMENT-END: ^ '...' (' '|'\n') ///Check if the next token is DOCUMENT-END: ^ '...' (' '|'\n')
@ -700,14 +700,14 @@ final class Scanner
return reader_.column == 0 && return reader_.column == 0 &&
reader_.peek() == '.' && reader_.peek() == '.' &&
reader_.prefix(3) == "..." && reader_.prefix(3) == "..." &&
" \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek(3)); " \t\0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek(3));
} }
///Check if the next token is BLOCK-ENTRY: '-' (' '|'\n') ///Check if the next token is BLOCK-ENTRY: '-' (' '|'\n')
bool checkBlockEntry() bool checkBlockEntry()
{ {
return reader_.peek() == '-' && return reader_.peek() == '-' &&
" \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek(1)); " \t\0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek(1));
} }
/** /**
@ -719,7 +719,7 @@ final class Scanner
{ {
return reader_.peek() == '?' && return reader_.peek() == '?' &&
(flowLevel_ > 0 || (flowLevel_ > 0 ||
" \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek(1))); " \t\0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek(1)));
} }
/** /**
@ -731,7 +731,7 @@ final class Scanner
{ {
return reader_.peek() == ':' && return reader_.peek() == ':' &&
(flowLevel_ > 0 || (flowLevel_ > 0 ||
" \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek(1))); " \t\0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek(1)));
} }
/** /**
@ -753,9 +753,9 @@ final class Scanner
bool checkPlain() bool checkPlain()
{ {
const c = reader_.peek(); const c = reader_.peek();
return !("-?:,[]{}#&*!|>\'\"%@` \t\0\n\r\u0085\u2028\u2029".canFind(c)) || return !("-?:,[]{}#&*!|>\'\"%@` \t\0\n\r\u0085\u2028\u2029"d.canFind(c)) ||
(!" \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek(1)) && (!" \t\0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek(1)) &&
(c == '-' || (flowLevel_ == 0 && "?:".canFind(c)))); (c == '-' || (flowLevel_ == 0 && "?:"d.canFind(c))));
} }
@ -770,7 +770,7 @@ final class Scanner
{ {
uint length = 0; uint length = 0;
dchar c = reader_.peek(); dchar c = reader_.peek();
while(isAlphaNum(c) || "-_".canFind(c)) while(isAlphaNum(c) || "-_"d.canFind(c))
{ {
++length; ++length;
c = reader_.peek(length); c = reader_.peek(length);
@ -788,7 +788,7 @@ final class Scanner
dstring scanToNextBreak() dstring scanToNextBreak()
{ {
uint length = 0; uint length = 0;
while(!"\0\n\r\u0085\u2028\u2029".canFind(reader_.peek(length))){++length;} while(!"\0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek(length))){++length;}
return reader_.get(length); return reader_.get(length);
} }
@ -855,7 +855,7 @@ final class Scanner
//Scan directive name. //Scan directive name.
const name = scanAlphaNumeric!"a directive"(startMark); const name = scanAlphaNumeric!"a directive"(startMark);
enforce(" \0\n\r\u0085\u2028\u2029".canFind(reader_.peek()), enforce(" \0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek()),
new Error("While scanning a directive", startMark, new Error("While scanning a directive", startMark,
"expected alphanumeric, - or _, but found " "expected alphanumeric, - or _, but found "
~ to!string(reader_.peek()), reader_.mark)); ~ to!string(reader_.peek()), reader_.mark));
@ -876,7 +876,7 @@ final class Scanner
reader_.forward(); reader_.forward();
result ~= '.' ~ scanYAMLDirectiveNumber(startMark); result ~= '.' ~ scanYAMLDirectiveNumber(startMark);
enforce(" \0\n\r\u0085\u2028\u2029".canFind(reader_.peek()), enforce(" \0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek()),
new Error("While scanning a directive", startMark, new Error("While scanning a directive", startMark,
"expected a digit or '.', but found: " "expected a digit or '.', but found: "
~ to!string(reader_.peek()), reader_.mark)); ~ to!string(reader_.peek()), reader_.mark));
@ -922,7 +922,7 @@ final class Scanner
dstring scanTagDirectivePrefix(in Mark startMark) dstring scanTagDirectivePrefix(in Mark startMark)
{ {
const value = scanTagURI("directive", startMark); const value = scanTagURI("directive", startMark);
enforce(" \0\n\r\u0085\u2028\u2029".canFind(reader_.peek()), enforce(" \0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek()),
new Error("While scanning a directive prefix", startMark, new Error("While scanning a directive prefix", startMark,
"expected ' ', but found" ~ to!string(reader_.peek()), "expected ' ', but found" ~ to!string(reader_.peek()),
reader_.mark)); reader_.mark));
@ -935,7 +935,7 @@ final class Scanner
{ {
findNextNonSpace(); findNextNonSpace();
if(reader_.peek() == '#'){scanToNextBreak();} if(reader_.peek() == '#'){scanToNextBreak();}
enforce("\0\n\r\u0085\u2028\u2029".canFind(reader_.peek()), enforce("\0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek()),
new Error("While scanning a directive", startMark, new Error("While scanning a directive", startMark,
"expected comment or a line break, but found" "expected comment or a line break, but found"
~ to!string(reader_.peek()), reader_.mark)); ~ to!string(reader_.peek()), reader_.mark));
@ -964,7 +964,7 @@ final class Scanner
dstring value = i == '*' ? scanAlphaNumeric!("an alias")(startMark) dstring value = i == '*' ? scanAlphaNumeric!("an alias")(startMark)
: scanAlphaNumeric!("an anchor")(startMark); : scanAlphaNumeric!("an anchor")(startMark);
enforce((" \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek()) || enforce((" \t\0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek()) ||
("?:,]}%@").canFind(reader_.peek())), ("?:,]}%@").canFind(reader_.peek())),
new Error("While scanning an " ~ (i == '*') ? "alias" : "anchor", new Error("While scanning an " ~ (i == '*') ? "alias" : "anchor",
startMark, "expected alphanumeric, - or _, but found "~ startMark, "expected alphanumeric, - or _, but found "~
@ -999,7 +999,7 @@ final class Scanner
reader_.mark)); reader_.mark));
reader_.forward(); reader_.forward();
} }
else if(" \t\0\n\r\u0085\u2028\u2029".canFind(c)) else if(" \t\0\n\r\u0085\u2028\u2029"d.canFind(c))
{ {
suffix = "!"; suffix = "!";
reader_.forward(); reader_.forward();
@ -1009,7 +1009,7 @@ final class Scanner
uint length = 1; uint length = 1;
bool useHandle = false; bool useHandle = false;
while(!" \0\n\r\u0085\u2028\u2029".canFind(c)) while(!" \0\n\r\u0085\u2028\u2029"d.canFind(c))
{ {
if(c == '!') if(c == '!')
{ {
@ -1030,7 +1030,7 @@ final class Scanner
suffix = scanTagURI("tag", startMark); suffix = scanTagURI("tag", startMark);
} }
enforce(" \0\n\r\u0085\u2028\u2029".canFind(reader_.peek()), enforce(" \0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek()),
new Error("While scanning a tag", startMark, new Error("While scanning a tag", startMark,
"expected ' ' but found" ~ to!string(reader_.peek()), "expected ' ' but found" ~ to!string(reader_.peek()),
reader_.mark)); reader_.mark));
@ -1078,7 +1078,7 @@ final class Scanner
while(reader_.column == indent && reader_.peek() != '\0') while(reader_.column == indent && reader_.peek() != '\0')
{ {
appender_.put(breaks); appender_.put(breaks);
const bool leadingNonSpace = !" \t".canFind(reader_.peek()); const bool leadingNonSpace = !" \t"d.canFind(reader_.peek());
appender_.put(scanToNextBreak()); appender_.put(scanToNextBreak());
lineBreak = ""d ~ scanLineBreak(); lineBreak = ""d ~ scanLineBreak();
@ -1092,7 +1092,7 @@ final class Scanner
//This is the folding according to the specification: //This is the folding according to the specification:
if(style == ScalarStyle.Folded && lineBreak == "\n" && if(style == ScalarStyle.Folded && lineBreak == "\n" &&
leadingNonSpace && !" \t".canFind(reader_.peek())) leadingNonSpace && !" \t"d.canFind(reader_.peek()))
{ {
if(breaks.length == 0){appender_.put(' ');} if(breaks.length == 0){appender_.put(' ');}
} }
@ -1128,7 +1128,7 @@ final class Scanner
///Get chomping indicator, if detected. Return false otherwise. ///Get chomping indicator, if detected. Return false otherwise.
bool getChomping() bool getChomping()
{ {
if(!"+-".canFind(c)){return false;} if(!"+-"d.canFind(c)){return false;}
chomping = c == '+' ? Chomping.Keep : Chomping.Strip; chomping = c == '+' ? Chomping.Keep : Chomping.Strip;
reader_.forward(); reader_.forward();
c = reader_.peek(); c = reader_.peek();
@ -1153,7 +1153,7 @@ final class Scanner
if(getChomping()) {getIncrement();} if(getChomping()) {getIncrement();}
else if(getIncrement()){getChomping();} else if(getIncrement()){getChomping();}
enforce(" \0\n\r\u0085\u2028\u2029".canFind(c), enforce(" \0\n\r\u0085\u2028\u2029"d.canFind(c),
new Error("While scanning a block scalar", startMark, new Error("While scanning a block scalar", startMark,
"expected chomping or indentation indicator, but found " "expected chomping or indentation indicator, but found "
~ to!string(c), reader_.mark)); ~ to!string(c), reader_.mark));
@ -1167,7 +1167,7 @@ final class Scanner
findNextNonSpace(); findNextNonSpace();
if(reader_.peek == '#'){scanToNextBreak();} if(reader_.peek == '#'){scanToNextBreak();}
enforce("\0\n\r\u0085\u2028\u2029".canFind(reader_.peek()), enforce("\0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek()),
new Error("While scanning a block scalar", startMark, new Error("While scanning a block scalar", startMark,
"expected a comment or a line break, but found " "expected a comment or a line break, but found "
~ to!string(reader_.peek()), reader_.mark)); ~ to!string(reader_.peek()), reader_.mark));
@ -1181,7 +1181,7 @@ final class Scanner
uint maxIndent; uint maxIndent;
Mark endMark = reader_.mark; Mark endMark = reader_.mark;
while(" \n\r\u0085\u2028\u2029".canFind(reader_.peek())) while(" \n\r\u0085\u2028\u2029"d.canFind(reader_.peek()))
{ {
if(reader_.peek() != ' ') if(reader_.peek() != ' ')
{ {
@ -1205,7 +1205,7 @@ final class Scanner
for(;;) for(;;)
{ {
while(reader_.column < indent && reader_.peek() == ' '){reader_.forward();} while(reader_.column < indent && reader_.peek() == ' '){reader_.forward();}
if(!"\n\r\u0085\u2028\u2029".canFind(reader_.peek())){break;} if(!"\n\r\u0085\u2028\u2029"d.canFind(reader_.peek())){break;}
chunks ~= scanLineBreak(); chunks ~= scanLineBreak();
endMark = reader_.mark; endMark = reader_.mark;
} }
@ -1242,7 +1242,7 @@ final class Scanner
{ {
dchar c = reader_.peek(); dchar c = reader_.peek();
uint length = 0; uint length = 0;
while(!(" \t\0\n\r\u0085\u2028\u2029".canFind(c) || "\'\"\\".canFind(c))) while(!(" \t\0\n\r\u0085\u2028\u2029\'\"\\"d.canFind(c)))
{ {
++length; ++length;
c = reader_.peek(length); c = reader_.peek(length);
@ -1258,7 +1258,7 @@ final class Scanner
reader_.forward(2); reader_.forward(2);
} }
else if((quotes == ScalarStyle.DoubleQuoted && c == '\'') || else if((quotes == ScalarStyle.DoubleQuoted && c == '\'') ||
(quotes == ScalarStyle.SingleQuoted && "\"\\".canFind(c))) (quotes == ScalarStyle.SingleQuoted && "\"\\"d.canFind(c)))
{ {
appender_.put(c); appender_.put(c);
reader_.forward(); reader_.forward();
@ -1290,17 +1290,16 @@ final class Scanner
dstring hex = reader_.get(length); dstring hex = reader_.get(length);
appender_.put(cast(dchar)parse!int(hex, 16)); appender_.put(cast(dchar)parse!int(hex, 16));
} }
else if("\n\r\u0085\u2028\u2029".canFind(c)) else if("\n\r\u0085\u2028\u2029"d.canFind(c))
{ {
scanLineBreak(); scanLineBreak();
appender_.put(scanFlowScalarBreaks(startMark)); appender_.put(scanFlowScalarBreaks(startMark));
} }
else else
{ {
throw new Error("While scanning a double quoted scalar", throw new Error("While scanning a double quoted scalar", startMark,
startMark, "found unknown escape character: " ~ to!string(c),
"found unknown escape character: " ~ reader_.mark);
to!string(c), reader_.mark);
} }
} }
else else
@ -1314,15 +1313,14 @@ final class Scanner
void scanFlowScalarSpaces(in Mark startMark) void scanFlowScalarSpaces(in Mark startMark)
{ {
uint length = 0; uint length = 0;
while(" \t".canFind(reader_.peek(length))){++length;} while(" \t"d.canFind(reader_.peek(length))){++length;}
const whitespaces = reader_.get(length); const whitespaces = reader_.get(length);
dchar c = reader_.peek(); dchar c = reader_.peek();
enforce(c != '\0', enforce(c != '\0', new Error("While scanning a quoted scalar", startMark,
new Error("While scanning a quoted scalar", startMark, "found unexpected end of stream", reader_.mark));
"found unexpected end of stream", reader_.mark));
if("\n\r\u0085\u2028\u2029".canFind(c)) if("\n\r\u0085\u2028\u2029"d.canFind(c))
{ {
const lineBreak = scanLineBreak(); const lineBreak = scanLineBreak();
const breaks = scanFlowScalarBreaks(startMark); const breaks = scanFlowScalarBreaks(startMark);
@ -1343,15 +1341,15 @@ final class Scanner
//Instead of checking indentation, we check for document separators. //Instead of checking indentation, we check for document separators.
const prefix = reader_.prefix(3); const prefix = reader_.prefix(3);
if((prefix == "---" || prefix == "...") && if((prefix == "---" || prefix == "...") &&
" \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek(3))) " \t\0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek(3)))
{ {
throw new Error("While scanning a quoted scalar", startMark, throw new Error("While scanning a quoted scalar", startMark,
"found unexpected document separator", reader_.mark); "found unexpected document separator", reader_.mark);
} }
while(" \t".canFind(reader_.peek())){reader_.forward();} while(" \t"d.canFind(reader_.peek())){reader_.forward();}
if("\n\r\u0085\u2028\u2029".canFind(reader_.peek())) if("\n\r\u0085\u2028\u2029"d.canFind(reader_.peek()))
{ {
appender.put(scanLineBreak()); appender.put(scanLineBreak());
} }
@ -1384,18 +1382,18 @@ final class Scanner
for(;;) for(;;)
{ {
c = reader_.peek(length); c = reader_.peek(length);
bool done = " \t\0\n\r\u0085\u2028\u2029".canFind(c) || bool done = " \t\0\n\r\u0085\u2028\u2029"d.canFind(c) ||
(flowLevel_ == 0 && c == ':' && (flowLevel_ == 0 && c == ':' &&
" \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek(length + 1))) || " \t\0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek(length + 1))) ||
(flowLevel_ > 0 && ",:?[]{}".canFind(c)); (flowLevel_ > 0 && ",:?[]{}"d.canFind(c));
if(done){break;} if(done){break;}
++length; ++length;
} }
//It's not clear what we should do with ':' in the flow context. //It's not clear what we should do with ':' in the flow context.
if(flowLevel_ > 0 && c == ':' && if(flowLevel_ > 0 && c == ':' &&
!" \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek(length + 1)) && !" \t\0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek(length + 1)) &&
!",[]{}".canFind(reader_.peek(length + 1))) !",[]{}"d.canFind(reader_.peek(length + 1)))
{ {
reader_.forward(length); reader_.forward(length);
throw new Error("While scanning a plain scalar", startMark, throw new Error("While scanning a plain scalar", startMark,
@ -1434,7 +1432,7 @@ final class Scanner
dstring whitespaces = reader_.get(length); dstring whitespaces = reader_.get(length);
dchar c = reader_.peek(); dchar c = reader_.peek();
if("\n\r\u0085\u2028\u2029".canFind(c)) if("\n\r\u0085\u2028\u2029"d.canFind(c))
{ {
const lineBreak = scanLineBreak(); const lineBreak = scanLineBreak();
allowSimpleKey_ = true; allowSimpleKey_ = true;
@ -1442,13 +1440,13 @@ final class Scanner
bool end() bool end()
{ {
return ["---"d, "..."d].canFind(reader_.prefix(3)) && return ["---"d, "..."d].canFind(reader_.prefix(3)) &&
" \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek(3)); " \t\0\n\r\u0085\u2028\u2029"d.canFind(reader_.peek(3));
} }
if(end()){return "";} if(end()){return "";}
dstring breaks; dstring breaks;
while(" \n\r\u0085\u2028\u2029".canFind(reader_.peek())) while(" \n\r\u0085\u2028\u2029"d.canFind(reader_.peek()))
{ {
if(reader_.peek() == ' '){reader_.forward();} if(reader_.peek() == ' '){reader_.forward();}
else else
@ -1482,7 +1480,7 @@ final class Scanner
c = reader_.peek(length); c = reader_.peek(length);
if(c != ' ') if(c != ' ')
{ {
while(isAlphaNum(c) || "-_".canFind(c)) while(isAlphaNum(c) || "-_"d.canFind(c))
{ {
++length; ++length;
c = reader_.peek(length); c = reader_.peek(length);
@ -1508,7 +1506,7 @@ final class Scanner
uint length = 0; uint length = 0;
dchar c = reader_.peek(); dchar c = reader_.peek();
while(isAlphaNum(c) || "-;/?:@&=+$,_.!~*\'()[]%".canFind(c)) while(isAlphaNum(c) || "-;/?:@&=+$,_.!~*\'()[]%"d.canFind(c))
{ {
if(c == '%') if(c == '%')
{ {
@ -1594,13 +1592,13 @@ final class Scanner
{ {
const c = reader_.peek(); const c = reader_.peek();
if("\r\n\u0085".canFind(c)) if("\r\n\u0085"d.canFind(c))
{ {
if(reader_.prefix(2) == "\r\n"){reader_.forward(2);} if(reader_.prefix(2) == "\r\n"d){reader_.forward(2);}
else{reader_.forward();} else{reader_.forward();}
return '\n'; return '\n';
} }
if("\u2028\u2029".canFind(c)) if("\u2028\u2029"d.canFind(c))
{ {
reader_.forward(); reader_.forward();
return c; return c;