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
}