1package multihash
2
3import (
4	"errors"
5	"io"
6	"math"
7
8	"github.com/multiformats/go-varint"
9)
10
11// Reader is an io.Reader wrapper that exposes a function
12// to read a whole multihash, parse it, and return it.
13type Reader interface {
14	io.Reader
15
16	ReadMultihash() (Multihash, error)
17}
18
19// Writer is an io.Writer wrapper that exposes a function
20// to write a whole multihash.
21type Writer interface {
22	io.Writer
23
24	WriteMultihash(Multihash) error
25}
26
27// NewReader wraps an io.Reader with a multihash.Reader
28func NewReader(r io.Reader) Reader {
29	return &mhReader{r}
30}
31
32// NewWriter wraps an io.Writer with a multihash.Writer
33func NewWriter(w io.Writer) Writer {
34	return &mhWriter{w}
35}
36
37type mhReader struct {
38	r io.Reader
39}
40
41func (r *mhReader) Read(buf []byte) (n int, err error) {
42	return r.r.Read(buf)
43}
44
45func (r *mhReader) ReadByte() (byte, error) {
46	if br, ok := r.r.(io.ByteReader); ok {
47		return br.ReadByte()
48	}
49	var b [1]byte
50	n, err := r.r.Read(b[:])
51	if n == 1 {
52		return b[0], nil
53	}
54	if err == nil {
55		if n != 0 {
56			panic("reader returned an invalid length")
57		}
58		err = io.ErrNoProgress
59	}
60	return 0, err
61}
62
63func (r *mhReader) ReadMultihash() (Multihash, error) {
64	code, err := varint.ReadUvarint(r)
65	if err != nil {
66		return nil, err
67	}
68
69	length, err := varint.ReadUvarint(r)
70	if err != nil {
71		return nil, err
72	}
73	if length > math.MaxInt32 {
74		return nil, errors.New("digest too long, supporting only <= 2^31-1")
75	}
76
77	buf := make([]byte, varint.UvarintSize(code)+varint.UvarintSize(length)+int(length))
78	n := varint.PutUvarint(buf, code)
79	n += varint.PutUvarint(buf[n:], length)
80	if _, err := io.ReadFull(r.r, buf[n:]); err != nil {
81		return nil, err
82	}
83
84	return Cast(buf)
85}
86
87type mhWriter struct {
88	w io.Writer
89}
90
91func (w *mhWriter) Write(buf []byte) (n int, err error) {
92	return w.w.Write(buf)
93}
94
95func (w *mhWriter) WriteMultihash(m Multihash) error {
96	_, err := w.w.Write([]byte(m))
97	return err
98}
99