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