/
reader.go
103 lines (95 loc) · 2.09 KB
/
reader.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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// Copyright 2020 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package stack
import (
"bytes"
"errors"
"io"
)
var (
errBufferFull = errors.New("buffer full")
)
type reader struct {
buf [16 * 1024]byte
rd io.Reader
r, w int
err error
}
// fill reads a new chunk into the buffer.
func (r *reader) fill() {
// Slide existing data to beginning.
if r.r > 0 {
copy(r.buf[:], r.buf[r.r:r.w])
r.w -= r.r
r.r = 0
}
if r.w >= len(r.buf) {
panic("tried to fill full buffer")
}
// Read new data: try a limited number of times.
for i := 100; i > 0; i-- {
n, err := r.rd.Read(r.buf[r.w:])
if n < 0 {
panic("reader returned negative count from Read")
}
r.w += n
if err != nil {
r.err = err
return
}
if n > 0 {
return
}
}
r.err = io.ErrNoProgress
}
func (r *reader) buffered() []byte {
return r.buf[r.r:r.w]
}
func (r *reader) readSlice() ([]byte, error) {
for s := 0; ; r.fill() {
if i := bytes.IndexByte(r.buf[r.r+s:r.w], '\n'); i >= 0 {
i += s
line := r.buf[r.r : r.r+i+1]
r.r += i + 1
return line, nil
}
if r.err != nil {
line := r.buf[r.r:r.w]
r.r = r.w
err := r.err
r.err = nil
return line, err
}
if r.w-r.r == len(r.buf) {
r.r = r.w
return r.buf[:], errBufferFull
}
s = r.w - r.r
}
}
// readLine is our own implementation of ReadBytes().
//
// We try to use readSlice() as much as we can but we need to tolerate if an
// input line is longer than the buffer specified at Reader creation. Not using
// the more complicated slice of slices that Reader.ReadBytes() uses since it
// should not happen often here. Instead bootstrap the memory allocation by
// starting with 4x buffer size, which should get most cases with a single
// allocation.
func (r *reader) readLine() ([]byte, error) {
var d []byte
for {
f, err := r.readSlice()
if err != errBufferFull {
if d == nil {
return f, err
}
return append(d, f...), err
}
if d == nil {
d = make([]byte, 0, len(f)*4)
}
d = append(d, f...)
}
}