119 lines
3.7 KiB
Go
119 lines
3.7 KiB
Go
|
package codec
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io"
|
||
|
)
|
||
|
|
||
|
// Buffer is a reader and a writer that wraps a slice of bytes and also
|
||
|
// provides API for decoding and encoding the protobuf binary format.
|
||
|
//
|
||
|
// Its operation is similar to that of a bytes.Buffer: writing pushes
|
||
|
// data to the end of the buffer while reading pops data from the head
|
||
|
// of the buffer. So the same buffer can be used to both read and write.
|
||
|
type Buffer struct {
|
||
|
buf []byte
|
||
|
index int
|
||
|
|
||
|
// tmp is used when another byte slice is needed, such as when
|
||
|
// serializing messages, since we need to know the length before
|
||
|
// we can write the length prefix; by caching this, including
|
||
|
// after it is grown by serialization operations, we reduce the
|
||
|
// number of allocations needed
|
||
|
tmp []byte
|
||
|
|
||
|
deterministic bool
|
||
|
}
|
||
|
|
||
|
// NewBuffer creates a new buffer with the given slice of bytes as the
|
||
|
// buffer's initial contents.
|
||
|
func NewBuffer(buf []byte) *Buffer {
|
||
|
return &Buffer{buf: buf}
|
||
|
}
|
||
|
|
||
|
// SetDeterministic sets this buffer to encode messages deterministically. This
|
||
|
// is useful for tests. But the overhead is non-zero, so it should not likely be
|
||
|
// used outside of tests. When true, map fields in a message must have their
|
||
|
// keys sorted before serialization to ensure deterministic output. Otherwise,
|
||
|
// values in a map field will be serialized in map iteration order.
|
||
|
func (cb *Buffer) SetDeterministic(deterministic bool) {
|
||
|
cb.deterministic = deterministic
|
||
|
}
|
||
|
|
||
|
// IsDeterministic returns whether or not this buffer is configured to encode
|
||
|
// messages deterministically.
|
||
|
func (cb *Buffer) IsDeterministic() bool {
|
||
|
return cb.deterministic
|
||
|
}
|
||
|
|
||
|
// Reset resets this buffer back to empty. Any subsequent writes/encodes
|
||
|
// to the buffer will allocate a new backing slice of bytes.
|
||
|
func (cb *Buffer) Reset() {
|
||
|
cb.buf = []byte(nil)
|
||
|
cb.index = 0
|
||
|
}
|
||
|
|
||
|
// Bytes returns the slice of bytes remaining in the buffer. Note that
|
||
|
// this does not perform a copy: if the contents of the returned slice
|
||
|
// are modified, the modifications will be visible to subsequent reads
|
||
|
// via the buffer.
|
||
|
func (cb *Buffer) Bytes() []byte {
|
||
|
return cb.buf[cb.index:]
|
||
|
}
|
||
|
|
||
|
// String returns the remaining bytes in the buffer as a string.
|
||
|
func (cb *Buffer) String() string {
|
||
|
return string(cb.Bytes())
|
||
|
}
|
||
|
|
||
|
// EOF returns true if there are no more bytes remaining to read.
|
||
|
func (cb *Buffer) EOF() bool {
|
||
|
return cb.index >= len(cb.buf)
|
||
|
}
|
||
|
|
||
|
// Skip attempts to skip the given number of bytes in the input. If
|
||
|
// the input has fewer bytes than the given count, io.ErrUnexpectedEOF
|
||
|
// is returned and the buffer is unchanged. Otherwise, the given number
|
||
|
// of bytes are skipped and nil is returned.
|
||
|
func (cb *Buffer) Skip(count int) error {
|
||
|
if count < 0 {
|
||
|
return fmt.Errorf("proto: bad byte length %d", count)
|
||
|
}
|
||
|
newIndex := cb.index + count
|
||
|
if newIndex < cb.index || newIndex > len(cb.buf) {
|
||
|
return io.ErrUnexpectedEOF
|
||
|
}
|
||
|
cb.index = newIndex
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Len returns the remaining number of bytes in the buffer.
|
||
|
func (cb *Buffer) Len() int {
|
||
|
return len(cb.buf) - cb.index
|
||
|
}
|
||
|
|
||
|
// Read implements the io.Reader interface. If there are no bytes
|
||
|
// remaining in the buffer, it will return 0, io.EOF. Otherwise,
|
||
|
// it reads max(len(dest), cb.Len()) bytes from input and copies
|
||
|
// them into dest. It returns the number of bytes copied and a nil
|
||
|
// error in this case.
|
||
|
func (cb *Buffer) Read(dest []byte) (int, error) {
|
||
|
if cb.index == len(cb.buf) {
|
||
|
return 0, io.EOF
|
||
|
}
|
||
|
copied := copy(dest, cb.buf[cb.index:])
|
||
|
cb.index += copied
|
||
|
return copied, nil
|
||
|
}
|
||
|
|
||
|
var _ io.Reader = (*Buffer)(nil)
|
||
|
|
||
|
// Write implements the io.Writer interface. It always returns
|
||
|
// len(data), nil.
|
||
|
func (cb *Buffer) Write(data []byte) (int, error) {
|
||
|
cb.buf = append(cb.buf, data...)
|
||
|
return len(data), nil
|
||
|
}
|
||
|
|
||
|
var _ io.Writer = (*Buffer)(nil)
|