// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package circular import ( "io" "sync/atomic" ) // Reader implements seekable reader with local position in the Buffer which // reads from the fixed part of the buffer. // // Reader is not safe to be used with concurrent Read/Seek operations. type Reader struct { buf *Buffer startOff, endOff int64 off int64 closed uint32 } // Read implements io.Reader. func (r *Reader) Read(p []byte) (n int, err error) { if atomic.LoadUint32(&r.closed) > 0 { err = ErrClosed return } r.buf.mu.Lock() defer r.buf.mu.Unlock() if r.off < r.buf.off-int64(r.buf.opt.MaxCapacity) { // reader is falling too much behind err = ErrOutOfSync return } if r.off == r.endOff { err = io.EOF return } if len(p) == 0 { return } n = int(r.endOff - r.off) if n > len(p) { n = len(p) } i := int(r.off % int64(r.buf.opt.MaxCapacity)) l := r.buf.opt.MaxCapacity - i if l < n { copy(p, r.buf.data[i:]) copy(p[l:], r.buf.data[:n-l]) } else { copy(p, r.buf.data[i:i+n]) } r.off += int64(n) return n, err } // Close implements io.Closer. func (r *Reader) Close() error { atomic.StoreUint32(&r.closed, 1) return nil } // Seek implements io.Seeker. func (r *Reader) Seek(offset int64, whence int) (int64, error) { newOff := r.off switch whence { case io.SeekCurrent: newOff += offset case io.SeekEnd: newOff = r.endOff + offset case io.SeekStart: newOff = r.startOff + offset } if newOff < r.startOff { return r.off - r.startOff, ErrSeekBeforeStart } if newOff > r.endOff { newOff = r.endOff } r.off = newOff return r.off - r.startOff, nil }