1// Copyright 2017 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package saltpack
5
6// chunker is an interface for a type that emits a sequence of
7// plaintext chunks.
8//
9// Implementations should follow exampleChunker in
10// chunk_reader_test.go pretty closely.
11type chunker interface {
12	// getNextChunk() returns a plaintext chunk with an error. If
13	// the chunk is empty, the error must be non-nil. Once
14	// getNextChunk() returns a non-nil error (which may be
15	// io.EOF), it can assume that it will never be called again.
16	getNextChunk() ([]byte, error)
17}
18
19// chunkReader is an io.Reader adaptor for chunker.
20type chunkReader struct {
21	chunker   chunker
22	prevChunk []byte
23	prevErr   error
24}
25
26func newChunkReader(chunker chunker) *chunkReader {
27	return &chunkReader{chunker: chunker}
28}
29
30func (r *chunkReader) Read(p []byte) (n int, err error) {
31	// Copy data into p until it is full, or getNextChunk()
32	// returns a non-nil error.
33	for {
34		// Drain r.prevChunk first before checking for an error.
35		if len(r.prevChunk) > 0 {
36			copied := copy(p[n:], r.prevChunk)
37			n += copied
38			r.prevChunk = r.prevChunk[copied:]
39			if len(r.prevChunk) > 0 {
40				// p is full.
41				return n, nil
42			}
43		}
44
45		if r.prevErr != nil {
46			// r.prevChunk is fully drained, so return the
47			// error.
48			return n, r.prevErr
49		}
50
51		r.prevChunk, r.prevErr = r.chunker.getNextChunk()
52		if len(r.prevChunk) == 0 && r.prevErr == nil {
53			panic("empty chunk and nil error")
54		}
55	}
56}
57