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...)
}
}
|