Interface IValueBoundaryScanner
- Namespace
- ShapeShift
- Assembly
- ShapeShift.dll
Locates the boundary of the next complete, self-delimited top-level value within a growing buffer, without fully parsing or converting it.
public interface IValueBoundaryScanner
Remarks
Implementations back the asynchronous Stream/PipeReader based
deserialization APIs offered by each format. Because format decoders are typically ref structs
that cannot survive an await, those APIs cannot incrementally resume a paused conversion.
Instead, they use an IValueBoundaryScanner to buffer input, a chunk at a time, only until a
complete top-level value is known to be present, and only then invoke the ordinary synchronous decoder
exactly once over that value's bytes.
A single instance is mutable and stateful across repeated calls to TryScan(in ReadOnlySequence<byte>, bool, out SequencePosition, out SequencePosition): each call may consume only part of a value (returning false to request more input), and the instance remembers its progress so that the next call can resume rather than re-scan from the beginning. Once a call returns true, the instance is ready to scan a subsequent top-level value (e.g. the next element of a newline-delimited stream) starting from a fresh buffer whose start coincides with the position immediately after the value that was just found.
Implementations must not throw for merely-incomplete input; they should return false so the
caller can supply more bytes (or fail with a clear error once isFinalBlock is true and
no more bytes will ever come). Genuinely malformed input (e.g. an unrecognized token) may be reported by throwing
DecoderException.
Implementations report, via the examined output of TryScan(in ReadOnlySequence<byte>, bool, out SequencePosition, out SequencePosition), exactly how much of the
buffer they guarantee they will never need to re-inspect -- even when a call returns false.
This lets a caller (such as ReadValueAsync<T>(PipeReader, IValueBoundaryScanner, Func<ReadOnlySequence<byte>, T?>, long, CancellationToken)) release that prefix back to
its underlying PipeReader immediately. Because the eventual decode step still
needs every byte of the value once it is fully recognized, an implementation may only report an
examined position past a value's first byte once that value is complete (its end is
known); before a value has begun, however, it is free to report progress through any bytes it can prove are
not part of one (for example, insignificant whitespace between JSON tokens), so pure separator bytes between
values need not be held onto merely because the next value has not yet arrived.
Methods
TryScan(in ReadOnlySequence<byte>, bool, out SequencePosition, out SequencePosition)
Attempts to locate the end of the next complete top-level value at the start of buffer.
bool TryScan(in ReadOnlySequence<byte> buffer, bool isFinalBlock, out SequencePosition end, out SequencePosition examined)
Parameters
bufferReadOnlySequence<byte>All input buffered so far and not yet consumed. Callers are expected to advance their underlying reader past
examinedafter every call (whether it returns true or false), so on the next call for the same value,bufferbegins exactly where the previous call'sexaminedleft off.isFinalBlockbooltrue if no further input will ever be appended to
buffer(the source has reached its end).endSequencePositionReceives the position, within
buffer, immediately after the complete value, if this method returns true; otherwise default.examinedSequencePositionReceives the position, within
buffer, up through which this instance guarantees it will never need to look again -- regardless of whether this call returns true or false. When this method returns true, this always equalsend. When it returns false, this isbuffer's start unless the implementation can prove the skipped-over bytes are not part of any value (for example, whitespace preceding the next JSON token) and therefore safe to discard even though the value itself has not yet been found. A caller that has reachedisFinalBlockand finds this method returns false withexaminedequal tobuffer's end may conclude that no further value begins here (a graceful end of a sequence of values), rather than that the input ended in the middle of one.
Returns
- bool
true if
buffercontains (starting at its start) one complete top-level value; false if more input is required before that can be determined.
Exceptions
- DecoderException
Thrown when the buffered input is definitely malformed.