mirror of
https://github.com/rclone/rclone
synced 2024-11-24 01:26:25 +01:00
b285efb476
Fixes #6547
231 lines
5.0 KiB
Go
231 lines
5.0 KiB
Go
package api
|
|
|
|
// BIN protocol helpers
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/rclone/rclone/lib/readers"
|
|
)
|
|
|
|
// protocol errors
|
|
var (
|
|
ErrorPrematureEOF = errors.New("premature EOF")
|
|
ErrorInvalidLength = errors.New("invalid length")
|
|
ErrorZeroTerminate = errors.New("string must end with zero")
|
|
)
|
|
|
|
// BinWriter is a binary protocol writer
|
|
type BinWriter struct {
|
|
b *bytes.Buffer // growing byte buffer
|
|
a []byte // temporary buffer for next varint
|
|
}
|
|
|
|
// NewBinWriter creates a binary protocol helper
|
|
func NewBinWriter() *BinWriter {
|
|
return &BinWriter{
|
|
b: new(bytes.Buffer),
|
|
a: make([]byte, binary.MaxVarintLen64),
|
|
}
|
|
}
|
|
|
|
// Bytes returns binary data
|
|
func (w *BinWriter) Bytes() []byte {
|
|
return w.b.Bytes()
|
|
}
|
|
|
|
// Reader returns io.Reader with binary data
|
|
func (w *BinWriter) Reader() io.Reader {
|
|
return bytes.NewReader(w.b.Bytes())
|
|
}
|
|
|
|
// WritePu16 writes a short as unsigned varint
|
|
func (w *BinWriter) WritePu16(val int) {
|
|
if val < 0 || val > 65535 {
|
|
panic(fmt.Sprintf("Invalid UInt16 %v", val))
|
|
}
|
|
w.WritePu64(int64(val))
|
|
}
|
|
|
|
// WritePu32 writes a signed long as unsigned varint
|
|
func (w *BinWriter) WritePu32(val int64) {
|
|
if val < 0 || val > 4294967295 {
|
|
panic(fmt.Sprintf("Invalid UInt32 %v", val))
|
|
}
|
|
w.WritePu64(val)
|
|
}
|
|
|
|
// WritePu64 writes an unsigned (actually, signed) long as unsigned varint
|
|
func (w *BinWriter) WritePu64(val int64) {
|
|
if val < 0 {
|
|
panic(fmt.Sprintf("Invalid UInt64 %v", val))
|
|
}
|
|
w.b.Write(w.a[:binary.PutUvarint(w.a, uint64(val))])
|
|
}
|
|
|
|
// WriteP64 writes an signed long as unsigned varint
|
|
func (w *BinWriter) WriteP64(val int64) {
|
|
w.b.Write(w.a[:binary.PutUvarint(w.a, uint64(val))])
|
|
}
|
|
|
|
// WriteString writes a zero-terminated string
|
|
func (w *BinWriter) WriteString(str string) {
|
|
buf := []byte(str)
|
|
w.WritePu64(int64(len(buf) + 1))
|
|
w.b.Write(buf)
|
|
w.b.WriteByte(0)
|
|
}
|
|
|
|
// Write writes a byte buffer
|
|
func (w *BinWriter) Write(buf []byte) {
|
|
w.b.Write(buf)
|
|
}
|
|
|
|
// WriteWithLength writes a byte buffer prepended with its length as varint
|
|
func (w *BinWriter) WriteWithLength(buf []byte) {
|
|
w.WritePu64(int64(len(buf)))
|
|
w.b.Write(buf)
|
|
}
|
|
|
|
// BinReader is a binary protocol reader helper
|
|
type BinReader struct {
|
|
b *bufio.Reader
|
|
count *readers.CountingReader
|
|
err error // keeps the first error encountered
|
|
}
|
|
|
|
// NewBinReader creates a binary protocol reader helper
|
|
func NewBinReader(reader io.Reader) *BinReader {
|
|
r := &BinReader{}
|
|
r.count = readers.NewCountingReader(reader)
|
|
r.b = bufio.NewReader(r.count)
|
|
return r
|
|
}
|
|
|
|
// Count returns number of bytes read
|
|
func (r *BinReader) Count() uint64 {
|
|
return r.count.BytesRead()
|
|
}
|
|
|
|
// Error returns first encountered error or nil
|
|
func (r *BinReader) Error() error {
|
|
return r.err
|
|
}
|
|
|
|
// check() keeps the first error encountered in a stream
|
|
func (r *BinReader) check(err error) bool {
|
|
if err == nil {
|
|
return true
|
|
}
|
|
if r.err == nil {
|
|
// keep the first error
|
|
r.err = err
|
|
}
|
|
if err != io.EOF {
|
|
panic(fmt.Sprintf("Error parsing response: %v", err))
|
|
}
|
|
return false
|
|
}
|
|
|
|
// ReadByteAsInt reads a single byte as uint32, returns -1 for EOF or errors
|
|
func (r *BinReader) ReadByteAsInt() int {
|
|
if octet, err := r.b.ReadByte(); r.check(err) {
|
|
return int(octet)
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// ReadByteAsShort reads a single byte as uint16, returns -1 for EOF or errors
|
|
func (r *BinReader) ReadByteAsShort() int16 {
|
|
if octet, err := r.b.ReadByte(); r.check(err) {
|
|
return int16(octet)
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// ReadIntSpl reads two bytes as little-endian uint16, returns -1 for EOF or errors
|
|
func (r *BinReader) ReadIntSpl() int {
|
|
var val uint16
|
|
if r.check(binary.Read(r.b, binary.LittleEndian, &val)) {
|
|
return int(val)
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// ReadULong returns uint64 equivalent of -1 for EOF or errors
|
|
func (r *BinReader) ReadULong() uint64 {
|
|
if val, err := binary.ReadUvarint(r.b); r.check(err) {
|
|
return val
|
|
}
|
|
return 0xffffffffffffffff
|
|
}
|
|
|
|
// ReadPu32 returns -1 for EOF or errors
|
|
func (r *BinReader) ReadPu32() int64 {
|
|
if val, err := binary.ReadUvarint(r.b); r.check(err) {
|
|
return int64(val)
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// ReadNBytes reads given number of bytes, returns invalid data for EOF or errors
|
|
func (r *BinReader) ReadNBytes(len int) []byte {
|
|
buf := make([]byte, len)
|
|
n, err := r.b.Read(buf)
|
|
if r.check(err) {
|
|
return buf
|
|
}
|
|
if n != len {
|
|
r.check(ErrorPrematureEOF)
|
|
}
|
|
return buf
|
|
}
|
|
|
|
// ReadBytesByLength reads buffer length and its bytes
|
|
func (r *BinReader) ReadBytesByLength() []byte {
|
|
len := r.ReadPu32()
|
|
if len < 0 {
|
|
r.check(ErrorInvalidLength)
|
|
return []byte{}
|
|
}
|
|
return r.ReadNBytes(int(len))
|
|
}
|
|
|
|
// ReadString reads a zero-terminated string with length
|
|
func (r *BinReader) ReadString() string {
|
|
len := int(r.ReadPu32())
|
|
if len < 1 {
|
|
r.check(ErrorInvalidLength)
|
|
return ""
|
|
}
|
|
buf := make([]byte, len-1)
|
|
n, err := r.b.Read(buf)
|
|
if !r.check(err) {
|
|
return ""
|
|
}
|
|
if n != len-1 {
|
|
r.check(ErrorPrematureEOF)
|
|
return ""
|
|
}
|
|
zeroByte, err := r.b.ReadByte()
|
|
if !r.check(err) {
|
|
return ""
|
|
}
|
|
if zeroByte != 0 {
|
|
r.check(ErrorZeroTerminate)
|
|
return ""
|
|
}
|
|
return string(buf)
|
|
}
|
|
|
|
// ReadDate reads a Unix encoded time
|
|
func (r *BinReader) ReadDate() time.Time {
|
|
return time.Unix(r.ReadPu32(), 0)
|
|
}
|