remove appenderNoGC, printNoGC, parseNoGC
This commit is contained in:
parent
f528351867
commit
d7f51a8225
|
@ -16,215 +16,6 @@ import std.typetuple;
|
||||||
import std.range;
|
import std.range;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// A NoGC version of std.conv.parse for integer types.
|
|
||||||
///
|
|
||||||
/// Differences:
|
|
||||||
/// overflow parameter - bool set to true if there was integer overflow.
|
|
||||||
/// Asserts that at least one character was parsed instead of throwing an exception.
|
|
||||||
/// The caller must validate the inputs before calling parseNoGC.
|
|
||||||
Target parseNoGC(Target, Source)(ref Source s, uint radix, out bool overflow)
|
|
||||||
@safe pure nothrow @nogc
|
|
||||||
if (isSomeChar!(ElementType!Source) &&
|
|
||||||
isIntegral!Target && !is(Target == enum))
|
|
||||||
in { assert(radix >= 2 && radix <= 36); }
|
|
||||||
body
|
|
||||||
{
|
|
||||||
immutable uint beyond = (radix < 10 ? '0' : 'a'-10) + radix;
|
|
||||||
|
|
||||||
Target v = 0;
|
|
||||||
size_t atStart = true;
|
|
||||||
|
|
||||||
// We can safely foreach over individual code points.
|
|
||||||
// Even with UTF-8 any digit is ASCII and anything not ASCII (such as the start of
|
|
||||||
// a UTF-8 sequence) is not a digit.
|
|
||||||
foreach(i; 0 .. s.length)
|
|
||||||
{
|
|
||||||
dchar c = s[i];
|
|
||||||
// We can just take a char instead of decoding because anything non-ASCII is not
|
|
||||||
// going to be a decodable digit, i.e. we will end at such a byte.
|
|
||||||
if (c < '0' || c >= 0x80)
|
|
||||||
break;
|
|
||||||
if (radix < 10)
|
|
||||||
{
|
|
||||||
if (c >= beyond)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (c > '9')
|
|
||||||
{
|
|
||||||
c |= 0x20;//poorman's tolower
|
|
||||||
if (c < 'a' || c >= beyond) { break; }
|
|
||||||
c -= 'a'-10-'0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto blah = cast(Target) (v * radix + c - '0');
|
|
||||||
if (blah < v)
|
|
||||||
{
|
|
||||||
overflow = true;
|
|
||||||
return Target.max;
|
|
||||||
}
|
|
||||||
v = blah;
|
|
||||||
atStart = false;
|
|
||||||
}
|
|
||||||
assert(!atStart, "Nothing to parse in parse()");
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Buils a message to a buffer similarly to writef/writefln, but without
|
|
||||||
/// using GC.
|
|
||||||
///
|
|
||||||
/// C snprintf would be better, but it isn't pure.
|
|
||||||
/// formattedWrite isn't completely @nogc yet (although it isn't GC-heavy).
|
|
||||||
///
|
|
||||||
/// The user has to ensure buffer is long enough - an assert checks that we don't run
|
|
||||||
/// out of space. Currently this can only write strings and dchars.
|
|
||||||
char[] printNoGC(S...)(char[] buffer, S args) @safe pure nothrow @nogc
|
|
||||||
{
|
|
||||||
auto appender = appenderNoGC(buffer);
|
|
||||||
|
|
||||||
foreach(arg; args)
|
|
||||||
{
|
|
||||||
alias A = typeof(arg);
|
|
||||||
static if(is(A == char[]) || is(A == string)) { appender.put(arg); }
|
|
||||||
else static if(is(Unqual!A == dchar)) { appender.putDChar(arg); }
|
|
||||||
else static assert(false, "printNoGC does not support " ~ A.stringof);
|
|
||||||
}
|
|
||||||
|
|
||||||
return appender.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// A UFCS utility function to write a dchar to an AppenderNoGCFixed using writeDCharTo.
|
|
||||||
///
|
|
||||||
/// The char $(B must) be a valid dchar.
|
|
||||||
void putDChar(ref AppenderNoGCFixed!(char[], char) appender, dchar c)
|
|
||||||
@safe pure nothrow @nogc
|
|
||||||
{
|
|
||||||
char[4] dcharBuf;
|
|
||||||
if(c < 0x80)
|
|
||||||
{
|
|
||||||
dcharBuf[0] = cast(char)c;
|
|
||||||
appender.put(dcharBuf[0 .. 1]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Should be safe to use as the first thing Reader does is validate everything.
|
|
||||||
const bytes = encodeValidCharNoGC(dcharBuf, c);
|
|
||||||
appender.put(dcharBuf[0 .. bytes]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience function that returns an $(D AppenderNoGCFixed!A) using with $(D array)
|
|
||||||
/// for storage.
|
|
||||||
AppenderNoGCFixed!(E[]) appenderNoGC(A : E[], E)(A array)
|
|
||||||
{
|
|
||||||
return AppenderNoGCFixed!(E[])(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A gutted, NoGC version of std.array.appender.
|
|
||||||
///
|
|
||||||
/// Works on a fixed-size buffer.
|
|
||||||
struct AppenderNoGCFixed(A : T[], T)
|
|
||||||
{
|
|
||||||
import std.array;
|
|
||||||
|
|
||||||
private struct Data
|
|
||||||
{
|
|
||||||
size_t capacity;
|
|
||||||
Unqual!T[] arr;
|
|
||||||
bool canExtend = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Data _data;
|
|
||||||
|
|
||||||
@nogc:
|
|
||||||
|
|
||||||
/// Construct an appender that will work with given buffer.
|
|
||||||
///
|
|
||||||
/// Data written to the appender will overwrite the buffer from the start.
|
|
||||||
this(T[] arr) @safe pure nothrow
|
|
||||||
{
|
|
||||||
// initialize to a given array.
|
|
||||||
_data.arr = cast(Unqual!T[])arr[0 .. 0]; //trusted
|
|
||||||
_data.capacity = arr.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the capacity of the array (the maximum number of elements the
|
|
||||||
* managed array can accommodate before triggering a reallocation). If any
|
|
||||||
* appending will reallocate, $(D capacity) returns $(D 0).
|
|
||||||
*/
|
|
||||||
@property size_t capacity() const @safe pure nothrow
|
|
||||||
{
|
|
||||||
return _data.capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the managed array.
|
|
||||||
*/
|
|
||||||
@property inout(T)[] data() inout @safe pure nothrow
|
|
||||||
{
|
|
||||||
return cast(typeof(return))(_data.arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure we can add nelems elements, resizing as necessary
|
|
||||||
private void ensureAddable(size_t nelems) @safe pure nothrow
|
|
||||||
{
|
|
||||||
assert(_data.capacity >= _data.arr.length + nelems,
|
|
||||||
"AppenderFixed ran out of space");
|
|
||||||
}
|
|
||||||
|
|
||||||
void put(U)(U[] items) if (is(Unqual!U == T))
|
|
||||||
{
|
|
||||||
// make sure we have enough space, then add the items
|
|
||||||
ensureAddable(items.length);
|
|
||||||
immutable len = _data.arr.length;
|
|
||||||
immutable newlen = len + items.length;
|
|
||||||
|
|
||||||
auto bigDataFun() @trusted nothrow { return _data.arr.ptr[0 .. newlen];}
|
|
||||||
auto bigData = bigDataFun();
|
|
||||||
|
|
||||||
alias UT = Unqual!T;
|
|
||||||
|
|
||||||
bigData[len .. newlen] = items[];
|
|
||||||
|
|
||||||
//We do this at the end, in case of exceptions
|
|
||||||
_data.arr = bigData;
|
|
||||||
}
|
|
||||||
|
|
||||||
// only allow overwriting data on non-immutable and non-const data
|
|
||||||
static if (isMutable!T)
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Clears the managed array. This allows the elements of the array to be reused
|
|
||||||
* for appending.
|
|
||||||
*
|
|
||||||
* Note that clear is disabled for immutable or const element types, due to the
|
|
||||||
* possibility that $(D AppenderNoGCFixed) might overwrite immutable data.
|
|
||||||
*/
|
|
||||||
void clear() @safe pure nothrow
|
|
||||||
{
|
|
||||||
_data.arr = ()@trusted{ return _data.arr.ptr[0 .. 0]; }();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/// Clear is not available for const/immutable data.
|
|
||||||
@disable void clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@safe unittest
|
|
||||||
{
|
|
||||||
char[256] buffer;
|
|
||||||
auto appender = appenderNoGC(buffer[]);
|
|
||||||
appender.put("found unsupported escape character: ");
|
|
||||||
appender.putDChar('a');
|
|
||||||
appender.putDChar('á');
|
|
||||||
assert(appender.data == "found unsupported escape character: aá");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Result of a validateUTF8NoGC call.
|
/// Result of a validateUTF8NoGC call.
|
||||||
struct ValidateResult
|
struct ValidateResult
|
||||||
{
|
{
|
||||||
|
|
|
@ -539,7 +539,7 @@ final class Parser
|
||||||
string notInPlace;
|
string notInPlace;
|
||||||
bool inEscape = false;
|
bool inEscape = false;
|
||||||
import dyaml.nogcutil;
|
import dyaml.nogcutil;
|
||||||
auto appender = appenderNoGC(cast(char[])tokenValue);
|
auto appender = appender!(char[])();
|
||||||
for(char[] oldValue = tokenValue; !oldValue.empty();)
|
for(char[] oldValue = tokenValue; !oldValue.empty();)
|
||||||
{
|
{
|
||||||
const dchar c = oldValue.front();
|
const dchar c = oldValue.front();
|
||||||
|
@ -549,7 +549,7 @@ final class Parser
|
||||||
{
|
{
|
||||||
if(c != '\\')
|
if(c != '\\')
|
||||||
{
|
{
|
||||||
if(notInPlace is null) { appender.putDChar(c); }
|
if(notInPlace is null) { appender.put(c); }
|
||||||
else { notInPlace ~= c; }
|
else { notInPlace ~= c; }
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -570,7 +570,7 @@ final class Parser
|
||||||
// many-byte unicode chars
|
// many-byte unicode chars
|
||||||
if(c != 'L' && c != 'P')
|
if(c != 'L' && c != 'P')
|
||||||
{
|
{
|
||||||
appender.putDChar(dyaml.escapes.fromEscape(c));
|
appender.put(dyaml.escapes.fromEscape(c));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Need to duplicate as we won't fit into
|
// Need to duplicate as we won't fit into
|
||||||
|
@ -596,10 +596,8 @@ final class Parser
|
||||||
assert(!hex.canFind!(d => !d.isHexDigit),
|
assert(!hex.canFind!(d => !d.isHexDigit),
|
||||||
"Scanner must ensure the hex string is valid");
|
"Scanner must ensure the hex string is valid");
|
||||||
|
|
||||||
bool overflow;
|
const decoded = cast(dchar)parse!int(hex, 16u);
|
||||||
const decoded = cast(dchar)parseNoGC!int(hex, 16u, overflow);
|
if(notInPlace is null) { appender.put(decoded); }
|
||||||
assert(!overflow, "Scanner must ensure there's no overflow");
|
|
||||||
if(notInPlace is null) { appender.putDChar(decoded); }
|
|
||||||
else { notInPlace ~= decoded; }
|
else { notInPlace ~= decoded; }
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -661,16 +661,14 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destroy the transaction and revert it if it hasn't been committed yet.
|
/// Destroy the transaction and revert it if it hasn't been committed yet.
|
||||||
///
|
void end() @safe pure nothrow @nogc
|
||||||
/// Does nothing for a default-initialized transaction.
|
|
||||||
~this() @safe pure nothrow @nogc
|
|
||||||
{
|
{
|
||||||
if(builder_ is null || committed_) { return; }
|
assert(builder_ && builder_.endStackUsed_ == stackLevel_ + 1,
|
||||||
assert(builder_.endStackUsed_ == stackLevel_ + 1,
|
|
||||||
"Parent transactions don't fully contain child transactions");
|
"Parent transactions don't fully contain child transactions");
|
||||||
builder_.pop();
|
builder_.pop();
|
||||||
builder_ = null;
|
builder_ = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -235,7 +235,13 @@ final class Scanner
|
||||||
/// Build an error message in msgBuffer_ and return it as a string.
|
/// Build an error message in msgBuffer_ and return it as a string.
|
||||||
string buildMsg(S ...)(S args) @trusted
|
string buildMsg(S ...)(S args) @trusted
|
||||||
{
|
{
|
||||||
return cast(string)msgBuffer_.printNoGC(args);
|
try {
|
||||||
|
return text(args);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Most scanning error messages have the same format; so build them with this
|
/// Most scanning error messages have the same format; so build them with this
|
||||||
|
@ -739,7 +745,7 @@ final class Scanner
|
||||||
tokens_.push(plain);
|
tokens_.push(plain);
|
||||||
}
|
}
|
||||||
|
|
||||||
pure nothrow @nogc:
|
pure:
|
||||||
|
|
||||||
///Check if the next token is DIRECTIVE: ^ '%' ...
|
///Check if the next token is DIRECTIVE: ^ '%' ...
|
||||||
bool checkDirective() @safe
|
bool checkDirective() @safe
|
||||||
|
@ -1326,7 +1332,7 @@ final class Scanner
|
||||||
// (which are at the end of the scalar). Otherwise re remove them (end the
|
// (which are at the end of the scalar). Otherwise re remove them (end the
|
||||||
// transaction).
|
// transaction).
|
||||||
if(chomping == Chomping.Keep) { breaksTransaction.commit(); }
|
if(chomping == Chomping.Keep) { breaksTransaction.commit(); }
|
||||||
else { breaksTransaction.__dtor(); }
|
else { breaksTransaction.end(); }
|
||||||
if(chomping != Chomping.Strip && lineBreak != int.max)
|
if(chomping != Chomping.Strip && lineBreak != int.max)
|
||||||
{
|
{
|
||||||
// If chomping is Keep, we keep the line break but the first line break
|
// If chomping is Keep, we keep the line break but the first line break
|
||||||
|
@ -1596,14 +1602,15 @@ final class Scanner
|
||||||
char[2] escapeStart = ['\\', cast(char) c];
|
char[2] escapeStart = ['\\', cast(char) c];
|
||||||
reader_.sliceBuilder.write(escapeStart);
|
reader_.sliceBuilder.write(escapeStart);
|
||||||
reader_.sliceBuilder.write(hex);
|
reader_.sliceBuilder.write(hex);
|
||||||
bool overflow;
|
|
||||||
// Note: This is just error checking; Parser does the actual
|
// Note: This is just error checking; Parser does the actual
|
||||||
// escaping (otherwise we could accidentally create an
|
// escaping (otherwise we could accidentally create an
|
||||||
// escape sequence here that wasn't in input, breaking the
|
// escape sequence here that wasn't in input, breaking the
|
||||||
// escaping code in parser, which is in parser because it
|
// escaping code in parser, which is in parser because it
|
||||||
// can't always be done in place)
|
// can't always be done in place)
|
||||||
parseNoGC!int(hex, 16u, overflow);
|
try {
|
||||||
if(overflow)
|
parse!int(hex, 16u);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
{
|
{
|
||||||
error("While scanning a double quoted scalar", startMark,
|
error("While scanning a double quoted scalar", startMark,
|
||||||
"overflow when parsing an escape sequence of " ~
|
"overflow when parsing an escape sequence of " ~
|
||||||
|
@ -1799,7 +1806,7 @@ final class Scanner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spacesTransaction.__dtor();
|
spacesTransaction.end();
|
||||||
char[] slice = reader_.sliceBuilder.finish();
|
char[] slice = reader_.sliceBuilder.finish();
|
||||||
|
|
||||||
return scalarToken(startMark, endMark, slice, ScalarStyle.Plain);
|
return scalarToken(startMark, endMark, slice, ScalarStyle.Plain);
|
||||||
|
@ -1835,7 +1842,7 @@ final class Scanner
|
||||||
const lineBreak = scanLineBreak();
|
const lineBreak = scanLineBreak();
|
||||||
allowSimpleKey_ = true;
|
allowSimpleKey_ = true;
|
||||||
|
|
||||||
static bool end(Reader reader_) @safe pure nothrow @nogc
|
static bool end(Reader reader_) @safe pure
|
||||||
{
|
{
|
||||||
const prefix = reader_.prefix(3);
|
const prefix = reader_.prefix(3);
|
||||||
return ("---" == prefix || "..." == prefix)
|
return ("---" == prefix || "..." == prefix)
|
||||||
|
|
Loading…
Reference in a new issue