Class MsgPackValueBoundaryScanner
- Namespace
- ShapeShift.MsgPack
- Assembly
- ShapeShift.MsgPack.dll
Recognizes the boundary of one complete, top-level MessagePack value by walking its self-delimiting headers, without buffering more than that one value requires and without fully decoding it.
public sealed class MsgPackValueBoundaryScanner : IValueBoundaryScanner
- Inheritance
-
MsgPackValueBoundaryScanner
- Implements
- Inherited Members
Remarks
MessagePack has no BCL equivalent of Utf8JsonReader's incremental-parsing
support, so this type implements a small, dedicated, resumable scan. It intentionally does not reuse
MsgPackDecoder for this purpose: that type is a ref struct whose Skip()
recursion uses the CLR call stack to track nested container state, which cannot be paused and resumed
across an await boundary, and re-running Deserialize() from the start on every
buffer growth would re-invoke user converter callbacks and reference-equality tracking for the same
already-scanned prefix multiple times.
Instead, this scanner walks the same byte-code grammar MsgPackDecoder understands, but only far enough to count bytes: it tracks how many sibling values remain in each open container (as a stack of counts) and how many payload bytes remain to be skipped for the value currently in progress, persisting both across calls so a value that arrives one small chunk at a time is recognized without re-scanning bytes already accounted for.
Container and string/binary/extension lengths are tracked as long rather than int specifically so that a hostile or corrupt 32-bit length header (up to MaxValue, doubled for map entry counts) cannot overflow the counters used here. This scanner never allocates memory proportional to a claimed length -- it only decrements a counter as real bytes arrive -- but (unlike its JSON counterpart) it cannot release any of those bytes back to the caller before the whole value is recognized: MessagePack has no concept of insignificant bytes between values, so every byte examined here is unconditionally part of the value that the caller's decode step still needs in full afterward. The guard against unbounded buffering of a hostile value is therefore entirely the caller-supplied maximum buffered size (see ReadValueAsync<T>(PipeReader, IValueBoundaryScanner, Func<ReadOnlySequence<byte>, T?>, long, CancellationToken)), not this type.
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.
public 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.