87 lines
2.6 KiB
V
87 lines
2.6 KiB
V
module cbor
|
|
|
|
import io
|
|
|
|
// Stream I/O wrappers over the standard `io.Reader` / `io.Writer`
|
|
// interfaces. Use these for files, sockets, pipes — anywhere the
|
|
// payload doesn't fit cleanly in a single `[]u8`.
|
|
|
|
// encode_to serialises `val` into an internal buffer, then writes the
|
|
// bytes to `w` in a loop until everything is accepted. Errors on the
|
|
// first I/O failure.
|
|
pub fn encode_to[T](val T, mut w io.Writer, opts EncodeOpts) ! {
|
|
bytes := encode[T](val, opts)!
|
|
mut written := 0
|
|
for written < bytes.len {
|
|
n := w.write(bytes[written..])!
|
|
if n == 0 {
|
|
return error('cbor: writer stopped accepting bytes at ${written}/${bytes.len}')
|
|
}
|
|
written += n
|
|
}
|
|
}
|
|
|
|
// decode_from reads bytes from `r` until EOF (or until
|
|
// `DecodeOpts.max_stream_bytes` is hit) and decodes a single top-level
|
|
// value. For multi-value streams, use `Unpacker` directly on a
|
|
// pre-buffered slice.
|
|
//
|
|
// Always set `max_stream_bytes` on untrusted readers — otherwise a peer
|
|
// that never sends EOF blocks the call forever.
|
|
pub fn decode_from[T](mut r io.Reader, opts DecodeOpts) !T {
|
|
bounded := opts.max_stream_bytes > 0
|
|
mut buf := []u8{cap: 4096}
|
|
for {
|
|
if bounded && buf.len >= opts.max_stream_bytes {
|
|
return error('cbor: stream exceeded max_stream_bytes (${opts.max_stream_bytes})')
|
|
}
|
|
slot_len := if bounded {
|
|
cap_left := opts.max_stream_bytes - buf.len
|
|
if cap_left < 4096 {
|
|
cap_left
|
|
} else {
|
|
4096
|
|
}
|
|
} else {
|
|
4096
|
|
}
|
|
mut slot := []u8{len: slot_len}
|
|
// Treat io.Eof as the legitimate end of the stream; any other
|
|
// reader error must propagate so callers don't mistake a
|
|
// transport failure for a successful (truncated) decode. The
|
|
// unbounded branch used to delegate to `io.read_all`, which
|
|
// also swallows non-EOF errors — same loop here keeps the
|
|
// strict semantic regardless of whether `max_stream_bytes` is set.
|
|
n := r.read(mut slot) or {
|
|
if err is io.Eof {
|
|
break
|
|
}
|
|
return err
|
|
}
|
|
if n == 0 {
|
|
break
|
|
}
|
|
buf << slot[..n]
|
|
}
|
|
return decode[T](buf, opts)!
|
|
}
|
|
|
|
// pack_to is the streaming sibling of `encode_to`, for users who built
|
|
// their payload manually via the `Packer` API. Errors if any
|
|
// indefinite-length container is still open — emitting half-closed
|
|
// CBOR would produce a payload no decoder can parse.
|
|
pub fn (mut p Packer) pack_to(mut w io.Writer) ! {
|
|
if !p.is_complete() {
|
|
return error('cbor: pack_to called with an open indefinite-length item — close it with pack_break first')
|
|
}
|
|
bytes := p.bytes()
|
|
mut written := 0
|
|
for written < bytes.len {
|
|
n := w.write(bytes[written..])!
|
|
if n == 0 {
|
|
return error('cbor: writer stopped at ${written}/${bytes.len}')
|
|
}
|
|
written += n
|
|
}
|
|
}
|