diff options
Diffstat (limited to 'vendor/github.com/djherbis/buffer/multi.go')
-rw-r--r-- | vendor/github.com/djherbis/buffer/multi.go | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/vendor/github.com/djherbis/buffer/multi.go b/vendor/github.com/djherbis/buffer/multi.go new file mode 100644 index 0000000000..a752483ee4 --- /dev/null +++ b/vendor/github.com/djherbis/buffer/multi.go @@ -0,0 +1,185 @@ +package buffer + +import ( + "bytes" + "encoding/gob" + "io" + "math" +) + +type chain struct { + Buf BufferAt + Next BufferAt +} + +type nopBufferAt struct { + Buffer +} + +func (buf *nopBufferAt) ReadAt(p []byte, off int64) (int, error) { + panic("ReadAt not implemented") +} + +func (buf *nopBufferAt) WriteAt(p []byte, off int64) (int, error) { + panic("WriteAt not implemented") +} + +// toBufferAt converts a Buffer to a BufferAt with nop ReadAt and WriteAt funcs +func toBufferAt(buf Buffer) BufferAt { + return &nopBufferAt{Buffer: buf} +} + +// NewMultiAt returns a BufferAt which is the logical concatenation of the passed BufferAts. +// The data in the buffers is shifted such that there is no non-empty buffer following +// a non-full buffer, this process is also run after every Read. +// If no buffers are passed, the returned Buffer is nil. +func NewMultiAt(buffers ...BufferAt) BufferAt { + if len(buffers) == 0 { + return nil + } else if len(buffers) == 1 { + return buffers[0] + } + + buf := &chain{ + Buf: buffers[0], + Next: NewMultiAt(buffers[1:]...), + } + + buf.Defrag() + + return buf +} + +// NewMulti returns a Buffer which is the logical concatenation of the passed buffers. +// The data in the buffers is shifted such that there is no non-empty buffer following +// a non-full buffer, this process is also run after every Read. +// If no buffers are passed, the returned Buffer is nil. +func NewMulti(buffers ...Buffer) Buffer { + bufAt := make([]BufferAt, len(buffers)) + for i, buf := range buffers { + bufAt[i] = toBufferAt(buf) + } + return NewMultiAt(bufAt...) +} + +func (buf *chain) Reset() { + buf.Next.Reset() + buf.Buf.Reset() +} + +func (buf *chain) Cap() (n int64) { + Next := buf.Next.Cap() + if buf.Buf.Cap() > math.MaxInt64-Next { + return math.MaxInt64 + } + return buf.Buf.Cap() + Next +} + +func (buf *chain) Len() (n int64) { + Next := buf.Next.Len() + if buf.Buf.Len() > math.MaxInt64-Next { + return math.MaxInt64 + } + return buf.Buf.Len() + Next +} + +func (buf *chain) Defrag() { + for !Full(buf.Buf) && !Empty(buf.Next) { + r := io.LimitReader(buf.Next, Gap(buf.Buf)) + if _, err := io.Copy(buf.Buf, r); err != nil && err != io.EOF { + return + } + } +} + +func (buf *chain) Read(p []byte) (n int, err error) { + n, err = buf.Buf.Read(p) + if len(p[n:]) > 0 && (err == nil || err == io.EOF) { + m, err := buf.Next.Read(p[n:]) + n += m + if err != nil { + return n, err + } + } + + buf.Defrag() + + return n, err +} + +func (buf *chain) ReadAt(p []byte, off int64) (n int, err error) { + if buf.Buf.Len() < off { + return buf.Next.ReadAt(p, off-buf.Buf.Len()) + } + + n, err = buf.Buf.ReadAt(p, off) + if len(p[n:]) > 0 && (err == nil || err == io.EOF) { + var m int + m, err = buf.Next.ReadAt(p[n:], 0) + n += m + } + return n, err +} + +func (buf *chain) Write(p []byte) (n int, err error) { + if n, err = buf.Buf.Write(p); err == io.ErrShortWrite { + err = nil + } + p = p[n:] + if len(p) > 0 && err == nil { + m, err := buf.Next.Write(p) + n += m + if err != nil { + return n, err + } + } + return n, err +} + +func (buf *chain) WriteAt(p []byte, off int64) (n int, err error) { + switch { + case buf.Buf.Cap() <= off: // past the end + return buf.Next.WriteAt(p, off-buf.Buf.Cap()) + + case buf.Buf.Cap() >= off+int64(len(p)): // fits in + return buf.Buf.WriteAt(p, off) + + default: // partial fit + n, err = buf.Buf.WriteAt(p, off) + if len(p[n:]) > 0 && (err == nil || err == io.ErrShortWrite) { + var m int + m, err = buf.Next.WriteAt(p[n:], 0) + n += m + } + return n, err + } +} + +func init() { + gob.Register(&chain{}) + gob.Register(&nopBufferAt{}) +} + +func (buf *chain) MarshalBinary() ([]byte, error) { + b := bytes.NewBuffer(nil) + enc := gob.NewEncoder(b) + if err := enc.Encode(&buf.Buf); err != nil { + return nil, err + } + if err := enc.Encode(&buf.Next); err != nil { + return nil, err + } + return b.Bytes(), nil +} + +func (buf *chain) UnmarshalBinary(data []byte) error { + b := bytes.NewBuffer(data) + dec := gob.NewDecoder(b) + if err := dec.Decode(&buf.Buf); err != nil { + return err + } + if err := dec.Decode(&buf.Next); err != nil { + return err + } + return nil +} |