/
chunk_stream.go
55 lines (49 loc) · 1.58 KB
/
chunk_stream.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package latencystream
import "io"
const ReaderMaxChunkSize = 1 << 16
// A ChunkStream is a stream of binary data which arrives in discrete chunks.
// A ChunkStream has no notion of errors; only of completion.
type ChunkStream struct {
// Chunks is the channel on which chunks are delivered sequentially.
// It will be closed when the stream is complete.
Chunks <-chan []byte
// Close is a stream which, when closed, will signal the stream to stop delivering chunks.
// It allows the source of the stream to stop blocking while waiting to send output chunks.
Close chan<- struct{}
}
// NewChunkStreamReader creates a ChunkStream which wraps an io.Reader.
// The size of the chunks will vary depending on the io.Reader.
//
// After calling this, you should not read from the reader manually, even if you close the
// ChunkStream.
func NewChunkStreamReader(r io.Reader) ChunkStream {
rawInput := make(chan []byte)
cancel := make(chan struct{})
go func() {
defer close(rawInput)
for {
buffer := make([]byte, ReaderMaxChunkSize)
count, err := r.Read(buffer)
if count > 0 {
// NOTE: if someone is reading our output channel but we are cancelled, there is
// probabliity 1/2^n that outputs will be sent rather than reading the cancelChan.
// To address this, we first check for cancelChan before doing the second select{}.
select {
case <-cancel:
return
default:
}
select {
case rawInput <- buffer[:count]:
case <-cancel:
return
}
}
if err != nil {
return
}
}
close(rawInput)
}()
return ChunkStream{rawInput, cancel}
}