diff --git a/src/preserves.nim b/src/preserves.nim index 60081bc..9b51fd0 100644 --- a/src/preserves.nim +++ b/src/preserves.nim @@ -5,9 +5,9 @@ import std/[options, sets, sequtils, strutils, tables, typetraits] from std/algorithm import sort from std/json import escapeJson, escapeJsonUnquoted import bigints -import ./preserves/private/[encoding, decoding, dot, macros, parsing, texts, values] +import ./preserves/private/[buffering, encoding, decoding, dot, macros, parsing, texts, values] -export encoding, decoding, parsing, texts, values +export buffering, encoding, decoding, parsing, texts, values when defined(tracePreserves): when defined(posix): diff --git a/src/preserves/private/buffering.nim b/src/preserves/private/buffering.nim new file mode 100644 index 0000000..3a5dff8 --- /dev/null +++ b/src/preserves/private/buffering.nim @@ -0,0 +1,79 @@ +# SPDX-FileCopyrightText: ☭ Emery Hemingway +# SPDX-License-Identifier: Unlicense + +import std/[endians, options, streams, strutils] +import bigints +import ./decoding, ./parsing, ./values + +type BufferedDecoder* = object + ## Type for buffering binary Preserves before decoding. + stream: StringStream + appendPosition, decodePosition, maxSize: int + +proc newBufferedDecoder*(maxSize = 4096): BufferedDecoder = + ## Create a new `newBufferedDecoder`. + runnableExamples: + var + buf = newBufferedDecoder() + bin = encode(parsePreserves("")) + buf.feed(bin[0..2]) + buf.feed(bin[3..bin.high]) + var (success, pr) = decode(buf) + assert success + assert $pr == "" + BufferedDecoder( + stream: newStringStream(newStringOfCap(maxSize)), + maxSize: maxSize, + ) + +proc feed*(dec: var BufferedDecoder; buf: pointer; len: int) = + assert len > 0 + if dec.maxSize > 0 and dec.maxSize < (dec.appendPosition + len): + raise newException(IOError, "BufferedDecoder at maximum buffer size") + dec.stream.setPosition(dec.appendPosition) + dec.stream.writeData(buf, len) + inc(dec.appendPosition, len) + assert dec.appendPosition == dec.stream.getPosition() + +proc feed*[T: byte|char](dec: var BufferedDecoder; data: openarray[T]) = + if data.len > 0: + dec.feed(addr data[0], data.len) + +proc feed*[T: byte|char](dec: var BufferedDecoder; data: openarray[T]; slice: Slice[int]) = + let n = slice.b + 1 - slice.a + if n > 0: + dec.feed(addr data[slice.a], n) + +proc decode*(dec: var BufferedDecoder): Option[Value] = + ## Decode from `dec`. If decoding fails the internal position of the + ## decoder does not advance. + if dec.appendPosition > 0: + assert(dec.decodePosition < dec.appendPosition) + dec.stream.setPosition(dec.decodePosition) + try: + result = dec.stream.decodePreserves.some + dec.decodePosition = dec.stream.getPosition() + if dec.decodePosition == dec.appendPosition: + dec.stream.setPosition(0) + dec.stream.data.setLen(0) + dec.appendPosition = 0 + dec.decodePosition = 0 + except IOError: + discard + +proc parse*(dec: var BufferedDecoder): Option[Value] = + ## Parse from `dec`. If parsing fails the internal position of the + ## decoder does not advance. + if dec.appendPosition > 0: + assert(dec.decodePosition < dec.appendPosition) + dec.stream.setPosition(dec.decodePosition) + try: + result = dec.stream.readAll.parsePreserves.some + dec.decodePosition = dec.stream.getPosition() + if dec.decodePosition == dec.appendPosition: + dec.stream.setPosition(0) + dec.stream.data.setLen(0) + dec.appendPosition = 0 + dec.decodePosition = 0 + except IOError, ValueError: + discard diff --git a/src/preserves/private/decoding.nim b/src/preserves/private/decoding.nim index 6987d99..b7b03a2 100644 --- a/src/preserves/private/decoding.nim +++ b/src/preserves/private/decoding.nim @@ -130,54 +130,3 @@ proc decodePreserves*(s: string): Value = proc decodePreserves*(s: seq[byte]): Value = ## Decode a byte-string of binary-encoded Preserves. decodePreserves(cast[string](s)) - -type BufferedDecoder* = object - ## Type for buffering binary Preserves before decoding. - stream: StringStream - appendPosition, decodePosition, maxSize: int - -proc newBufferedDecoder*(maxSize = 4096): BufferedDecoder = - ## Create a new `newBufferedDecoder`. - runnableExamples: - var - buf = newBufferedDecoder() - bin = encode(parsePreserves("")) - buf.feed(bin[0..2]) - buf.feed(bin[3..bin.high]) - var (success, pr) = decode(buf) - assert success - assert $pr == "" - BufferedDecoder( - stream: newStringStream(newStringOfCap(maxSize)), - maxSize: maxSize, - ) - -proc feed*(dec: var BufferedDecoder; buf: pointer; len: int) = - assert len > 0 - if dec.maxSize > 0 and dec.maxSize < (dec.appendPosition + len): - raise newException(IOError, "BufferedDecoder at maximum buffer size") - dec.stream.setPosition(dec.appendPosition) - dec.stream.writeData(buf, len) - inc(dec.appendPosition, len) - assert dec.appendPosition == dec.stream.getPosition() - -proc feed*[T: byte|char](dec: var BufferedDecoder; data: openarray[T]) = - if data.len > 0: - dec.feed(unsafeAddr data[0], data.len) - -proc decode*(dec: var BufferedDecoder): Option[Value] = - ## Decode from `dec`. If decoding fails the internal position of the - ## decoder does not advance. - if dec.appendPosition > 0: - assert(dec.decodePosition < dec.appendPosition) - dec.stream.setPosition(dec.decodePosition) - try: - result = dec.stream.decodePreserves.some - dec.decodePosition = dec.stream.getPosition() - if dec.decodePosition == dec.appendPosition: - dec.stream.setPosition(0) - dec.stream.data.setLen(0) - dec.appendPosition = 0 - dec.decodePosition = 0 - except IOError: - discard