open-vault/vendor/github.com/jackc/pgx/chunkreader/chunkreader.go
2018-10-03 09:55:26 -07:00

90 lines
1.7 KiB
Go

package chunkreader
import (
"io"
)
type ChunkReader struct {
r io.Reader
buf []byte
rp, wp int // buf read position and write position
options Options
}
type Options struct {
MinBufLen int // Minimum buffer length
}
func NewChunkReader(r io.Reader) *ChunkReader {
cr, err := NewChunkReaderEx(r, Options{})
if err != nil {
panic("default options can't be bad")
}
return cr
}
func NewChunkReaderEx(r io.Reader, options Options) (*ChunkReader, error) {
if options.MinBufLen == 0 {
options.MinBufLen = 4096
}
return &ChunkReader{
r: r,
buf: make([]byte, options.MinBufLen),
options: options,
}, nil
}
// Next returns buf filled with the next n bytes. If an error occurs, buf will
// be nil.
func (r *ChunkReader) Next(n int) (buf []byte, err error) {
// n bytes already in buf
if (r.wp - r.rp) >= n {
buf = r.buf[r.rp : r.rp+n]
r.rp += n
return buf, err
}
// available space in buf is less than n
if len(r.buf) < n {
r.copyBufContents(r.newBuf(n))
}
// buf is large enough, but need to shift filled area to start to make enough contiguous space
minReadCount := n - (r.wp - r.rp)
if (len(r.buf) - r.wp) < minReadCount {
newBuf := r.newBuf(n)
r.copyBufContents(newBuf)
}
if err := r.appendAtLeast(minReadCount); err != nil {
return nil, err
}
buf = r.buf[r.rp : r.rp+n]
r.rp += n
return buf, nil
}
func (r *ChunkReader) appendAtLeast(fillLen int) error {
n, err := io.ReadAtLeast(r.r, r.buf[r.wp:], fillLen)
r.wp += n
return err
}
func (r *ChunkReader) newBuf(size int) []byte {
if size < r.options.MinBufLen {
size = r.options.MinBufLen
}
return make([]byte, size)
}
func (r *ChunkReader) copyBufContents(dest []byte) {
r.wp = copy(dest, r.buf[r.rp:r.wp])
r.rp = 0
r.buf = dest
}