227 lines
5.8 KiB
Go
227 lines
5.8 KiB
Go
// Copyright 2026 Dolthub, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package utils
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"math"
|
|
)
|
|
|
|
// WireRW handles all read and write operations for the Postgres binary wire format. This should only be used for
|
|
// interacting with the wire format, and not for internal use (utils.Reader and utils.Writer exist for internal use).
|
|
type WireRW struct {
|
|
buf *bytes.Buffer
|
|
readIdx uint32
|
|
numSlice []byte
|
|
}
|
|
|
|
// NewWireWriter creates a new WireRW for writing to. This is intended strictly for writing to the binary wire format
|
|
// that Postgres expects, and should not be used for internal write operations (utils.Writer is for internal use).
|
|
func NewWireWriter() *WireRW {
|
|
return &WireRW{
|
|
buf: bytes.NewBuffer(make([]byte, 0, 8)),
|
|
readIdx: 0,
|
|
numSlice: make([]byte, 8), // 8 bytes will cover all floats and integers
|
|
}
|
|
}
|
|
|
|
// NewWireReader creates a new WireRW for reading from data previously written by either a valid Postgres server or
|
|
// a WireRW instance. This is intended strictly for reading the binary wire format that Postgres expects, and should not
|
|
// be used for internal read operations (utils.Reader is for internal use).
|
|
func NewWireReader(data []byte) *WireRW {
|
|
return &WireRW{
|
|
buf: bytes.NewBuffer(data),
|
|
readIdx: 0,
|
|
numSlice: make([]byte, 8), // 8 bytes will cover all floats and integers
|
|
}
|
|
}
|
|
|
|
// ReadBool reads a 1-byte bool.
|
|
func (rw *WireRW) ReadBool() bool {
|
|
return rw.ReadInt8() != 0
|
|
}
|
|
|
|
// ReadInt8 reads an int8.
|
|
func (rw *WireRW) ReadInt8() int8 {
|
|
return int8(rw.ReadUint8())
|
|
}
|
|
|
|
// ReadInt16 reads an int16.
|
|
func (rw *WireRW) ReadInt16() int16 {
|
|
return int16(rw.ReadUint16())
|
|
}
|
|
|
|
// ReadInt32 reads an int32.
|
|
func (rw *WireRW) ReadInt32() int32 {
|
|
return int32(rw.ReadUint32())
|
|
}
|
|
|
|
// ReadInt64 reads an int64.
|
|
func (rw *WireRW) ReadInt64() int64 {
|
|
return int64(rw.ReadUint64())
|
|
}
|
|
|
|
// ReadUint8 reads a uint8.
|
|
func (rw *WireRW) ReadUint8() uint8 {
|
|
data := rw.buf.Bytes()[rw.readIdx]
|
|
rw.readIdx += 1
|
|
return data
|
|
}
|
|
|
|
// ReadUint16 reads a uint16.
|
|
func (rw *WireRW) ReadUint16() uint16 {
|
|
data := rw.buf.Bytes()[rw.readIdx : rw.readIdx+2]
|
|
rw.readIdx += 2
|
|
return binary.BigEndian.Uint16(data)
|
|
}
|
|
|
|
// ReadUint32 reads a uint32.
|
|
func (rw *WireRW) ReadUint32() uint32 {
|
|
data := rw.buf.Bytes()[rw.readIdx : rw.readIdx+4]
|
|
rw.readIdx += 4
|
|
return binary.BigEndian.Uint32(data)
|
|
}
|
|
|
|
// ReadUint64 reads a uint64.
|
|
func (rw *WireRW) ReadUint64() uint64 {
|
|
data := rw.buf.Bytes()[rw.readIdx : rw.readIdx+8]
|
|
rw.readIdx += 8
|
|
return binary.BigEndian.Uint64(data)
|
|
}
|
|
|
|
// ReadFloat32 reads a float32.
|
|
func (rw *WireRW) ReadFloat32() float32 {
|
|
return math.Float32frombits(rw.ReadUint32())
|
|
}
|
|
|
|
// ReadFloat64 reads a float64.
|
|
func (rw *WireRW) ReadFloat64() float64 {
|
|
return math.Float64frombits(rw.ReadUint64())
|
|
}
|
|
|
|
// ReadString reads the next N bytes as a string.
|
|
func (rw *WireRW) ReadString(n uint32) string {
|
|
return string(rw.ReadBytes(n))
|
|
}
|
|
|
|
// ReadBytes reads the next N bytes.
|
|
func (rw *WireRW) ReadBytes(n uint32) []byte {
|
|
data := rw.buf.Bytes()[rw.readIdx : rw.readIdx+n]
|
|
rw.readIdx += n
|
|
return data
|
|
}
|
|
|
|
// WriteBool writes a 1-byte bool.
|
|
func (rw *WireRW) WriteBool(val bool) *WireRW {
|
|
if val {
|
|
rw.buf.WriteByte(1)
|
|
} else {
|
|
rw.buf.WriteByte(0)
|
|
}
|
|
return rw
|
|
}
|
|
|
|
// WriteInt8 writes an int8.
|
|
func (rw *WireRW) WriteInt8(val int8) *WireRW {
|
|
rw.WriteUint8(byte(val))
|
|
return rw
|
|
}
|
|
|
|
// WriteInt16 writes an int16.
|
|
func (rw *WireRW) WriteInt16(val int16) *WireRW {
|
|
rw.WriteUint16(uint16(val))
|
|
return rw
|
|
}
|
|
|
|
// WriteInt32 writes an int32.
|
|
func (rw *WireRW) WriteInt32(val int32) *WireRW {
|
|
rw.WriteUint32(uint32(val))
|
|
return rw
|
|
}
|
|
|
|
// WriteInt64 writes an int64.
|
|
func (rw *WireRW) WriteInt64(val int64) *WireRW {
|
|
rw.WriteUint64(uint64(val))
|
|
return rw
|
|
}
|
|
|
|
// WriteUint8 writes a uint8.
|
|
func (rw *WireRW) WriteUint8(val uint8) *WireRW {
|
|
rw.buf.WriteByte(val)
|
|
return rw
|
|
}
|
|
|
|
// WriteUint16 writes a uint16.
|
|
func (rw *WireRW) WriteUint16(val uint16) *WireRW {
|
|
binary.BigEndian.PutUint16(rw.numSlice, val)
|
|
rw.buf.Write(rw.numSlice[:2])
|
|
return rw
|
|
}
|
|
|
|
// WriteUint32 writes a uint32.
|
|
func (rw *WireRW) WriteUint32(val uint32) *WireRW {
|
|
binary.BigEndian.PutUint32(rw.numSlice, val)
|
|
rw.buf.Write(rw.numSlice[:4])
|
|
return rw
|
|
}
|
|
|
|
// WriteUint64 writes a uint64.
|
|
func (rw *WireRW) WriteUint64(val uint64) *WireRW {
|
|
binary.BigEndian.PutUint64(rw.numSlice, val)
|
|
rw.buf.Write(rw.numSlice[:8])
|
|
return rw
|
|
}
|
|
|
|
// WriteFloat32 writes a float32.
|
|
func (rw *WireRW) WriteFloat32(val float32) *WireRW {
|
|
rw.WriteUint32(math.Float32bits(val))
|
|
return rw
|
|
}
|
|
|
|
// WriteFloat64 writes a float64.
|
|
func (rw *WireRW) WriteFloat64(val float64) *WireRW {
|
|
rw.WriteUint64(math.Float64bits(val))
|
|
return rw
|
|
}
|
|
|
|
// WriteString writes the raw string bytes.
|
|
func (rw *WireRW) WriteString(val string) *WireRW {
|
|
_, _ = rw.buf.WriteString(val)
|
|
return rw
|
|
}
|
|
|
|
// WriteBytes writes the bytes given.
|
|
func (rw *WireRW) WriteBytes(val []byte) *WireRW {
|
|
_, _ = rw.buf.Write(val)
|
|
return rw
|
|
}
|
|
|
|
// Reserve ensures that there are at least N bytes in the buffer, which can prevent reallocations if the space needed is
|
|
// known upfront.
|
|
func (rw *WireRW) Reserve(n uint64) {
|
|
rw.buf.Grow(int(n))
|
|
}
|
|
|
|
// BufferSize returns the number of bytes in the buffer.
|
|
func (rw *WireRW) BufferSize() uint64 {
|
|
return uint64(rw.buf.Len())
|
|
}
|
|
|
|
// BufferData returns the data in the buffer. This does not make a copy.
|
|
func (rw *WireRW) BufferData() []byte {
|
|
return rw.buf.Bytes()
|
|
}
|