1
0
Fork 0
bocadillo/buffer/buffer.go

185 lines
5.0 KiB
Go

package buffer
import (
"encoding/binary"
"github.com/localhots/bocadillo/mysql"
)
// Buffer is a simple wrapper over a slice of bytes with a cursor. It allows for
// easy command building and results parsing.
type Buffer struct {
data []byte
pos int
}
// New creates a new buffer from a given slice of bytes and sets the cursor to
// the beginning.
func New(data []byte) *Buffer {
return &Buffer{data: data}
}
// NewCommandBuffer pre-allocates a buffer of a given size and reserves 4 bytes
// at the beginning for the driver, these would be used to set command length
// and sequence number.
func NewCommandBuffer(size int) *Buffer {
return &Buffer{data: make([]byte, size+4), pos: 4}
}
// Skip advances the cursor by N bytes.
func (b *Buffer) Skip(n int) {
b.pos += n
}
// Read returns next N bytes and advances the cursor.
func (b *Buffer) Read(n int) []byte {
b.pos += n
return b.data[b.pos-n : b.pos]
}
// Cur returns remaining unread buffer.
func (b *Buffer) Cur() []byte {
return b.data[b.pos:]
}
// More returns true if there's more to read.
func (b *Buffer) More() bool {
return b.pos < len(b.data)-1
}
// Bytes returns entire buffer contents.
func (b *Buffer) Bytes() []byte {
return b.data
}
// ReadUint8 reads a uint8 and advances cursor by 1 byte.
func (b *Buffer) ReadUint8() uint8 {
return mysql.DecodeUint8(b.Read(1))
}
// ReadUint16 reads a uint16 and advances cursor by 2 bytes.
func (b *Buffer) ReadUint16() uint16 {
return mysql.DecodeUint16(b.Read(2))
}
// ReadUint24 reads a 3-byte integer as uint32 and advances cursor by 3 bytes.
func (b *Buffer) ReadUint24() uint32 {
return mysql.DecodeUint24(b.Read(3))
}
// ReadUint32 reads a uint32 and advances cursor by 4 bytes.
func (b *Buffer) ReadUint32() uint32 {
return mysql.DecodeUint32(b.Read(4))
}
// ReadUint48 reads a 6-byte integer as uint64 and advances cursor by 6 bytes.
func (b *Buffer) ReadUint48() uint64 {
return mysql.DecodeUint48(b.Read(6))
}
// ReadUint64 reads a uint64 and advances cursor by 8 bytes.
func (b *Buffer) ReadUint64() uint64 {
return mysql.DecodeUint64(b.Read(8))
}
// ReadUintLenEnc reads a length-encoded integer and advances cursor accordingly.
func (b *Buffer) ReadUintLenEnc() (val uint64, isNull bool, size int) {
val, isNull, size = mysql.DecodeUintLenEnc(b.Cur())
b.Skip(size)
return
}
// ReadVarLen64 reads a number encoded in given size of bytes and advances
// cursor accordingly.
func (b *Buffer) ReadVarLen64(n int) uint64 {
return mysql.DecodeVarLen64(b.Read(n), n)
}
// ReadFloat32 reads a float32 and advances cursor by 4 bytes.
func (b *Buffer) ReadFloat32() float32 {
return mysql.DecodeFloat32(b.Read(4))
}
// ReadFloat64 reads a float64 and advances cursor by 8 bytes.
func (b *Buffer) ReadFloat64() float64 {
return mysql.DecodeFloat64(b.Read(8))
}
// ReadStringNullTerm reads a NULL-terminated string and advances cursor by its
// length plus 1 extra byte.
func (b *Buffer) ReadStringNullTerm() []byte {
str := mysql.DecodeStringNullTerm(b.Cur())
b.Skip(len(str) + 1)
return str
}
// ReadStringVarLen reads a variable-length string and advances cursor by the
// same number of bytes.
func (b *Buffer) ReadStringVarLen(n int) []byte {
return mysql.DecodeStringVarLen(b.Read(n), n)
}
// ReadStringVarEnc reads a variable-length length of the string and the string
// itself, then advances cursor by the same number of bytes.
func (b *Buffer) ReadStringVarEnc(n int) []byte {
length := int(mysql.DecodeVarLen64(b.Read(n), n))
return mysql.DecodeStringVarLen(b.Read(length), length)
}
// ReadStringLenEnc reads a length-encoded string and advances cursor
// accordingly.
func (b *Buffer) ReadStringLenEnc() (str []byte, size int) {
str, size = mysql.DecodeStringLenEnc(b.Cur())
b.Skip(size)
return
}
// ReadStringEOF reads remaining contents of the buffer as a new string.
func (b *Buffer) ReadStringEOF() []byte {
return mysql.DecodeStringEOF(b.Cur())
}
// WriteByte writes given byte to the buffer and advances cursor by 1.
func (b *Buffer) WriteByte(v byte) {
b.data[b.pos] = v
b.pos++
}
// WriteUint16 writes given uint16 value to the buffer and advances cursor by 2.
func (b *Buffer) WriteUint16(v uint16) {
binary.LittleEndian.PutUint16(b.data[b.pos:], v)
b.pos += 2
}
// WriteUint32 writes given uint32 value to the buffer and advances cursor by 4.
func (b *Buffer) WriteUint32(v uint32) {
binary.LittleEndian.PutUint32(b.data[b.pos:], v)
b.pos += 4
}
// WriteStringLenEnc writes a length-encoded string to the buffer and advances
// cursor accordingly.
func (b *Buffer) WriteStringLenEnc(s string) {
b.data[b.pos] = byte(len(s))
b.pos++
b.pos += copy(b.data[b.pos:], s)
}
// WriteStringEOF writes given string to the buffer and advances cursor by its
// length.
func (b *Buffer) WriteStringEOF(s string) {
b.pos += copy(b.data[b.pos:], s)
}
//
// Special types
//
// ReadDecimal decodes a decimal value from the buffer anf then advances cursor
// accordingly.
func (b *Buffer) ReadDecimal(precision, decimals int) mysql.Decimal {
dec, n := mysql.DecodeDecimal(b.Cur(), precision, decimals)
b.Skip(n)
return dec
}