v / vlib / encoding / cbor / stream.v
86 lines · 81 sloc · 2.62 KB · 468855eef1db0ff73c62be2d1bf176ffa0e1478e
Raw
1module cbor
2
3import io
4
5// Stream I/O wrappers over the standard `io.Reader` / `io.Writer`
6// interfaces. Use these for files, sockets, pipes — anywhere the
7// payload doesn't fit cleanly in a single `[]u8`.
8
9// encode_to serialises `val` into an internal buffer, then writes the
10// bytes to `w` in a loop until everything is accepted. Errors on the
11// first I/O failure.
12pub fn encode_to[T](val T, mut w io.Writer, opts EncodeOpts) ! {
13 bytes := encode[T](val, opts)!
14 mut written := 0
15 for written < bytes.len {
16 n := w.write(bytes[written..])!
17 if n == 0 {
18 return error('cbor: writer stopped accepting bytes at ${written}/${bytes.len}')
19 }
20 written += n
21 }
22}
23
24// decode_from reads bytes from `r` until EOF (or until
25// `DecodeOpts.max_stream_bytes` is hit) and decodes a single top-level
26// value. For multi-value streams, use `Unpacker` directly on a
27// pre-buffered slice.
28//
29// Always set `max_stream_bytes` on untrusted readers — otherwise a peer
30// that never sends EOF blocks the call forever.
31pub fn decode_from[T](mut r io.Reader, opts DecodeOpts) !T {
32 bounded := opts.max_stream_bytes > 0
33 mut buf := []u8{cap: 4096}
34 for {
35 if bounded && buf.len >= opts.max_stream_bytes {
36 return error('cbor: stream exceeded max_stream_bytes (${opts.max_stream_bytes})')
37 }
38 slot_len := if bounded {
39 cap_left := opts.max_stream_bytes - buf.len
40 if cap_left < 4096 {
41 cap_left
42 } else {
43 4096
44 }
45 } else {
46 4096
47 }
48 mut slot := []u8{len: slot_len}
49 // Treat io.Eof as the legitimate end of the stream; any other
50 // reader error must propagate so callers don't mistake a
51 // transport failure for a successful (truncated) decode. The
52 // unbounded branch used to delegate to `io.read_all`, which
53 // also swallows non-EOF errors — same loop here keeps the
54 // strict semantic regardless of whether `max_stream_bytes` is set.
55 n := r.read(mut slot) or {
56 if err is io.Eof {
57 break
58 }
59 return err
60 }
61 if n == 0 {
62 break
63 }
64 buf << slot[..n]
65 }
66 return decode[T](buf, opts)!
67}
68
69// pack_to is the streaming sibling of `encode_to`, for users who built
70// their payload manually via the `Packer` API. Errors if any
71// indefinite-length container is still open — emitting half-closed
72// CBOR would produce a payload no decoder can parse.
73pub fn (mut p Packer) pack_to(mut w io.Writer) ! {
74 if !p.is_complete() {
75 return error('cbor: pack_to called with an open indefinite-length item — close it with pack_break first')
76 }
77 bytes := p.bytes()
78 mut written := 0
79 for written < bytes.len {
80 n := w.write(bytes[written..])!
81 if n == 0 {
82 return error('cbor: writer stopped at ${written}/${bytes.len}')
83 }
84 written += n
85 }
86}
87