Source File
tail.go
Belonging Package
go.pact.im/x/extraio
package extraio
import (
)
// TailReader buffers the last n bytes read from the underlying io.Reader. That
// is, its Read method does not return last n bytes read. Use Tail method to get
// the underlying buffer on EOF.
//
// Note that its Read method may return zero byte count with a nil error if read
// bytes fit into the underlying buffer. Some io.Reader client implementations
// return io.ErrNoProgress error when many calls to Read have failed to return
// any data or error.
type TailReader struct {
reader io.Reader
cur int // mutable
buf []byte // mutable
}
// NewTailReader returns a new reader that buffers last n bytes read from r.
func ( io.Reader, uint) *TailReader {
var []byte
if != 0 {
= make([]byte, 0, )
}
return &TailReader{
reader: ,
buf: ,
}
}
// Reset resets the reader’s state.
func ( *TailReader) () {
.buf = .buf[:0]
}
// Length returns the length of the underlying buffer. It is faster than calling
// len(r.Tail()) since the underlying buffer may not be contiguous and Tail has
// to linearize it to return a contiguous slice of bytes.
func ( *TailReader) () int {
return len(.buf)
}
// Tail returns the buffer with last read bytes. The underlying buffer may not
// be contiguous and Tail linearizes the contents before returning a contiguous
// byte slice.
func ( *TailReader) () []byte {
.linearize()
return .buf
}
// Read implements the io.Reader interface. It reads from the underlying
// io.Reader but keeps the last n read bytes in the internal ring buffer.
func ( *TailReader) ( []byte) (int, error) {
, := .reader.Read()
if <= 0 || .buf == nil {
return ,
}
:= cap(.buf)
:= len(.buf)
:= -
switch {
// Case 1. Data fits into the free buffer space.
case <= :
.buf = append(.buf, [:]...)
= 0
// Case 2. Data exceeds buffer capacity.
case >= :
if == 0 {
rotate([:], -)
:= .buf[.cur:]
:= .buf[:.cur]
swap([len():], )
swap(, )
} else {
swap(.buf, [-:])
.buf = .buf[:]
copy(.buf[:], [-:])
-=
rotate([:], -)
}
// Case 3. Not enough free space but data fits into the buffer.
default:
if == 0 {
:= swap(.buf[.cur:], [:])
.cur = (.cur + ) %
= swap(.buf[.cur:], [:])
.cur = (.cur + ) %
} else {
.buf = append(.buf, [:]...)
copy(, [:])
-=
swap(.buf, [:])
.cur =
}
}
return ,
}
// linearize makes the underlying ring buffer contiguous.
func ( *TailReader) () {
rotate(.buf, .cur)
.cur = 0
}
// swap swaps elements between dst and src slices. It returns the number of
// swapped elements, that is, min(len(dst), len(src)).
func (, []byte) int {
:= min(len(), len())
for := 0; < ; ++ {
[], [] = [], []
}
return
}
// rotate performs in-place left rotation of the slice by n positions from the
// start for positive n. If n is negative, the slice is right rotated by the
// absolute value of n.
//
// Examples:
//
// rotate([]byte("lohel"), 2)
// => []byte("hello")
//
// rotate([]byte("45123"), 2)
// => []byte("12345")
//
// rotate([]byte("34512"), -2)
// => []byte("12345")
//
func ( []byte, int) {
:= len()
if == 0 || == 1 {
return
}
%=
if == 0 || == {
return
}
if < 0 {
+=
}
reverse([:])
reverse([:])
reverse()
}
// reverse performs in-place reversal of the slice.
func ( []byte) {
for , := 0, len()-1; < ; , = +1, -1 {
[], [] = [], []
}
}
// min returns the minimum of the two integers.
func (, int) int {
if < {
return
}
return
}
The pages are generated with Golds v0.4.9. (GOOS=linux GOARCH=amd64)