Beyond Big O

Framed message decoder

Framed message decoder

Medium

Your task

  • A byte stream carries messages with a 2-byte unsigned big-endian payload length followed by that many payload bytes.
  • Network reads can split a header or payload anywhere, contain several frames, or be empty.
  • Decode the chunks in order and return all complete payloads, the unconsumed raw tail, and an error string.
  • This exercise models stream framing; do not open sockets.

Required API

  • DecodeFrames
    func DecodeFrames(chunks [][]byte, maxFrame int) DecodeResult
  • DecodeResult.Frames
    Frames [][]byte
  • DecodeResult.Remaining
    Remaining []byte
  • DecodeResult.Error
    Error string

Behavior

  • maxFrame is the largest allowed payload length, inclusive.
  • A zero-length payload is a complete frame and must be returned as an empty byte sequence.
  • Once a complete 2-byte header declares a length above maxFrame, stop decoding and return error "frame_too_large".
  • Keep all earlier complete frames.
  • Remaining must contain the invalid header and every byte after it, including bytes in later supplied chunks; do not resynchronize after the error.
  • If the stream ends with a partial header or an incomplete allowed payload, return that entire incomplete frame as Remaining and leave Error empty.
  • A truncated final frame is buffered data, not a protocol error.
  • On a fully consumed valid stream, Remaining is empty and Error is empty.
  • Payloads are arbitrary bytes, not text.
  • Input chunk boundaries must not change the decoded result for the same stream.

Constraints

  • Input is valid: 0–16 chunks, each containing 0–256 bytes, with at most 256 bytes in total. maxFrame is 0–128.
  • A byte is an integer from 0 to 255.
  • Do not mutate chunks, and keep separate calls independent.
  • Return independent payload/tail values, not views that change input.
  • Use only the standard library and bounded memory proportional to the supplied bytes.
Notes and example notation
  • Examples use arrays of byte values: {chunks, maxFrame} as input and {frames, remaining, error} as output.
  • The starter uses byte sequences for payloads and Remaining.

Examples

The runner calls your functions directly; you do not need to parse JSON.

Example 1 · empty

Arguments
chunks
[]
maxFrame
16
Expected return
{
  "frames": [],
  "remaining": [],
  "error": ""
}
Test data
Input
{"chunks":[],"maxFrame":16}
Expected output
{"frames":[],"remaining":[],"error":""}

Example 2 · split header and payload

Arguments
chunks
[
  [
    0
  ],
  [
    2,
    65
  ],
  [
    66,
    0,
    0
  ]
]
maxFrame
4
Expected return
{
  "frames": [
    [
      65,
      66
    ],
    []
  ],
  "remaining": [],
  "error": ""
}
Test data
Input
{"chunks":[[0],[2,65],[66,0,0]],"maxFrame":4}
Expected output
{"frames":[[65,66],[]],"remaining":[],"error":""}

Example 3 · complete plus tail

Arguments
chunks
[
  [
    0,
    1,
    0,
    0
  ]
]
maxFrame
1
Expected return
{
  "frames": [
    [
      0
    ]
  ],
  "remaining": [
    0
  ],
  "error": ""
}
Test data
Input
{"chunks":[[0,1,0,0]],"maxFrame":1}
Expected output
{"frames":[[0]],"remaining":[0],"error":""}

Example 4 · oversized after good frame

Arguments
chunks
[
  [
    0,
    1,
    65,
    0,
    3
  ],
  [
    9
  ],
  [
    9,
    9
  ]
]
maxFrame
2
Expected return
{
  "frames": [
    [
      65
    ]
  ],
  "remaining": [
    0,
    3,
    9,
    9,
    9
  ],
  "error": "frame_too_large"
}
Test data
Input
{"chunks":[[0,1,65,0,3],[9],[9,9]],"maxFrame":2}
Expected output
{"frames":[[65]],"remaining":[0,3,9,9,9],"error":"frame_too_large"}
Editor options
Typing preferences

Saved in this browser.

Restoring local drafts…