# SPDX-FileCopyrightText: ☭ Emery Hemingway # SPDX-License-Identifier: Unlicense import std/[assertions, 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