decodeAll static method
Read multiple frames from a buffer.
Returns the decoded frames and the number of bytes consumed.
When maxPayloadLen is non-null, a frame declaring a larger payload is
rejected from the header alone — the oversized payload is never sliced or
buffered — by throwing RpcFrameException. Frames decoded before the
offending one are not returned on throw; the caller is expected to treat
the error as a protocol violation and tear down.
maxMetadataLen is enforced the same way, from the header alone.
Malformed metadata in an otherwise well-sized frame is different, and
onMalformedMetadata is how a caller says so. The framing is INTACT there
— the declared length is known and the payload is fully present — so the
frame can be stepped over and decoding continues in sync. With the
callback the offending stream id is reported and the frame skipped;
without it the old throw is kept, which is what a server wants.
Implementation
static (List<RpcDecodedFrame>, int) decodeAll(
Uint8List data, {
int? maxPayloadLen,
int? maxMetadataLen,
void Function(int streamId)? onMalformedMetadata,
}) {
final frames = <RpcDecodedFrame>[];
var offset = 0;
if (data.length < headerSize) return (frames, offset);
// One view over the whole buffer, and no intermediate frame slice.
final view = ByteData.sublistView(data);
while (offset + headerSize <= data.length) {
// A size violation throws here, before the payload is known to be
// present, and is never stepped over.
final header = _readHeader(
view,
offset,
maxPayloadLen: maxPayloadLen,
maxMetadataLen: maxMetadataLen,
);
final end = offset + headerSize + header.payloadLen;
if (data.length < end) break;
if (onMalformedMetadata == null) {
frames.add(_frameAt(data, offset, header));
} else {
try {
frames.add(_frameAt(data, offset, header));
} on RpcFrameException {
// Only a complete metadata payload that will not parse gets here,
// and its framing is intact.
onMalformedMetadata(header.streamId);
}
}
offset = end;
}
return (frames, offset);
}