diff --git a/source/dyaml/nogcutil.d b/source/dyaml/nogcutil.d index 8b2bcf6..322fb04 100644 --- a/source/dyaml/nogcutil.d +++ b/source/dyaml/nogcutil.d @@ -16,215 +16,6 @@ import std.typetuple; 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. struct ValidateResult { diff --git a/source/dyaml/parser.d b/source/dyaml/parser.d index 578f205..199eb0a 100644 --- a/source/dyaml/parser.d +++ b/source/dyaml/parser.d @@ -539,7 +539,7 @@ final class Parser string notInPlace; bool inEscape = false; import dyaml.nogcutil; - auto appender = appenderNoGC(cast(char[])tokenValue); + auto appender = appender!(char[])(); for(char[] oldValue = tokenValue; !oldValue.empty();) { const dchar c = oldValue.front(); @@ -549,7 +549,7 @@ final class Parser { if(c != '\\') { - if(notInPlace is null) { appender.putDChar(c); } + if(notInPlace is null) { appender.put(c); } else { notInPlace ~= c; } continue; } @@ -570,7 +570,7 @@ final class Parser // many-byte unicode chars if(c != 'L' && c != 'P') { - appender.putDChar(dyaml.escapes.fromEscape(c)); + appender.put(dyaml.escapes.fromEscape(c)); continue; } // Need to duplicate as we won't fit into @@ -596,10 +596,8 @@ final class Parser assert(!hex.canFind!(d => !d.isHexDigit), "Scanner must ensure the hex string is valid"); - bool overflow; - const decoded = cast(dchar)parseNoGC!int(hex, 16u, overflow); - assert(!overflow, "Scanner must ensure there's no overflow"); - if(notInPlace is null) { appender.putDChar(decoded); } + const decoded = cast(dchar)parse!int(hex, 16u); + if(notInPlace is null) { appender.put(decoded); } else { notInPlace ~= decoded; } continue; } diff --git a/source/dyaml/reader.d b/source/dyaml/reader.d index 2d1f020..c197e60 100644 --- a/source/dyaml/reader.d +++ b/source/dyaml/reader.d @@ -661,16 +661,14 @@ public: } /// Destroy the transaction and revert it if it hasn't been committed yet. - /// - /// Does nothing for a default-initialized transaction. - ~this() @safe pure nothrow @nogc + void end() @safe pure nothrow @nogc { - if(builder_ is null || committed_) { return; } - assert(builder_.endStackUsed_ == stackLevel_ + 1, + assert(builder_ && builder_.endStackUsed_ == stackLevel_ + 1, "Parent transactions don't fully contain child transactions"); builder_.pop(); builder_ = null; } + } private: diff --git a/source/dyaml/scanner.d b/source/dyaml/scanner.d index df0b2f9..f833230 100644 --- a/source/dyaml/scanner.d +++ b/source/dyaml/scanner.d @@ -235,7 +235,13 @@ final class Scanner /// Build an error message in msgBuffer_ and return it as a string. 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 @@ -739,7 +745,7 @@ final class Scanner tokens_.push(plain); } - pure nothrow @nogc: + pure: ///Check if the next token is DIRECTIVE: ^ '%' ... bool checkDirective() @safe @@ -1326,7 +1332,7 @@ final class Scanner // (which are at the end of the scalar). Otherwise re remove them (end the // transaction). if(chomping == Chomping.Keep) { breaksTransaction.commit(); } - else { breaksTransaction.__dtor(); } + else { breaksTransaction.end(); } if(chomping != Chomping.Strip && lineBreak != int.max) { // 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]; reader_.sliceBuilder.write(escapeStart); reader_.sliceBuilder.write(hex); - bool overflow; // Note: This is just error checking; Parser does the actual // escaping (otherwise we could accidentally create an // escape sequence here that wasn't in input, breaking the // escaping code in parser, which is in parser because it // can't always be done in place) - parseNoGC!int(hex, 16u, overflow); - if(overflow) + try { + parse!int(hex, 16u); + } + catch (Exception) { error("While scanning a double quoted scalar", startMark, "overflow when parsing an escape sequence of " ~ @@ -1799,7 +1806,7 @@ final class Scanner } } - spacesTransaction.__dtor(); + spacesTransaction.end(); char[] slice = reader_.sliceBuilder.finish(); return scalarToken(startMark, endMark, slice, ScalarStyle.Plain); @@ -1835,7 +1842,7 @@ final class Scanner const lineBreak = scanLineBreak(); allowSimpleKey_ = true; - static bool end(Reader reader_) @safe pure nothrow @nogc + static bool end(Reader reader_) @safe pure { const prefix = reader_.prefix(3); return ("---" == prefix || "..." == prefix)