diff --git a/read.go b/read.go index 7c39162c..7ddad508 100644 --- a/read.go +++ b/read.go @@ -2,103 +2,39 @@ package pgio import ( "encoding/binary" - "io" ) -type Uint16Reader interface { - ReadUint16() (n uint16, err error) +func NextByte(buf []byte) ([]byte, byte) { + b := buf[0] + return buf[1:], b } -type Uint32Reader interface { - ReadUint32() (n uint32, err error) +func NextUint16(buf []byte) ([]byte, uint16) { + n := binary.BigEndian.Uint16(buf) + return buf[2:], n } -type Uint64Reader interface { - ReadUint64() (n uint64, err error) +func NextUint32(buf []byte) ([]byte, uint32) { + n := binary.BigEndian.Uint32(buf) + return buf[4:], n } -// ReadByte reads a byte from r. -func ReadByte(r io.Reader) (byte, error) { - if r, ok := r.(io.ByteReader); ok { - return r.ReadByte() - } - - buf := make([]byte, 1) - _, err := r.Read(buf) - return buf[0], err +func NextUint64(buf []byte) ([]byte, uint64) { + n := binary.BigEndian.Uint64(buf) + return buf[8:], n } -// ReadUint16 reads an uint16 from r in PostgreSQL wire format (network byte order). This -// may be more efficient than directly using Read if r provides a ReadUint16 -// method. -func ReadUint16(r io.Reader) (uint16, error) { - if r, ok := r.(Uint16Reader); ok { - return r.ReadUint16() - } - - buf := make([]byte, 2) - _, err := io.ReadFull(r, buf) - if err != nil { - return 0, err - } - - return binary.BigEndian.Uint16(buf), nil +func NextInt16(buf []byte) ([]byte, int16) { + buf, n := NextUint16(buf) + return buf, int16(n) } -// ReadInt16 reads an int16 r in PostgreSQL wire format (network byte order). This -// may be more efficient than directly using Read if r provides a ReadUint16 -// method. -func ReadInt16(r io.Reader) (int16, error) { - n, err := ReadUint16(r) - return int16(n), err +func NextInt32(buf []byte) ([]byte, int32) { + buf, n := NextUint32(buf) + return buf, int32(n) } -// ReadUint32 reads an uint32 r in PostgreSQL wire format (network byte order). This -// may be more efficient than directly using Read if r provides a ReadUint32 -// method. -func ReadUint32(r io.Reader) (uint32, error) { - if r, ok := r.(Uint32Reader); ok { - return r.ReadUint32() - } - - buf := make([]byte, 4) - _, err := io.ReadFull(r, buf) - if err != nil { - return 0, err - } - - return binary.BigEndian.Uint32(buf), nil -} - -// ReadInt32 reads an int32 r in PostgreSQL wire format (network byte order). This -// may be more efficient than directly using Read if r provides a ReadUint32 -// method. -func ReadInt32(r io.Reader) (int32, error) { - n, err := ReadUint32(r) - return int32(n), err -} - -// ReadUint64 reads an uint64 r in PostgreSQL wire format (network byte order). This -// may be more efficient than directly using Read if r provides a ReadUint64 -// method. -func ReadUint64(r io.Reader) (uint64, error) { - if r, ok := r.(Uint64Reader); ok { - return r.ReadUint64() - } - - buf := make([]byte, 8) - _, err := io.ReadFull(r, buf) - if err != nil { - return 0, err - } - - return binary.BigEndian.Uint64(buf), nil -} - -// ReadInt64 reads an int64 r in PostgreSQL wire format (network byte order). This -// may be more efficient than directly using Read if r provides a ReadUint64 -// method. -func ReadInt64(r io.Reader) (int64, error) { - n, err := ReadUint64(r) - return int64(n), err +func NextInt64(buf []byte) ([]byte, int64) { + buf, n := NextUint64(buf) + return buf, int64(n) } diff --git a/read_test.go b/read_test.go new file mode 100644 index 00000000..fbe29ae4 --- /dev/null +++ b/read_test.go @@ -0,0 +1,57 @@ +package pgio + +import ( + "testing" +) + +func TestNextByte(t *testing.T) { + buf := []byte{42, 1} + var b byte + buf, b = NextByte(buf) + if b != 42 { + t.Errorf("NextByte(buf) => %v, want %v", b, 42) + } + buf, b = NextByte(buf) + if b != 1 { + t.Errorf("NextByte(buf) => %v, want %v", b, 1) + } +} + +func TestNextUint16(t *testing.T) { + buf := []byte{0, 42, 0, 1} + var n uint16 + buf, n = NextUint16(buf) + if n != 42 { + t.Errorf("NextUint16(buf) => %v, want %v", n, 42) + } + buf, n = NextUint16(buf) + if n != 1 { + t.Errorf("NextUint16(buf) => %v, want %v", n, 1) + } +} + +func TestNextUint32(t *testing.T) { + buf := []byte{0, 0, 0, 42, 0, 0, 0, 1} + var n uint32 + buf, n = NextUint32(buf) + if n != 42 { + t.Errorf("NextUint32(buf) => %v, want %v", n, 42) + } + buf, n = NextUint32(buf) + if n != 1 { + t.Errorf("NextUint32(buf) => %v, want %v", n, 1) + } +} + +func TestNextUint64(t *testing.T) { + buf := []byte{0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 1} + var n uint64 + buf, n = NextUint64(buf) + if n != 42 { + t.Errorf("NextUint64(buf) => %v, want %v", n, 42) + } + buf, n = NextUint64(buf) + if n != 1 { + t.Errorf("NextUint64(buf) => %v, want %v", n, 1) + } +} diff --git a/write.go b/write.go index 823fbd00..96aedf9d 100644 --- a/write.go +++ b/write.go @@ -1,97 +1,40 @@ package pgio -import ( - "encoding/binary" - "io" -) +import "encoding/binary" -type Uint16Writer interface { - WriteUint16(uint16) (n int, err error) +func AppendUint16(buf []byte, n uint16) []byte { + wp := len(buf) + buf = append(buf, 0, 0) + binary.BigEndian.PutUint16(buf[wp:], n) + return buf } -type Uint32Writer interface { - WriteUint32(uint32) (n int, err error) +func AppendUint32(buf []byte, n uint32) []byte { + wp := len(buf) + buf = append(buf, 0, 0, 0, 0) + binary.BigEndian.PutUint32(buf[wp:], n) + return buf } -type Uint64Writer interface { - WriteUint64(uint64) (n int, err error) +func AppendUint64(buf []byte, n uint64) []byte { + wp := len(buf) + buf = append(buf, 0, 0, 0, 0, 0, 0, 0, 0) + binary.BigEndian.PutUint64(buf[wp:], n) + return buf } -// WriteByte writes b to w. -func WriteByte(w io.Writer, b byte) error { - if w, ok := w.(io.ByteWriter); ok { - return w.WriteByte(b) - } - _, err := w.Write([]byte{b}) - return err +func AppendInt16(buf []byte, n int16) []byte { + return AppendUint16(buf, uint16(n)) } -// WriteUint16 writes n to w in PostgreSQL wire format (network byte order). This -// may be more efficient than directly using Write if w provides a WriteUint16 -// method. -func WriteUint16(w io.Writer, n uint16) (int, error) { - if w, ok := w.(Uint16Writer); ok { - return w.WriteUint16(n) - } - b := make([]byte, 2) - binary.BigEndian.PutUint16(b, n) - return w.Write(b) +func AppendInt32(buf []byte, n int32) []byte { + return AppendUint32(buf, uint32(n)) } -// WriteInt16 writes n to w in PostgreSQL wire format (network byte order). This -// may be more efficient than directly using Write if w provides a WriteUint16 -// method. -func WriteInt16(w io.Writer, n int16) (int, error) { - return WriteUint16(w, uint16(n)) +func AppendInt64(buf []byte, n int64) []byte { + return AppendUint64(buf, uint64(n)) } -// WriteUint32 writes n to w in PostgreSQL wire format (network byte order). This -// may be more efficient than directly using Write if w provides a WriteUint32 -// method. -func WriteUint32(w io.Writer, n uint32) (int, error) { - if w, ok := w.(Uint32Writer); ok { - return w.WriteUint32(n) - } - b := make([]byte, 4) - binary.BigEndian.PutUint32(b, n) - return w.Write(b) -} - -// WriteInt32 writes n to w in PostgreSQL wire format (network byte order). This -// may be more efficient than directly using Write if w provides a WriteUint32 -// method. -func WriteInt32(w io.Writer, n int32) (int, error) { - return WriteUint32(w, uint32(n)) -} - -// WriteUint64 writes n to w in PostgreSQL wire format (network byte order). This -// may be more efficient than directly using Write if w provides a WriteUint64 -// method. -func WriteUint64(w io.Writer, n uint64) (int, error) { - if w, ok := w.(Uint64Writer); ok { - return w.WriteUint64(n) - } - b := make([]byte, 8) - binary.BigEndian.PutUint64(b, n) - return w.Write(b) -} - -// WriteInt64 writes n to w in PostgreSQL wire format (network byte order). This -// may be more efficient than directly using Write if w provides a WriteUint64 -// method. -func WriteInt64(w io.Writer, n int64) (int, error) { - return WriteUint64(w, uint64(n)) -} - -// WriteCString writes s to w followed by a null byte. -func WriteCString(w io.Writer, s string) (int, error) { - n, err := io.WriteString(w, s) - if err != nil { - return n, err - } - err = WriteByte(w, 0) - if err != nil { - return n, err - } - return n + 1, nil +func SetInt32(buf []byte, n int32) { + binary.BigEndian.PutUint32(buf, uint32(n)) } diff --git a/write_test.go b/write_test.go new file mode 100644 index 00000000..bd50e71c --- /dev/null +++ b/write_test.go @@ -0,0 +1,78 @@ +package pgio + +import ( + "reflect" + "testing" +) + +func TestAppendUint16NilBuf(t *testing.T) { + buf := AppendUint16(nil, 1) + if !reflect.DeepEqual(buf, []byte{0, 1}) { + t.Errorf("AppendUint16(nil, 1) => %v, want %v", buf, []byte{0, 1}) + } +} + +func TestAppendUint16EmptyBuf(t *testing.T) { + buf := []byte{} + buf = AppendUint16(buf, 1) + if !reflect.DeepEqual(buf, []byte{0, 1}) { + t.Errorf("AppendUint16(nil, 1) => %v, want %v", buf, []byte{0, 1}) + } +} + +func TestAppendUint16BufWithCapacityDoesNotAllocate(t *testing.T) { + buf := make([]byte, 0, 4) + AppendUint16(buf, 1) + buf = buf[0:2] + if !reflect.DeepEqual(buf, []byte{0, 1}) { + t.Errorf("AppendUint16(nil, 1) => %v, want %v", buf, []byte{0, 1}) + } +} + +func TestAppendUint32NilBuf(t *testing.T) { + buf := AppendUint32(nil, 1) + if !reflect.DeepEqual(buf, []byte{0, 0, 0, 1}) { + t.Errorf("AppendUint32(nil, 1) => %v, want %v", buf, []byte{0, 0, 0, 1}) + } +} + +func TestAppendUint32EmptyBuf(t *testing.T) { + buf := []byte{} + buf = AppendUint32(buf, 1) + if !reflect.DeepEqual(buf, []byte{0, 0, 0, 1}) { + t.Errorf("AppendUint32(nil, 1) => %v, want %v", buf, []byte{0, 0, 0, 1}) + } +} + +func TestAppendUint32BufWithCapacityDoesNotAllocate(t *testing.T) { + buf := make([]byte, 0, 4) + AppendUint32(buf, 1) + buf = buf[0:4] + if !reflect.DeepEqual(buf, []byte{0, 0, 0, 1}) { + t.Errorf("AppendUint32(nil, 1) => %v, want %v", buf, []byte{0, 0, 0, 1}) + } +} + +func TestAppendUint64NilBuf(t *testing.T) { + buf := AppendUint64(nil, 1) + if !reflect.DeepEqual(buf, []byte{0, 0, 0, 0, 0, 0, 0, 1}) { + t.Errorf("AppendUint64(nil, 1) => %v, want %v", buf, []byte{0, 0, 0, 0, 0, 0, 0, 1}) + } +} + +func TestAppendUint64EmptyBuf(t *testing.T) { + buf := []byte{} + buf = AppendUint64(buf, 1) + if !reflect.DeepEqual(buf, []byte{0, 0, 0, 0, 0, 0, 0, 1}) { + t.Errorf("AppendUint64(nil, 1) => %v, want %v", buf, []byte{0, 0, 0, 0, 0, 0, 0, 1}) + } +} + +func TestAppendUint64BufWithCapacityDoesNotAllocate(t *testing.T) { + buf := make([]byte, 0, 8) + AppendUint64(buf, 1) + buf = buf[0:8] + if !reflect.DeepEqual(buf, []byte{0, 0, 0, 0, 0, 0, 0, 1}) { + t.Errorf("AppendUint64(nil, 1) => %v, want %v", buf, []byte{0, 0, 0, 0, 0, 0, 0, 1}) + } +}