Fast bytes decoder with error reporting and oneOf branching.
Each Decoder carries two execution strategies:
Fast path — a raw elm/bytes Bytes.Decode.Decoder composed via
Decode.map2, Decode.andThen, Decode.loop, etc. When present, decode
executes a single Decode.decode call with zero per-step overhead. This
matches the performance of hand-written elm/bytes decoders.
Slow path — a state-passing function that tracks byte offsets, reports
structured errors, and supports backtracking in oneOf. This path is only
executed when the fast path is unavailable or returns Nothing (i.e. an
error occurred).
The key insight is that most decoders succeed on well-formed input. By deferring error tracking to a re-decode, the happy path pays no cost for error reporting.
Not every combinator can be expressed as a raw Bytes.Decode.Decoder:
succeed, map, map2–map5, keep, ignore, skip,
andThen, loop, repeat, and all primitives.fail (no decoder can produce a value), oneOf
(requires backtracking which Bytes.Decode.Decoder cannot do mid-stream).andThen and loop remain fast as long as the
callback returns a decoder with a fast path. If a callback branch returns
fail, that branch uses Decode.fail to signal the raw decoder, which causes
Decode.decode to return Nothing and triggers the slow re-decode.In practice, fail branches inside andThen are error-handling code that only
runs on malformed input — exactly when we want to re-decode with error tracking.
Run the given decoder on the provided bytes.
First tries the fast path (a single Decode.decode call). If the fast path is
unavailable or fails, re-decodes with the slow path to produce a detailed error.
Always succeed with the given value, consuming no input.
Always fail with the given error.
This decoder has no fast path — using it (even in a branch of andThen) will
cause decode to fall back to the slow path for error reporting.
Add context to errors that may occur during decoding.
The fast path is preserved (context only matters on the error path).
Decode one byte as an unsigned integer from 0 to 255.
Decode two bytes as an unsigned integer from 0 to 65535.
Decode four bytes as an unsigned integer from 0 to 4294967295.
Decode one byte as a signed integer from -128 to 127.
Decode two bytes as a signed integer from -32768 to 32767.
Decode four bytes as a signed integer from -2147483648 to 2147483647.
Transform the value a decoder produces.
Combine what 2 decoders produce.
Combine what 3 decoders produce.
Combine what 4 decoders produce.
Combine what 5 decoders produce.
Decode a value and apply it to the function held by the previous decoder.
import Bytes.Decoder as BD
BD.succeed Record
|> BD.keep BD.unsignedInt8
|> BD.keep BD.unsignedInt8
|> BD.keep BD.unsignedInt8Run a decoder but discard its result, keeping the previous value.
The ignored decoder must still succeed for the pipeline to succeed.
Skip n bytes, then continue with the given decoder.
Decode one thing, then decode another thing based on the result.
The fast path uses Decode.andThen. If the callback returns a decoder without
a fast path (e.g. via fail), the fast decoder signals failure via
Decode.fail, causing decode to fall back to the slow path.
Try each decoder in order, returning the first success.
This decoder has no fast path — it always uses the slow path with backtracking.
For tag-based dispatch, prefer andThen which preserves the fast path.
Collects all errors on failure for diagnostic purposes.
Caution: oneOf backtracks by byte offset, not by meaning. If two branches
consume the same number of bytes but interpret them differently (e.g.
unsignedInt32 vs float32), the first branch that successfully reads those
bytes wins — even if the value makes no sense for your domain. In general,
raw byte sequences are untagged, so oneOf cannot distinguish between
alternatives that happen to have the same byte layout. Prefer andThen with
an explicit tag byte whenever possible.
Decode exactly count copies of the same decoder into a list.
If the inner decoder has a fast path, repeat preserves it by composing
a single Decode.loop decoder.
Represent the next step of a loop: either continue with updated state, or finish with a final value.
Decode in a loop. Each iteration produces either Loop (continue with new
state) or Done (finish with a value).
The fast path uses Decode.loop (a tight while loop in the JS kernel).
If any iteration's callback returns a decoder without a fast path, the fast
decoder signals failure via Decode.fail and decode falls back to the slow
path.
Produce the current byte offset in the input.
This decoder has no fast path — using it will cause decode to fall back to
the slow path.
import Bytes.Decoder as BD
BD.decode BD.position (fromList [ 1, 2, 3 ])
--> Ok BD.startOfInput
BD.decode
(BD.unsignedInt8
|> BD.andThen (\_ -> BD.position)
)
(fromList [ 1, 2, 3 ])
|> Result.map (\\p -> p == BD.startOfInput)
--> Ok FalseThe position at the very start of the input (offset 0).
Useful as the relativeTo argument in randomAccess.
Read data at an arbitrary offset, then resume where you left off.
This decoder has no fast path.
As an example, consider data laid out as:
import Bytes exposing (Bytes)
import Bytes.Decoder as BD exposing (Decoder)
import Bytes.Encode as E
input : Bytes
input =
[ E.unsignedInt8 5
, E.unsignedInt8 15
, E.unsignedInt8 6
, E.string (String.repeat 12 "\u{0000}")
, E.string "hello"
]
|> E.sequence
|> E.encode
stringAtOffset : Decoder c e String
stringAtOffset =
BD.map2 Tuple.pair BD.unsignedInt8 BD.unsignedInt8
|> BD.andThen
(\( len, offset ) ->
BD.randomAccess
{ offset = offset, relativeTo = BD.startOfInput }
(BD.string len)
)
BD.decode stringAtOffset input
--> Ok "hello"Parsing continues at the sequential position after randomAccess returns:
final : Decoder c e { string : String, number : Int }
final =
BD.succeed (\s n -> { string = s, number = n })
|> BD.keep stringAtOffset
|> BD.keep BD.unsignedInt8
BD.decode final input
--> Ok { string = "hello", number = 6 }
A decoder that reads binary data and produces either a value or an error.
Internally, it carries an optional raw
Bytes.Decode.Decoder(fast path) and a state-passing function (slow path for error reporting).