mirror of
https://github.com/jackc/pgx.git
synced 2025-04-28 14:07:51 +00:00
Altered chunkreader to never reuse memory. Altered pgproto3 to to copy memory when decoding. Renamed UnmarshalBinary to Decode because of changed semantics.
90 lines
1.7 KiB
Go
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
|
|
}
|