Source File
buffer.go
Belonging Package
github.com/pkg/sftp/internal/encoding/ssh/filexfer
package sshfx
import (
)
// Various encoding errors.
var (
ErrShortPacket = errors.New("packet too short")
ErrLongPacket = errors.New("packet too long")
)
// Buffer wraps up the various encoding details of the SSH format.
//
// Data types are encoded as per section 4 from https://tools.ietf.org/html/draft-ietf-secsh-architecture-09#page-8
type Buffer struct {
b []byte
off int
Err error
}
// NewBuffer creates and initializes a new buffer using buf as its initial contents.
// The new buffer takes ownership of buf, and the caller should not use buf after this call.
//
// In most cases, new(Buffer) (or just declaring a Buffer variable) is sufficient to initialize a Buffer.
func ( []byte) *Buffer {
return &Buffer{
b: ,
}
}
// NewMarshalBuffer creates a new Buffer ready to start marshaling a Packet into.
// It preallocates enough space for uint32(length), uint8(type), uint32(request-id) and size more bytes.
func ( int) *Buffer {
return NewBuffer(make([]byte, 4+1+4+))
}
// Bytes returns a slice of length b.Len() holding the unconsumed bytes in the Buffer.
// The slice is valid for use only until the next buffer modification
// (that is, only until the next call to an Append or Consume method).
func ( *Buffer) () []byte {
return .b[.off:]
}
// Len returns the number of unconsumed bytes in the buffer.
func ( *Buffer) () int { return len(.b) - .off }
// Cap returns the capacity of the buffer’s underlying byte slice,
// that is, the total space allocated for the buffer’s data.
func ( *Buffer) () int { return cap(.b) }
// Reset resets the buffer to be empty, but it retains the underlying storage for use by future Appends.
func ( *Buffer) () {
* = Buffer{
b: .b[:0],
}
}
// StartPacket resets and initializes the buffer to be ready to start marshaling a packet into.
// It truncates the buffer, reserves space for uint32(length), then appends the given packetType and requestID.
func ( *Buffer) ( PacketType, uint32) {
* = Buffer{
b: append(.b[:0], make([]byte, 4)...),
}
.AppendUint8(uint8())
.AppendUint32()
}
// Packet finalizes the packet started from StartPacket.
// It is expected that this will end the ownership of the underlying byte-slice,
// and so the returned byte-slices may be reused the same as any other byte-slice,
// the caller should not use this buffer after this call.
//
// It writes the packet body length into the first four bytes of the buffer in network byte order (big endian).
// The packet body length is the length of this buffer less the 4-byte length itself, plus the length of payload.
//
// It is assumed that no Consume methods have been called on this buffer,
// and so it returns the whole underlying slice.
func ( *Buffer) ( []byte) (, []byte, error) {
.PutLength(len(.b) - 4 + len())
return .b, , nil
}
// ConsumeUint8 consumes a single byte from the buffer.
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
func ( *Buffer) () uint8 {
if .Err != nil {
return 0
}
if .Len() < 1 {
.off = len(.b)
.Err = ErrShortPacket
return 0
}
var uint8
, .off = .b[.off], .off+1
return
}
// AppendUint8 appends a single byte into the buffer.
func ( *Buffer) ( uint8) {
.b = append(.b, )
}
// ConsumeBool consumes a single byte from the buffer, and returns true if that byte is non-zero.
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
func ( *Buffer) () bool {
return .ConsumeUint8() != 0
}
// AppendBool appends a single bool into the buffer.
// It encodes it as a single byte, with false as 0, and true as 1.
func ( *Buffer) ( bool) {
if {
.AppendUint8(1)
} else {
.AppendUint8(0)
}
}
// ConsumeUint16 consumes a single uint16 from the buffer, in network byte order (big-endian).
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
func ( *Buffer) () uint16 {
if .Err != nil {
return 0
}
if .Len() < 2 {
.off = len(.b)
.Err = ErrShortPacket
return 0
}
:= binary.BigEndian.Uint16(.b[.off:])
.off += 2
return
}
// AppendUint16 appends single uint16 into the buffer, in network byte order (big-endian).
func ( *Buffer) ( uint16) {
.b = append(.b,
byte(>>8),
byte(>>0),
)
}
// unmarshalUint32 is used internally to read the packet length.
// It is unsafe, and so not exported.
// Even within this package, its use should be avoided.
func unmarshalUint32( []byte) uint32 {
return binary.BigEndian.Uint32([:4])
}
// ConsumeUint32 consumes a single uint32 from the buffer, in network byte order (big-endian).
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
func ( *Buffer) () uint32 {
if .Err != nil {
return 0
}
if .Len() < 4 {
.off = len(.b)
.Err = ErrShortPacket
return 0
}
:= binary.BigEndian.Uint32(.b[.off:])
.off += 4
return
}
// AppendUint32 appends a single uint32 into the buffer, in network byte order (big-endian).
func ( *Buffer) ( uint32) {
.b = append(.b,
byte(>>24),
byte(>>16),
byte(>>8),
byte(>>0),
)
}
// ConsumeCount consumes a single uint32 count from the buffer, in network byte order (big-endian) as an int.
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
func ( *Buffer) () int {
return int(.ConsumeUint32())
}
// AppendCount appends a single int length as a uint32 into the buffer, in network byte order (big-endian).
func ( *Buffer) ( int) {
.AppendUint32(uint32())
}
// ConsumeUint64 consumes a single uint64 from the buffer, in network byte order (big-endian).
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
func ( *Buffer) () uint64 {
if .Err != nil {
return 0
}
if .Len() < 8 {
.off = len(.b)
.Err = ErrShortPacket
return 0
}
:= binary.BigEndian.Uint64(.b[.off:])
.off += 8
return
}
// AppendUint64 appends a single uint64 into the buffer, in network byte order (big-endian).
func ( *Buffer) ( uint64) {
.b = append(.b,
byte(>>56),
byte(>>48),
byte(>>40),
byte(>>32),
byte(>>24),
byte(>>16),
byte(>>8),
byte(>>0),
)
}
// ConsumeInt64 consumes a single int64 from the buffer, in network byte order (big-endian) with two’s complement.
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
func ( *Buffer) () int64 {
return int64(.ConsumeUint64())
}
// AppendInt64 appends a single int64 into the buffer, in network byte order (big-endian) with two’s complement.
func ( *Buffer) ( int64) {
.AppendUint64(uint64())
}
// ConsumeByteSlice consumes a single string of raw binary data from the buffer.
// A string is a uint32 length, followed by that number of raw bytes.
// If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket.
//
// The returned slice aliases the buffer contents, and is valid only as long as the buffer is not reused
// (that is, only until the next call to Reset, PutLength, StartPacket, or UnmarshalBinary).
//
// In no case will any Consume calls return overlapping slice aliases,
// and Append calls are guaranteed to not disturb this slice alias.
func ( *Buffer) () []byte {
:= int(.ConsumeUint32())
if .Err != nil {
return nil
}
if .Len() < || < 0 {
.off = len(.b)
.Err = ErrShortPacket
return nil
}
:= .b[.off:]
if len() > || cap() > {
= [::]
}
.off += int()
return
}
// ConsumeByteSliceCopy consumes a single string of raw binary data as a copy from the buffer.
// A string is a uint32 length, followed by that number of raw bytes.
// If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket.
//
// The returned slice does not alias any buffer contents,
// and will therefore be valid even if the buffer is later reused.
//
// If hint has sufficient capacity to hold the data, it will be reused and overwritten,
// otherwise a new backing slice will be allocated and returned.
func ( *Buffer) ( []byte) []byte {
:= .ConsumeByteSlice()
if := len() - len(); > 0 {
= append(, make([]byte, )...)
}
:= copy(, )
= [:]
return
}
// AppendByteSlice appends a single string of raw binary data into the buffer.
// A string is a uint32 length, followed by that number of raw bytes.
func ( *Buffer) ( []byte) {
.AppendUint32(uint32(len()))
.b = append(.b, ...)
}
// ConsumeString consumes a single string of binary data from the buffer.
// A string is a uint32 length, followed by that number of raw bytes.
// If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket.
//
// NOTE: Go implicitly assumes that strings contain UTF-8 encoded data.
// All caveats on using arbitrary binary data in Go strings applies.
func ( *Buffer) () string {
return string(.ConsumeByteSlice())
}
// AppendString appends a single string of binary data into the buffer.
// A string is a uint32 length, followed by that number of raw bytes.
func ( *Buffer) ( string) {
.AppendByteSlice([]byte())
}
// PutLength writes the given size into the first four bytes of the buffer in network byte order (big endian).
func ( *Buffer) ( int) {
if len(.b) < 4 {
.b = append(.b, make([]byte, 4-len(.b))...)
}
binary.BigEndian.PutUint32(.b, uint32())
}
// MarshalBinary returns a clone of the full internal buffer.
func ( *Buffer) () ([]byte, error) {
:= make([]byte, len(.b))
:= copy(, .b)
return [:], nil
}
// UnmarshalBinary sets the internal buffer of b to be a clone of data, and zeros the internal offset.
func ( *Buffer) ( []byte) error {
if := len() - len(.b); > 0 {
.b = append(.b, make([]byte, )...)
}
:= copy(.b, )
.b = .b[:]
.off = 0
return nil
}
![]() |
The pages are generated with Golds v0.6.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |