goext/dataext/bufferedReadCloser.go
2022-12-20 09:50:13 +01:00

158 lines
2.5 KiB
Go

package dataext
import (
"errors"
"io"
)
type brcMode int
const (
modeSourceReading = 0
modeSourceFinished = 1
modeBufferReading = 2
modeBufferFinished = 3
)
type BufferedReadCloser interface {
io.ReadCloser
BufferedAll() ([]byte, error)
Reset() error
}
type bufferedReadCloser struct {
buffer []byte
inner io.ReadCloser
mode brcMode
off int
}
func NewBufferedReadCloser(sub io.ReadCloser) BufferedReadCloser {
return &bufferedReadCloser{
buffer: make([]byte, 0, 1024),
inner: sub,
mode: modeSourceReading,
off: 0,
}
}
func (b *bufferedReadCloser) Read(p []byte) (int, error) {
switch b.mode {
case modeSourceReading:
n, err := b.inner.Read(p)
if n > 0 {
b.buffer = append(b.buffer, p[0:n]...)
}
if err == io.EOF {
b.mode = modeSourceFinished
}
return n, err
case modeSourceFinished:
return 0, io.EOF
case modeBufferReading:
if len(b.buffer) <= b.off {
b.mode = modeBufferFinished
if len(p) == 0 {
return 0, nil
}
return 0, io.EOF
}
n := copy(p, b.buffer[b.off:])
b.off += n
return n, nil
case modeBufferFinished:
return 0, io.EOF
default:
return 0, errors.New("object in undefined status")
}
}
func (b *bufferedReadCloser) Close() error {
switch b.mode {
case modeSourceReading:
_, err := b.BufferedAll()
if err != nil {
return err
}
err = b.inner.Close()
if err != nil {
return err
}
b.mode = modeSourceFinished
return nil
case modeSourceFinished:
return nil
case modeBufferReading:
b.mode = modeBufferFinished
return nil
case modeBufferFinished:
return nil
default:
return errors.New("object in undefined status")
}
}
func (b *bufferedReadCloser) BufferedAll() ([]byte, error) {
switch b.mode {
case modeSourceReading:
arr := make([]byte, 1024)
for b.mode == modeSourceReading {
_, err := b.Read(arr)
if err != nil && err != io.EOF {
return nil, err
}
}
return b.buffer, nil
case modeSourceFinished:
return b.buffer, nil
case modeBufferReading:
return b.buffer, nil
case modeBufferFinished:
return b.buffer, nil
default:
return nil, errors.New("object in undefined status")
}
}
func (b *bufferedReadCloser) Reset() error {
switch b.mode {
case modeSourceReading:
fallthrough
case modeSourceFinished:
err := b.Close()
if err != nil {
return err
}
b.mode = modeBufferReading
b.off = 0
return nil
case modeBufferReading:
fallthrough
case modeBufferFinished:
b.mode = modeBufferReading
b.off = 0
return nil
default:
return errors.New("object in undefined status")
}
}