diff --git a/cmd/cgat/main.go b/cmd/cgat/main.go index 4f09e52123cdc4be9444b7af999de8c7d2b2fb04..f0cdd8c9fdfae987b27a6ce0840bb84a293debcb 100644 --- a/cmd/cgat/main.go +++ b/cmd/cgat/main.go @@ -9,7 +9,6 @@ import ( "pggat2/lib/bouncer/backends/v0" "pggat2/lib/bouncer/bouncers/v2" "pggat2/lib/bouncer/frontends/v0" - "pggat2/lib/middleware" "pggat2/lib/middleware/interceptor" "pggat2/lib/middleware/middlewares/eqp" "pggat2/lib/middleware/middlewares/ps" @@ -17,7 +16,6 @@ import ( "pggat2/lib/rob" "pggat2/lib/rob/schedulers/v1" "pggat2/lib/zap" - "pggat2/lib/zap/onebuffer" "pggat2/lib/zap/zio" ) @@ -48,17 +46,18 @@ func testServer(r rob.Scheduler) { panic(err) } rw := zio.MakeReadWriter(conn) - eqps := eqp.MakeServer() - pss := ps.MakeServer() - mw := interceptor.MakeInterceptor(&rw, []middleware.Middleware{ - &eqps, - &pss, - }) - backends.Accept(&mw, "postgres", "password", "uniswap") + eqps := eqp.NewServer() + pss := ps.NewServer() + mw := interceptor.NewInterceptor( + rw, + eqps, + pss, + ) + backends.Accept(mw, "postgres", "password", "uniswap") r.AddSink(0, server{ - rw: &mw, - eqps: &eqps, - pss: &pss, + rw: mw, + eqps: eqps, + pss: pss, }) } @@ -106,27 +105,22 @@ func main() { go func() { source := r.NewSource() client := zio.MakeReadWriter(conn) - defer client.Done() - ob := onebuffer.MakeOnebuffer(&client) - eqpc := eqp.MakeClient() + eqpc := eqp.NewClient() defer eqpc.Done() - psc := ps.MakeClient() - defer psc.Done() - mw := interceptor.MakeInterceptor(&ob, []middleware.Middleware{ + psc := ps.NewClient() + mw := interceptor.NewInterceptor( + client, unterminate.Unterminate, - &eqpc, - &psc, - }) - frontends.Accept(&mw, DefaultParameterStatus) + eqpc, + psc, + ) + frontends.Accept(mw, DefaultParameterStatus) for { - err = ob.Buffer() - if err != nil { - break - } + // TODO(garet) sleep until more work is available source.Do(0, work{ - rw: &mw, - eqpc: &eqpc, - psc: &psc, + rw: mw, + eqpc: eqpc, + psc: psc, }) } }() diff --git a/lib/middleware/context.go b/lib/middleware/context.go index ba696bc9390a106ee1ff476db3b04a2dcb733c87..6df9341ed51526be3b63c7dfa4695894422b2039 100644 --- a/lib/middleware/context.go +++ b/lib/middleware/context.go @@ -6,6 +6,6 @@ type Context interface { // Cancel the current packet Cancel() - // Send to underlying writer - Send(out zap.Out) error + BuildBefore(typed bool) zap.Builder + BuildAfter(typed bool) zap.Builder } diff --git a/lib/middleware/interceptor/context.go b/lib/middleware/interceptor/context.go index 3df2fe1f8a492359b99b05b8ce864f4241bbf085..4c975b95d0f6fbb8c80a53b4cae3ec2ff18255fb 100644 --- a/lib/middleware/interceptor/context.go +++ b/lib/middleware/interceptor/context.go @@ -3,20 +3,16 @@ package interceptor import ( "pggat2/lib/middleware" "pggat2/lib/util/decorator" - "pggat2/lib/zap" ) type Context struct { noCopy decorator.NoCopy cancelled bool - zap.ReadWriter } -func makeContext(rw zap.ReadWriter) Context { - return Context{ - ReadWriter: rw, - } +func makeContext() Context { + return Context{} } func (T *Context) reset() { diff --git a/lib/middleware/interceptor/interceptor.go b/lib/middleware/interceptor/interceptor.go index 3b0cb01753f5a342a4adafc2a6411b0f84419a41..1183bd9c8f6756e4abd70e63ca1e0f14f73bff55 100644 --- a/lib/middleware/interceptor/interceptor.go +++ b/lib/middleware/interceptor/interceptor.go @@ -1,88 +1,75 @@ package interceptor import ( + "time" + "pggat2/lib/middleware" "pggat2/lib/zap" ) type Interceptor struct { middlewares []middleware.Middleware - Context + context Context + rw zap.ReadWriter } -func MakeInterceptor(rw zap.ReadWriter, middlewares []middleware.Middleware) Interceptor { - return Interceptor{ +func NewInterceptor(rw zap.ReadWriter, middlewares ...middleware.Middleware) *Interceptor { + return &Interceptor{ middlewares: middlewares, - Context: makeContext(rw), + context: makeContext(), } } -func (T *Interceptor) Read() (zap.In, error) { - for { - in, err := T.ReadWriter.Read() - if err != nil { - return zap.In{}, err - } +func (T *Interceptor) ReadInto(buffer *zap.Buffer, typed bool) error { + pre := buffer.Count() + + if err := T.rw.ReadInto(buffer, typed); err != nil { + return err + } + + post := buffer.Count() - T.Context.reset() + for i := pre; i < post; i++ { for _, mw := range T.middlewares { - err = mw.Read(&T.Context, in) - if err != nil { - return zap.In{}, err - } - if T.cancelled { - break + T.context.reset() + if err := mw.Read(&T.context, buffer.Inspect(i)); err != nil { + return err } - in.Reset() - } - if !T.cancelled { - return in, nil + if T.context.cancelled { + // TODO(garet) cancel packet + panic("TODO") + } } } + + return nil } -func (T *Interceptor) ReadUntyped() (zap.In, error) { - for { - in, err := T.ReadWriter.ReadUntyped() - if err != nil { - return zap.In{}, err - } +func (T *Interceptor) SetReadDeadline(time time.Time) error { + return T.rw.SetReadDeadline(time) +} - T.Context.reset() +func (T *Interceptor) WriteFrom(buffer *zap.Buffer) error { + for i := 0; i < buffer.Count(); i++ { for _, mw := range T.middlewares { - err = mw.Read(&T.Context, in) - if err != nil { - return zap.In{}, err - } - if T.cancelled { - break + T.context.reset() + if err := mw.Write(&T.context, buffer.Inspect(i)); err != nil { + return err } - in.Reset() - } - if !T.cancelled { - return in, nil + if T.context.cancelled { + // TODO(garet) cancel packet + panic("TODO") + } } } -} -func (T *Interceptor) Send(out zap.Out) error { - T.Context.reset() - for _, mw := range T.middlewares { - err := mw.Send(&T.Context, out) - if err != nil { - return err - } - if T.cancelled { - break - } - } + return T.rw.WriteFrom(buffer) +} - if !T.cancelled { - return T.Context.ReadWriter.Send(out) - } - return nil +func (T *Interceptor) SetWriteDeadline(time time.Time) error { + return T.rw.SetWriteDeadline(time) } var _ zap.ReadWriter = (*Interceptor)(nil) diff --git a/lib/middleware/middleware.go b/lib/middleware/middleware.go index dac5eb766260235209e453e2bde9b8ce65a864fb..c0623c9d04f191a33b0b2d48e3e9752538c75604 100644 --- a/lib/middleware/middleware.go +++ b/lib/middleware/middleware.go @@ -3,6 +3,6 @@ package middleware import "pggat2/lib/zap" type Middleware interface { - Send(ctx Context, out zap.Out) error - Read(ctx Context, in zap.In) error + Write(ctx Context, packet zap.Inspector) error + Read(ctx Context, packet zap.Inspector) error } diff --git a/lib/middleware/middlewares/eqp/client.go b/lib/middleware/middlewares/eqp/client.go index d70fa978adbae2246ef4cb6f32089468a256beae..46569f5045f6fa17e319862422e81be129de36e6 100644 --- a/lib/middleware/middlewares/eqp/client.go +++ b/lib/middleware/middlewares/eqp/client.go @@ -13,8 +13,8 @@ type Client struct { portals map[string]Portal } -func MakeClient() Client { - return Client{ +func NewClient() *Client { + return &Client{ preparedStatements: make(map[string]PreparedStatement), portals: make(map[string]Portal), } @@ -47,8 +47,7 @@ func (T *Client) Done() { } } -func (T *Client) Send(_ middleware.Context, out zap.Out) error { - in := zap.OutToIn(out) +func (T *Client) Write(_ middleware.Context, in zap.Inspector) error { switch in.Type() { case packets.ReadyForQuery: state, ok := packets.ReadReadyForQuery(in) @@ -68,7 +67,7 @@ func (T *Client) Send(_ middleware.Context, out zap.Out) error { return nil } -func (T *Client) Read(ctx middleware.Context, in zap.In) error { +func (T *Client) Read(ctx middleware.Context, in zap.Inspector) error { switch in.Type() { case packets.Query: // clobber unnamed portal and unnamed prepared statement diff --git a/lib/middleware/middlewares/eqp/portal.go b/lib/middleware/middlewares/eqp/portal.go index 4b8c89ed0c780c4325a4f276d0736a7ee78728f8..ec3b8696de7e2f1f7c44a15225bc3f7a7421d175 100644 --- a/lib/middleware/middlewares/eqp/portal.go +++ b/lib/middleware/middlewares/eqp/portal.go @@ -14,7 +14,7 @@ type Portal struct { hash uint64 } -func ReadBind(in zap.In) (destination string, portal Portal, ok bool) { +func ReadBind(in zap.Inspector) (destination string, portal Portal, ok bool) { in.Reset() if in.Type() != packets.Bind { return @@ -27,7 +27,7 @@ func ReadBind(in zap.In) (destination string, portal Portal, ok bool) { if !ok { return } - full := zap.InToOut(in).Full() + full := in.Payload() portal.hash = maphash.Bytes(seed, full) portal.raw = global.GetBytes(int32(len(full))) copy(portal.raw, full) diff --git a/lib/middleware/middlewares/eqp/preparedStatement.go b/lib/middleware/middlewares/eqp/preparedStatement.go index 2f354918cf8fa80b8faf6de54c6603dd433419bc..c6e9f9359fb165679d81b7df4e4fc8b336a531fe 100644 --- a/lib/middleware/middlewares/eqp/preparedStatement.go +++ b/lib/middleware/middlewares/eqp/preparedStatement.go @@ -13,7 +13,7 @@ type PreparedStatement struct { hash uint64 } -func ReadParse(in zap.In) (destination string, preparedStatement PreparedStatement, ok bool) { +func ReadParse(in zap.Inspector) (destination string, preparedStatement PreparedStatement, ok bool) { in.Reset() if in.Type() != packets.Parse { return @@ -22,7 +22,7 @@ func ReadParse(in zap.In) (destination string, preparedStatement PreparedStateme if !ok { return } - full := zap.InToOut(in).Full() + full := in.Payload() preparedStatement.hash = maphash.Bytes(seed, full) preparedStatement.raw = global.GetBytes(int32(len(full))) copy(preparedStatement.raw, full) diff --git a/lib/middleware/middlewares/eqp/server.go b/lib/middleware/middlewares/eqp/server.go index db0809adfb905c714e838d764f2278f40313622e..4cad60714beaf6ec9d507bc04f4d8cbf450f0193 100644 --- a/lib/middleware/middlewares/eqp/server.go +++ b/lib/middleware/middlewares/eqp/server.go @@ -22,13 +22,11 @@ type Server struct { pendingPortals ring.Ring[string] pendingCloses ring.Ring[Close] - buf zap.Buf - peer *Client } -func MakeServer() Server { - return Server{ +func NewServer() *Server { + return &Server{ preparedStatements: make(map[string]uint64), portals: make(map[string]HashedPortal), } @@ -214,8 +212,7 @@ func (T *Server) syncPortal(ctx middleware.Context, target string) error { return T.bindPortal(ctx, target, expected) } -func (T *Server) Send(ctx middleware.Context, out zap.Out) error { - in := zap.OutToIn(out) +func (T *Server) Write(ctx middleware.Context, in zap.Inspector) error { switch in.Type() { case packets.Query: // clobber unnamed portal and unnamed prepared statement @@ -263,7 +260,7 @@ func (T *Server) Send(ctx middleware.Context, out zap.Out) error { return nil } -func (T *Server) Read(ctx middleware.Context, in zap.In) error { +func (T *Server) Read(ctx middleware.Context, in zap.Inspector) error { switch in.Type() { case packets.ParseComplete: ctx.Cancel() @@ -315,7 +312,6 @@ func (T *Server) Read(ctx middleware.Context, in zap.In) error { } func (T *Server) Done() { - T.buf.Done() for name := range T.preparedStatements { T.deletePreparedStatement(name) } diff --git a/lib/middleware/middlewares/ps/client.go b/lib/middleware/middlewares/ps/client.go index bcf9302af916005b27f086ffc8b46f1b4757c7f5..86bf0003654d73a3c25984d1cec50b586e6d3b17 100644 --- a/lib/middleware/middlewares/ps/client.go +++ b/lib/middleware/middlewares/ps/client.go @@ -10,7 +10,6 @@ import ( type Client struct { parameters map[string]string - buf zap.Buf peer *Server dirty bool @@ -18,16 +17,12 @@ type Client struct { middleware.Nil } -func MakeClient() Client { - return Client{ +func NewClient() *Client { + return &Client{ parameters: make(map[string]string), } } -func (T *Client) Done() { - T.buf.Done() -} - func (T *Client) SetServer(peer *Server) { T.dirty = true T.peer = peer @@ -81,8 +76,7 @@ func (T *Client) sync(ctx middleware.Context) error { return nil } -func (T *Client) Send(ctx middleware.Context, out zap.Out) error { - in := zap.OutToIn(out) +func (T *Client) Send(ctx middleware.Context, in zap.Inspector) error { switch in.Type() { case packets.ParameterStatus: key, value, ok := packets.ReadParameterStatus(in) diff --git a/lib/middleware/middlewares/ps/server.go b/lib/middleware/middlewares/ps/server.go index 29f8990be50f9cb1aa5775b2028743e57c8597f6..aa7d9cb109bcaea63d395c22ea9f05b66f4ac83b 100644 --- a/lib/middleware/middlewares/ps/server.go +++ b/lib/middleware/middlewares/ps/server.go @@ -14,13 +14,13 @@ type Server struct { middleware.Nil } -func MakeServer() Server { - return Server{ +func NewServer() *Server { + return &Server{ parameters: make(map[string]string), } } -func (T *Server) Read(_ middleware.Context, in zap.In) error { +func (T *Server) Read(_ middleware.Context, in zap.Inspector) error { switch in.Type() { case packets.ParameterStatus: key, value, ok := packets.ReadParameterStatus(in) diff --git a/lib/middleware/middlewares/unterminate/unterminate.go b/lib/middleware/middlewares/unterminate/unterminate.go index 0f3e0b7cae55077856efb3280e6673370708eaa0..2dc5b2aadd9fca4f6551ca522d45bfce5717a07f 100644 --- a/lib/middleware/middlewares/unterminate/unterminate.go +++ b/lib/middleware/middlewares/unterminate/unterminate.go @@ -14,7 +14,7 @@ type unterm struct { middleware.Nil } -func (unterm) Read(_ middleware.Context, in zap.In) error { +func (unterm) Read(_ middleware.Context, in zap.Inspector) error { if in.Type() == packets.Terminate { return io.EOF } diff --git a/lib/middleware/nil.go b/lib/middleware/nil.go index f749bcd7389e0c894bbad38691927353414fa01a..b8ea179ce1d77db77733e90c35e2b1c9d9ca5ea8 100644 --- a/lib/middleware/nil.go +++ b/lib/middleware/nil.go @@ -4,11 +4,11 @@ import "pggat2/lib/zap" type Nil struct{} -func (Nil) Send(_ Context, _ zap.Out) error { +func (Nil) Write(_ Context, _ zap.Inspector) error { return nil } -func (Nil) Read(_ Context, _ zap.In) error { +func (Nil) Read(_ Context, _ zap.Inspector) error { return nil } diff --git a/lib/zap/buf.go b/lib/zap/buf.go deleted file mode 100644 index 38646535a58962a000703da9295b54c956bfca17..0000000000000000000000000000000000000000 --- a/lib/zap/buf.go +++ /dev/null @@ -1,363 +0,0 @@ -package zap - -import ( - "encoding/binary" - "io" - "math" - - "pggat2/lib/global" - "pggat2/lib/util/decorator" - "pggat2/lib/util/slices" -) - -type Buf struct { - noCopy decorator.NoCopy - - pos int - buf []byte - rev int -} - -func (T *Buf) assertRev(rev int) { - // this check can be turned off when in production mode (for dev, this is helpful though) - if T.rev != rev { - panic("use after resource release") - } -} - -func (T *Buf) setBufLen(length int) { - if cap(T.buf) < length { - newBuf := global.GetBytes(int32(length)) - copy(newBuf, T.buf) - global.PutBytes(T.buf) - T.buf = newBuf - return - } - T.buf = slices.Resize(T.buf, length) -} - -func (T *Buf) ensureBufExtra(extra int) { - if cap(T.buf) < len(T.buf)+extra { - newBuf := global.GetBytes(int32(len(T.buf) + extra)) - copy(newBuf, T.buf) - newBuf = newBuf[:len(T.buf)] - global.PutBytes(T.buf) - T.buf = newBuf - } -} - -func (T *Buf) In() In { - return In{ - buf: T, - rev: T.rev, - } -} - -func (T *Buf) Out() Out { - return Out{ - buf: T, - rev: T.rev, - } -} - -func (T *Buf) ReadByte(reader io.Reader) (byte, error) { - T.rev++ - T.pos = 0 - - T.setBufLen(1) - _, err := io.ReadFull(reader, T.buf) - if err != nil { - return 0, err - } - return T.buf[0], nil -} - -func (T *Buf) Read(reader io.Reader, typed bool) (In, error) { - T.rev++ - T.pos = 0 - - // read header - T.setBufLen(5) - var err error - if typed { - _, err = io.ReadFull(reader, T.buf) - } else { - _, err = io.ReadFull(reader, T.buf[1:]) - } - if err != nil { - return In{}, err - } - - // extract length - length := binary.BigEndian.Uint32(T.buf[1:]) - - // read payload - T.setBufLen(int(length) + 1) - _, err = io.ReadFull(reader, T.buf[5:]) - if err != nil { - return In{}, err - } - - return T.In(), nil -} - -func (T *Buf) WriteByte(writer io.Writer, b byte) error { - T.rev++ - T.pos = 0 - - T.setBufLen(1) - T.buf[0] = b - _, err := writer.Write(T.buf) - return err -} - -func (T *Buf) Write() Out { - T.rev++ - T.pos = 0 - - T.setBufLen(5) - T.buf[0] = 0 - - return T.Out() -} - -func (T *Buf) Swap(buf []byte) []byte { - T.pos = 0 - T.rev++ - - v := T.buf - T.buf = buf - return v -} - -func (T *Buf) Done() { - T.rev++ - T.pos = 0 - global.PutBytes(T.buf) - T.buf = nil -} - -func (T *Buf) full() []byte { - // put length - binary.BigEndian.PutUint32(T.buf[1:], uint32(len(T.buf)-1)) - - if T.readType() == 0 { - // untyped - return T.buf[1:] - } else { - // typed - return T.buf - } -} - -// read methods - -func (T *Buf) resetRead() { - T.pos = 0 -} - -func (T *Buf) readType() Type { - return Type(T.buf[0]) -} - -func (T *Buf) remaining() []byte { - return T.buf[T.pos+5:] -} - -func (T *Buf) readUint8() (uint8, bool) { - rem := T.remaining() - if len(rem) < 1 { - return 0, false - } - T.pos += 1 - return rem[0], true -} - -func (T *Buf) readUint16() (uint16, bool) { - rem := T.remaining() - if len(rem) < 2 { - return 0, false - } - T.pos += 2 - return binary.BigEndian.Uint16(rem), true -} - -func (T *Buf) readUint32() (uint32, bool) { - rem := T.remaining() - if len(rem) < 4 { - return 0, false - } - T.pos += 4 - return binary.BigEndian.Uint32(rem), true -} - -func (T *Buf) readUint64() (uint64, bool) { - rem := T.remaining() - if len(rem) < 8 { - return 0, false - } - T.pos += 8 - return binary.BigEndian.Uint64(rem), true -} - -func (T *Buf) readInt8() (int8, bool) { - v, ok := T.readUint8() - if !ok { - return 0, false - } - return int8(v), true -} - -func (T *Buf) readInt16() (int16, bool) { - v, ok := T.readUint16() - if !ok { - return 0, false - } - return int16(v), true -} - -func (T *Buf) readInt32() (int32, bool) { - v, ok := T.readUint32() - if !ok { - return 0, false - } - return int32(v), true -} - -func (T *Buf) readInt64() (int64, bool) { - v, ok := T.readUint64() - if !ok { - return 0, false - } - return int64(v), true -} - -func (T *Buf) readFloat32() (float32, bool) { - v, ok := T.readUint32() - if !ok { - return 0, false - } - return math.Float32frombits(v), true -} - -func (T *Buf) readFloat64() (float64, bool) { - v, ok := T.readUint64() - if !ok { - return 0, false - } - return math.Float64frombits(v), true -} - -func (T *Buf) readUnsafeString() ([]byte, bool) { - rem := T.remaining() - for i, c := range rem { - if c == 0 { - T.pos += i + 1 - return rem[:i], true - } - } - return nil, false -} - -func (T *Buf) readStringBytes(v []byte) ([]byte, bool) { - str, ok := T.readUnsafeString() - v = slices.Resize(v, len(str)) - copy(v, str) - return v, ok -} - -func (T *Buf) readString() (string, bool) { - str, ok := T.readUnsafeString() - return string(str), ok -} - -func (T *Buf) readBytes(b []byte) bool { - rem := T.remaining() - if len(rem) < len(b) { - return false - } - T.pos += len(b) - copy(b, rem) - return true -} - -func (T *Buf) readUnsafeBytes(count int) ([]byte, bool) { - rem := T.remaining() - if len(rem) < count { - return nil, false - } - T.pos += count - return rem[:count], true -} - -// write methods - -func (T *Buf) resetWrite() { - T.setBufLen(5) - T.buf[0] = 0 -} - -func (T *Buf) writeType(typ Type) { - T.buf[0] = byte(typ) -} - -func (T *Buf) writeUint8(v uint8) { - T.ensureBufExtra(1) - T.buf = append(T.buf, v) -} - -func (T *Buf) writeUint16(v uint16) { - T.ensureBufExtra(2) - T.buf = binary.BigEndian.AppendUint16(T.buf, v) -} - -func (T *Buf) writeUint32(v uint32) { - T.ensureBufExtra(4) - T.buf = binary.BigEndian.AppendUint32(T.buf, v) -} - -func (T *Buf) writeUint64(v uint64) { - T.ensureBufExtra(8) - T.buf = binary.BigEndian.AppendUint64(T.buf, v) -} - -func (T *Buf) writeInt8(v int8) { - T.writeUint8(uint8(v)) -} - -func (T *Buf) writeInt16(v int16) { - T.writeUint16(uint16(v)) -} - -func (T *Buf) writeInt32(v int32) { - T.writeUint32(uint32(v)) -} - -func (T *Buf) writeInt64(v int64) { - T.writeUint64(uint64(v)) -} - -func (T *Buf) writeFloat32(v float32) { - T.writeUint32(math.Float32bits(v)) -} - -func (T *Buf) writeFloat64(v float64) { - T.writeUint64(math.Float64bits(v)) -} - -func (T *Buf) writeStringBytes(v []byte) { - T.ensureBufExtra(len(v) + 1) - T.buf = append(T.buf, v...) - T.buf = append(T.buf, 0) -} - -func (T *Buf) writeString(v string) { - T.ensureBufExtra(len(v) + 1) - T.buf = append(T.buf, v...) - T.buf = append(T.buf, 0) -} - -func (T *Buf) writeBytes(v []byte) { - T.ensureBufExtra(len(v)) - T.buf = append(T.buf, v...) -} diff --git a/lib/zap/buffer.go b/lib/zap/buffer.go new file mode 100644 index 0000000000000000000000000000000000000000..f39580f03155e2036be3c845d65b447bb67499a8 --- /dev/null +++ b/lib/zap/buffer.go @@ -0,0 +1,100 @@ +package zap + +import ( + "encoding/binary" + "io" + + "pggat2/lib/util/slices" +) + +type buffered struct { + offset int + typed bool +} + +type Buffer struct { + primary []byte + items []buffered +} + +func (T *Buffer) ReadFrom(reader io.Reader, typed bool) error { + offset := len(T.primary) + if typed { + T.primary = append(T.primary, 0, 0, 0, 0, 0) + } else { + T.primary = append(T.primary, 0, 0, 0, 0) + } + + _, err := io.ReadFull(reader, T.primary[offset:]) + if err != nil { + T.primary = T.primary[:offset] + return err + } + + var length uint32 + if typed { + length = binary.BigEndian.Uint32(T.primary[offset+1:]) + } else { + length = binary.BigEndian.Uint32(T.primary[offset:]) + } + + T.primary = slices.Resize(T.primary, len(T.primary)+int(length)-4) + + var payload []byte + if typed { + payload = T.primary[offset+5:] + } else { + payload = T.primary[offset+4:] + } + + _, err = io.ReadFull(reader, payload) + return err +} + +func (T *Buffer) WriteInto(writer io.Writer) error { + _, err := writer.Write(T.primary) + return err +} + +func (T *Buffer) Full() []byte { + return T.primary +} + +func (T *Buffer) Reset() { + T.primary = T.primary[:0] + T.items = T.items[:0] +} + +// Count returns the number of packets in the buffer +func (T *Buffer) Count() int { + return len(T.items) +} + +func (T *Buffer) Inspect(i int) Inspector { + item := T.items[i] + inspector := Inspector{ + buffer: T, + offset: item.offset, + typed: item.typed, + } + inspector.Reset() + return inspector +} + +func (T *Buffer) Build(typed bool) Builder { + offset := len(T.primary) + T.items = append(T.items, buffered{ + offset: offset, + typed: typed, + }) + if typed { + T.primary = append(T.primary, 0, 0, 0, 0, 4) + } else { + T.primary = append(T.primary, 0, 0, 0, 4) + } + return Builder{ + buffer: T, + offset: offset, + typed: typed, + } +} diff --git a/lib/zap/builder.go b/lib/zap/builder.go new file mode 100644 index 0000000000000000000000000000000000000000..70421b0c44f7e25673c6b444f2d13143e097cfd3 --- /dev/null +++ b/lib/zap/builder.go @@ -0,0 +1,124 @@ +package zap + +import ( + "encoding/binary" + "math" + + "pggat2/lib/util/slices" +) + +type Builder struct { + buffer *Buffer + offset int + typed bool +} + +func (T *Builder) Header() []byte { + if T.typed { + return T.buffer.primary[T.offset : T.offset+5] + } + return T.buffer.primary[T.offset : T.offset+4] +} + +func (T *Builder) Payload() []byte { + length := T.GetLength() + if T.typed { + return T.buffer.primary[T.offset+5 : T.offset+5+length] + } + return T.buffer.primary[T.offset+4 : T.offset+4+length] +} + +func (T *Builder) next(n int) []byte { + // add n to length + oldLength := T.GetLength() + T.Length(oldLength + n) + + if T.typed { + return T.buffer.primary[T.offset+oldLength+1 : T.offset+oldLength+n+1] + } + return T.buffer.primary[T.offset+oldLength : T.offset+oldLength+n] +} + +func (T *Builder) Reset() { + T.Length(4) + if T.typed { + T.Type(0) + } +} + +func (T *Builder) Length(n int) { + header := T.Header() + length := header[len(header)-4:] + + binary.BigEndian.PutUint32(length, uint32(n)) + + if T.typed { + T.buffer.primary = slices.Resize(T.buffer.primary, T.offset+n+1) + } else { + T.buffer.primary = slices.Resize(T.buffer.primary, T.offset+n) + } +} + +func (T *Builder) GetLength() int { + header := T.Header() + length := header[len(header)-4:] + + return int(binary.BigEndian.Uint32(length)) +} + +func (T *Builder) Type(typ Type) { + if !T.typed { + panic("Type() called on untyped builder") + } + + T.buffer.primary[T.offset] = typ +} + +func (T *Builder) Int8(v int8) { + T.Uint8(uint8(v)) +} + +func (T *Builder) Int16(v int16) { + T.Uint16(uint16(v)) +} + +func (T *Builder) Int32(v int32) { + T.Uint32(uint32(v)) +} + +func (T *Builder) Int64(v int64) { + T.Uint64(uint64(v)) +} + +func (T *Builder) Uint8(v uint8) { + T.next(1)[0] = v +} + +func (T *Builder) Uint16(v uint16) { + binary.BigEndian.PutUint16(T.next(2), v) +} + +func (T *Builder) Uint32(v uint32) { + binary.BigEndian.PutUint32(T.next(4), v) +} + +func (T *Builder) Uint64(v uint64) { + binary.BigEndian.PutUint64(T.next(8), v) +} + +func (T *Builder) Float32(v float32) { + T.Uint32(math.Float32bits(v)) +} + +func (T *Builder) Float64(v float64) { + T.Uint64(math.Float64bits(v)) +} + +func (T *Builder) String(v string) { + copy(T.next(len(v)), v) + T.Uint8(0) +} + +func (T *Builder) Bytes(v []byte) { + copy(T.next(len(v)), v) +} diff --git a/lib/zap/in.go b/lib/zap/in.go deleted file mode 100644 index 5eecf6258d924ab3f7af27ad9c0ca09b75f64470..0000000000000000000000000000000000000000 --- a/lib/zap/in.go +++ /dev/null @@ -1,91 +0,0 @@ -package zap - -type In struct { - buf *Buf - rev int -} - -func (T In) Reset() { - T.buf.assertRev(T.rev) - T.buf.resetRead() -} - -func (T In) Remaining() []byte { - T.buf.assertRev(T.rev) - return T.buf.remaining() -} - -func (T In) Type() Type { - T.buf.assertRev(T.rev) - return T.buf.readType() -} - -func (T In) Int8() (int8, bool) { - T.buf.assertRev(T.rev) - return T.buf.readInt8() -} - -func (T In) Int16() (int16, bool) { - T.buf.assertRev(T.rev) - return T.buf.readInt16() -} - -func (T In) Int32() (int32, bool) { - T.buf.assertRev(T.rev) - return T.buf.readInt32() -} - -func (T In) Int64() (int64, bool) { - T.buf.assertRev(T.rev) - return T.buf.readInt64() -} - -func (T In) Uint8() (uint8, bool) { - T.buf.assertRev(T.rev) - return T.buf.readUint8() -} - -func (T In) Uint16() (uint16, bool) { - T.buf.assertRev(T.rev) - return T.buf.readUint16() -} - -func (T In) Uint32() (uint32, bool) { - T.buf.assertRev(T.rev) - return T.buf.readUint32() -} - -func (T In) Uint64() (uint64, bool) { - T.buf.assertRev(T.rev) - return T.buf.readUint64() -} - -func (T In) Float32() (float32, bool) { - T.buf.assertRev(T.rev) - return T.buf.readFloat32() -} - -func (T In) Float64() (float64, bool) { - T.buf.assertRev(T.rev) - return T.buf.readFloat64() -} - -func (T In) StringBytes(b []byte) ([]byte, bool) { - T.buf.assertRev(T.rev) - return T.buf.readStringBytes(b) -} - -func (T In) String() (string, bool) { - T.buf.assertRev(T.rev) - return T.buf.readString() -} - -func (T In) Bytes(b []byte) bool { - T.buf.assertRev(T.rev) - return T.buf.readBytes(b) -} - -func (T In) UnsafeBytes(count int) ([]byte, bool) { - T.buf.assertRev(T.rev) - return T.buf.readUnsafeBytes(count) -} diff --git a/lib/zap/inspector.go b/lib/zap/inspector.go new file mode 100644 index 0000000000000000000000000000000000000000..408861b7ad77f17b2311a0d65251063f9782ce7d --- /dev/null +++ b/lib/zap/inspector.go @@ -0,0 +1,164 @@ +package zap + +import ( + "encoding/binary" + "math" +) + +type Inspector struct { + buffer *Buffer + offset int + typed bool + + position int +} + +func (T *Inspector) Reset() { + if T.typed { + T.position = 5 + } else { + T.position = 4 + } +} + +func (T *Inspector) Length() int { + var length []byte + if T.typed { + length = T.buffer.primary[T.offset+1 : T.offset+5] + } else { + length = T.buffer.primary[T.offset : T.offset+4] + } + + return int(binary.BigEndian.Uint32(length)) +} + +func (T *Inspector) Payload() []byte { + length := T.Length() + if T.typed { + return T.buffer.primary[T.offset+5 : T.offset+5+length] + } + return T.buffer.primary[T.offset+4 : T.offset+4+length] +} + +func (T *Inspector) Remaining() []byte { + if T.typed { + return T.buffer.primary[T.offset+T.position : T.offset+T.Length()+1] + } else { + return T.buffer.primary[T.offset+T.position : T.offset+T.Length()] + } +} + +func (T *Inspector) Type() Type { + if !T.typed { + panic("call of Type() on untyped packet") + } + + return T.buffer.primary[T.offset] +} + +func (T *Inspector) Int8() (int8, bool) { + if v, ok := T.Uint8(); ok { + return int8(v), true + } + return 0, false +} + +func (T *Inspector) Int16() (int16, bool) { + if v, ok := T.Uint16(); ok { + return int16(v), true + } + return 0, false +} + +func (T *Inspector) Int32() (int32, bool) { + if v, ok := T.Uint32(); ok { + return int32(v), true + } + return 0, false +} + +func (T *Inspector) Int64() (int64, bool) { + if v, ok := T.Uint64(); ok { + return int64(v), true + } + return 0, false +} + +func (T *Inspector) Uint8() (uint8, bool) { + rem := T.Remaining() + if len(rem) < 1 { + return 0, false + } + T.position += 1 + return rem[0], true +} + +func (T *Inspector) Uint16() (uint16, bool) { + rem := T.Remaining() + if len(rem) < 2 { + return 0, false + } + T.position += 2 + return binary.BigEndian.Uint16(rem), true +} + +func (T *Inspector) Uint32() (uint32, bool) { + rem := T.Remaining() + if len(rem) < 4 { + return 0, false + } + T.position += 4 + return binary.BigEndian.Uint32(rem), true +} + +func (T *Inspector) Uint64() (uint64, bool) { + rem := T.Remaining() + if len(rem) < 8 { + return 0, false + } + T.position += 8 + return binary.BigEndian.Uint64(rem), true +} + +func (T *Inspector) Float32() (float32, bool) { + if v, ok := T.Uint32(); ok { + return math.Float32frombits(v), true + } + return 0, false +} + +func (T *Inspector) Float64() (float64, bool) { + if v, ok := T.Uint64(); ok { + return math.Float64frombits(v), true + } + return 0, false +} + +func (T *Inspector) String() (string, bool) { + rem := T.Remaining() + for i, c := range rem { + if c == 0 { + T.position += i + 1 + return string(rem[:i]), true + } + } + return "", false +} + +func (T *Inspector) Bytes(b []byte) bool { + rem := T.Remaining() + if len(rem) < len(b) { + return false + } + T.position += copy(b, rem) + return true +} + +func (T *Inspector) UnsafeBytes(count int) ([]byte, bool) { + rem := T.Remaining() + if len(rem) < count { + return nil, false + } + T.position += count + return rem[:count], true +} diff --git a/lib/zap/onebuffer/onebuffer.go b/lib/zap/onebuffer/onebuffer.go deleted file mode 100644 index 3ecca273fe48d68b44f96ee178c4a7d71d04efa0..0000000000000000000000000000000000000000 --- a/lib/zap/onebuffer/onebuffer.go +++ /dev/null @@ -1,57 +0,0 @@ -package onebuffer - -import ( - "pggat2/lib/util/decorator" - "pggat2/lib/zap" -) - -type Onebuffer struct { - noCopy decorator.NoCopy - - in zap.In - read bool - zap.ReadWriter -} - -func MakeOnebuffer(inner zap.ReadWriter) Onebuffer { - return Onebuffer{ - read: true, - ReadWriter: inner, - } -} - -func (T *Onebuffer) Buffer() error { - if !T.read { - panic("a packet is already buffered in the Onebuffer!") - } - var err error - T.in, err = T.ReadWriter.Read() - T.read = false - return err -} - -func (T *Onebuffer) BufferUntyped() error { - if !T.read { - panic("a packet is already buffered in the Onebuffer!") - } - var err error - T.in, err = T.ReadWriter.ReadUntyped() - T.read = false - return err -} - -func (T *Onebuffer) Read() (zap.In, error) { - if !T.read { - T.read = true - return T.in, nil - } - return T.ReadWriter.Read() -} - -func (T *Onebuffer) ReadUntyped() (zap.In, error) { - if !T.read { - T.read = true - return T.in, nil - } - return T.ReadWriter.ReadUntyped() -} diff --git a/lib/zap/out.go b/lib/zap/out.go deleted file mode 100644 index 551f9d8c1405c9a9c9282bad9244b9af9066943c..0000000000000000000000000000000000000000 --- a/lib/zap/out.go +++ /dev/null @@ -1,86 +0,0 @@ -package zap - -type Out struct { - buf *Buf - rev int -} - -func (T Out) Reset() { - T.buf.assertRev(T.rev) - T.buf.resetWrite() -} - -func (T Out) Full() []byte { - T.buf.assertRev(T.rev) - return T.buf.full() -} - -func (T Out) Type(typ Type) { - T.buf.assertRev(T.rev) - T.buf.writeType(typ) -} - -func (T Out) Int8(v int8) { - T.buf.assertRev(T.rev) - T.buf.writeInt8(v) -} - -func (T Out) Int16(v int16) { - T.buf.assertRev(T.rev) - T.buf.writeInt16(v) -} - -func (T Out) Int32(v int32) { - T.buf.assertRev(T.rev) - T.buf.writeInt32(v) -} - -func (T Out) Int64(v int64) { - T.buf.assertRev(T.rev) - T.buf.writeInt64(v) -} - -func (T Out) Uint8(v uint8) { - T.buf.assertRev(T.rev) - T.buf.writeUint8(v) -} - -func (T Out) Uint16(v uint16) { - T.buf.assertRev(T.rev) - T.buf.writeUint16(v) -} - -func (T Out) Uint32(v uint32) { - T.buf.assertRev(T.rev) - T.buf.writeUint32(v) -} - -func (T Out) Uint64(v uint64) { - T.buf.assertRev(T.rev) - T.buf.writeUint64(v) -} - -func (T Out) Float32(v float32) { - T.buf.assertRev(T.rev) - T.buf.writeFloat32(v) -} - -func (T Out) Float64(v float64) { - T.buf.assertRev(T.rev) - T.buf.writeFloat64(v) -} - -func (T Out) StringBytes(v []byte) { - T.buf.assertRev(T.rev) - T.buf.writeStringBytes(v) -} - -func (T Out) String(v string) { - T.buf.assertRev(T.rev) - T.buf.writeString(v) -} - -func (T Out) Bytes(v []byte) { - T.buf.assertRev(T.rev) - T.buf.writeBytes(v) -} diff --git a/lib/zap/packets/v3.0/authenticationcleartext.go b/lib/zap/packets/v3.0/authenticationcleartext.go index 9b3ebdbc407014b18ae819bc278d301f60f0b79d..bf415543dce965e64ca58092f2695116e622d0cf 100644 --- a/lib/zap/packets/v3.0/authenticationcleartext.go +++ b/lib/zap/packets/v3.0/authenticationcleartext.go @@ -1,10 +1,8 @@ package packets -import ( - "pggat2/lib/zap" -) +import "pggat2/lib/zap" -func ReadAuthenticationCleartext(in zap.In) bool { +func ReadAuthenticationCleartext(in zap.Inspector) bool { in.Reset() if in.Type() != Authentication { return false @@ -19,7 +17,7 @@ func ReadAuthenticationCleartext(in zap.In) bool { return true } -func WriteAuthenticationCleartext(out zap.Out) { +func WriteAuthenticationCleartext(out zap.Builder) { out.Reset() out.Type(Authentication) out.Int32(3) diff --git a/lib/zap/packets/v3.0/authenticationmd5.go b/lib/zap/packets/v3.0/authenticationmd5.go index 2bcef712c0335e3774aabb430ff0662fd1c83c13..773066a0a1d386ed6a8ac0088507a8f147ebaa51 100644 --- a/lib/zap/packets/v3.0/authenticationmd5.go +++ b/lib/zap/packets/v3.0/authenticationmd5.go @@ -1,10 +1,8 @@ package packets -import ( - "pggat2/lib/zap" -) +import "pggat2/lib/zap" -func ReadAuthenticationMD5(in zap.In) ([4]byte, bool) { +func ReadAuthenticationMD5(in zap.Inspector) ([4]byte, bool) { in.Reset() if in.Type() != Authentication { return [4]byte{}, false @@ -24,7 +22,7 @@ func ReadAuthenticationMD5(in zap.In) ([4]byte, bool) { return salt, true } -func WriteAuthenticationMD5(out zap.Out, salt [4]byte) { +func WriteAuthenticationMD5(out zap.Builder, salt [4]byte) { out.Reset() out.Type(Authentication) out.Uint32(5) diff --git a/lib/zap/packets/v3.0/authenticationok.go b/lib/zap/packets/v3.0/authenticationok.go index cd682ec5a31f4bb49cb0fa3d6c82b275ee46ec16..29271a7fb27e676a764c80be2f1cc5ccd995ba2f 100644 --- a/lib/zap/packets/v3.0/authenticationok.go +++ b/lib/zap/packets/v3.0/authenticationok.go @@ -4,7 +4,7 @@ import ( "pggat2/lib/zap" ) -func ReadAuthenticationOk(in zap.In) bool { +func ReadAuthenticationOk(in zap.Inspector) bool { in.Reset() if in.Type() != Authentication { return false @@ -19,7 +19,7 @@ func ReadAuthenticationOk(in zap.In) bool { return true } -func WriteAuthenticationOk(out zap.Out) { +func WriteAuthenticationOk(out zap.Builder) { out.Reset() out.Type(Authentication) out.Int32(0) diff --git a/lib/zap/packets/v3.0/authenticationresponse.go b/lib/zap/packets/v3.0/authenticationresponse.go index 1dd93e0247ee1d66426f1c56bf356ae2742f3f88..eec9dc69d5e9ab2990dbafd78aadba07afb98d52 100644 --- a/lib/zap/packets/v3.0/authenticationresponse.go +++ b/lib/zap/packets/v3.0/authenticationresponse.go @@ -1,10 +1,8 @@ package packets -import ( - "pggat2/lib/zap" -) +import "pggat2/lib/zap" -func ReadAuthenticationResponse(in zap.In) ([]byte, bool) { +func ReadAuthenticationResponse(in zap.Inspector) ([]byte, bool) { in.Reset() if in.Type() != AuthenticationResponse { return nil, false @@ -12,7 +10,7 @@ func ReadAuthenticationResponse(in zap.In) ([]byte, bool) { return in.Remaining(), true } -func WriteAuthenticationResponse(out zap.Out, resp []byte) { +func WriteAuthenticationResponse(out zap.Builder, resp []byte) { out.Reset() out.Type(AuthenticationResponse) out.Bytes(resp) diff --git a/lib/zap/packets/v3.0/authenticationsasl.go b/lib/zap/packets/v3.0/authenticationsasl.go index 5d6ba59fab4be8c8402f394ee3ba2117fb83ebef..dd9bc38e6b37b4b33a98978bdabe45d75a9b7dbd 100644 --- a/lib/zap/packets/v3.0/authenticationsasl.go +++ b/lib/zap/packets/v3.0/authenticationsasl.go @@ -1,10 +1,8 @@ package packets -import ( - "pggat2/lib/zap" -) +import "pggat2/lib/zap" -func ReadAuthenticationSASL(in zap.In) ([]string, bool) { +func ReadAuthenticationSASL(in zap.Inspector) ([]string, bool) { in.Reset() if in.Type() != Authentication { return nil, false @@ -46,7 +44,7 @@ func ReadAuthenticationSASL(in zap.In) ([]string, bool) { return mechanisms, true } -func WriteAuthenticationSASL(out zap.Out, mechanisms []string) { +func WriteAuthenticationSASL(out zap.Builder, mechanisms []string) { out.Reset() out.Type(Authentication) out.Int32(10) diff --git a/lib/zap/packets/v3.0/authenticationsaslcontinue.go b/lib/zap/packets/v3.0/authenticationsaslcontinue.go index 269a373feb431f9050e088fbc45f7fcf1574ba42..cf7f18ff42f1125f9d4e3a7d92137fa506370276 100644 --- a/lib/zap/packets/v3.0/authenticationsaslcontinue.go +++ b/lib/zap/packets/v3.0/authenticationsaslcontinue.go @@ -4,7 +4,7 @@ import ( "pggat2/lib/zap" ) -func ReadAuthenticationSASLContinue(in zap.In) ([]byte, bool) { +func ReadAuthenticationSASLContinue(in zap.Inspector) ([]byte, bool) { in.Reset() if in.Type() != Authentication { return nil, false @@ -19,7 +19,7 @@ func ReadAuthenticationSASLContinue(in zap.In) ([]byte, bool) { return in.Remaining(), true } -func WriteAuthenticationSASLContinue(out zap.Out, resp []byte) { +func WriteAuthenticationSASLContinue(out zap.Builder, resp []byte) { out.Reset() out.Type(Authentication) out.Int32(11) diff --git a/lib/zap/packets/v3.0/authenticationsaslfinal.go b/lib/zap/packets/v3.0/authenticationsaslfinal.go index 747a95091d0e0d87d67f679dad1a0785dd51642f..b29ae94efd13ff8b0e3d01ce54ee487009e34ebb 100644 --- a/lib/zap/packets/v3.0/authenticationsaslfinal.go +++ b/lib/zap/packets/v3.0/authenticationsaslfinal.go @@ -1,10 +1,8 @@ package packets -import ( - "pggat2/lib/zap" -) +import "pggat2/lib/zap" -func ReadAuthenticationSASLFinal(in zap.In) ([]byte, bool) { +func ReadAuthenticationSASLFinal(in zap.Inspector) ([]byte, bool) { in.Reset() if in.Type() != Authentication { return nil, false @@ -19,7 +17,7 @@ func ReadAuthenticationSASLFinal(in zap.In) ([]byte, bool) { return in.Remaining(), true } -func WriteAuthenticationSASLFinal(out zap.Out, resp []byte) { +func WriteAuthenticationSASLFinal(out zap.Builder, resp []byte) { out.Reset() out.Type(Authentication) out.Int32(12) diff --git a/lib/zap/packets/v3.0/backendkeydata.go b/lib/zap/packets/v3.0/backendkeydata.go index feb05bac9571bfc2c4df9cc10dbf49d6653d70f4..74040a54ce33a3eceb4331ba839cb81da2d144b5 100644 --- a/lib/zap/packets/v3.0/backendkeydata.go +++ b/lib/zap/packets/v3.0/backendkeydata.go @@ -1,10 +1,8 @@ package packets -import ( - "pggat2/lib/zap" -) +import "pggat2/lib/zap" -func ReadBackendKeyData(in zap.In) ([8]byte, bool) { +func ReadBackendKeyData(in zap.Inspector) ([8]byte, bool) { in.Reset() if in.Type() != BackendKeyData { return [8]byte{}, false @@ -17,7 +15,7 @@ func ReadBackendKeyData(in zap.In) ([8]byte, bool) { return cancellationKey, true } -func WriteBackendKeyData(out zap.Out, cancellationKey [8]byte) { +func WriteBackendKeyData(out zap.Builder, cancellationKey [8]byte) { out.Reset() out.Type(BackendKeyData) out.Bytes(cancellationKey[:]) diff --git a/lib/zap/packets/v3.0/bind.go b/lib/zap/packets/v3.0/bind.go index b6effa444ee915d88d1166b61d7866298c43e653..9691883930d7f2da0d67986cdae80bc471a5e43d 100644 --- a/lib/zap/packets/v3.0/bind.go +++ b/lib/zap/packets/v3.0/bind.go @@ -4,7 +4,7 @@ import ( "pggat2/lib/zap" ) -func ReadBind(in zap.In) (destination string, source string, parameterFormatCodes []int16, parameterValues [][]byte, resultFormatCodes []int16, ok bool) { +func ReadBind(in zap.Inspector) (destination string, source string, parameterFormatCodes []int16, parameterValues [][]byte, resultFormatCodes []int16, ok bool) { in.Reset() if in.Type() != Bind { return @@ -70,7 +70,7 @@ func ReadBind(in zap.In) (destination string, source string, parameterFormatCode return } -func WriteBind(out zap.Out, destination, source string, parameterFormatCodes []int16, parameterValues [][]byte, resultFormatCodes []int16) { +func WriteBind(out zap.Builder, destination, source string, parameterFormatCodes []int16, parameterValues [][]byte, resultFormatCodes []int16) { out.Reset() out.Type(Bind) out.String(destination) diff --git a/lib/zap/packets/v3.0/close.go b/lib/zap/packets/v3.0/close.go index 05e11bf9beb948e5ba639f7dbee8b3d5f9bcd6c6..e382f3d3b65f8713910671e3b3eb0cbe2481d5ee 100644 --- a/lib/zap/packets/v3.0/close.go +++ b/lib/zap/packets/v3.0/close.go @@ -1,10 +1,8 @@ package packets -import ( - "pggat2/lib/zap" -) +import "pggat2/lib/zap" -func ReadClose(in zap.In) (which uint8, target string, ok bool) { +func ReadClose(in zap.Inspector) (which uint8, target string, ok bool) { in.Reset() if in.Type() != Close { return @@ -20,7 +18,7 @@ func ReadClose(in zap.In) (which uint8, target string, ok bool) { return } -func WriteClose(out zap.Out, which uint8, target string) { +func WriteClose(out zap.Builder, which uint8, target string) { out.Reset() out.Type(Close) out.Uint8(which) diff --git a/lib/zap/packets/v3.0/describe.go b/lib/zap/packets/v3.0/describe.go index 906abfbce91f265e1b1a729d8d8ece43a7300ccb..a5b52fff301e9ab392a61d7c94ed8957defd79df 100644 --- a/lib/zap/packets/v3.0/describe.go +++ b/lib/zap/packets/v3.0/describe.go @@ -4,7 +4,7 @@ import ( "pggat2/lib/zap" ) -func ReadDescribe(in zap.In) (which uint8, target string, ok bool) { +func ReadDescribe(in zap.Inspector) (which uint8, target string, ok bool) { in.Reset() if in.Type() != Describe { return @@ -20,7 +20,7 @@ func ReadDescribe(in zap.In) (which uint8, target string, ok bool) { return } -func WriteDescribe(out zap.Out, which uint8, target string) { +func WriteDescribe(out zap.Builder, which uint8, target string) { out.Reset() out.Type(Describe) out.Uint8(which) diff --git a/lib/zap/packets/v3.0/errorresponse.go b/lib/zap/packets/v3.0/errorresponse.go index 2fae634108448d360bebf29b3c1de3fdcf514d83..d6f36cd59e88479df8888188f9e8d353591c65e3 100644 --- a/lib/zap/packets/v3.0/errorresponse.go +++ b/lib/zap/packets/v3.0/errorresponse.go @@ -5,7 +5,7 @@ import ( "pggat2/lib/zap" ) -func ReadErrorResponse(in zap.In) (perror.Error, bool) { +func ReadErrorResponse(in zap.Inspector) (perror.Error, bool) { in.Reset() if in.Type() != ErrorResponse { return nil, false @@ -54,7 +54,7 @@ func ReadErrorResponse(in zap.In) (perror.Error, bool) { ), true } -func WriteErrorResponse(out zap.Out, err perror.Error) { +func WriteErrorResponse(out zap.Builder, err perror.Error) { out.Reset() out.Type(ErrorResponse) diff --git a/lib/zap/packets/v3.0/execute.go b/lib/zap/packets/v3.0/execute.go index 84fa064d11d75af53bc9b7c556fdbf481c4d57ef..7c32249a94cea7809e89b8e7223e3cbeb9e2a479 100644 --- a/lib/zap/packets/v3.0/execute.go +++ b/lib/zap/packets/v3.0/execute.go @@ -4,7 +4,7 @@ import ( "pggat2/lib/zap" ) -func ReadExecute(in zap.In) (target string, maxRows int32, ok bool) { +func ReadExecute(in zap.Inspector) (target string, maxRows int32, ok bool) { in.Reset() if in.Type() != Execute { return @@ -20,7 +20,7 @@ func ReadExecute(in zap.In) (target string, maxRows int32, ok bool) { return } -func WriteExecute(out zap.Out, target string, maxRows int32) { +func WriteExecute(out zap.Builder, target string, maxRows int32) { out.Reset() out.Type(Execute) out.String(target) diff --git a/lib/zap/packets/v3.0/negotiateprotocolversion.go b/lib/zap/packets/v3.0/negotiateprotocolversion.go index db2df2b807345d331eb041bc8a976c3ae5f5c93d..73595713bb14c2490ccbc3559bc5245f70ca37a6 100644 --- a/lib/zap/packets/v3.0/negotiateprotocolversion.go +++ b/lib/zap/packets/v3.0/negotiateprotocolversion.go @@ -4,7 +4,7 @@ import ( "pggat2/lib/zap" ) -func ReadNegotiateProtocolVersion(in zap.In) (minorProtocolVersion int32, unrecognizedOptions []string, ok bool) { +func ReadNegotiateProtocolVersion(in zap.Inspector) (minorProtocolVersion int32, unrecognizedOptions []string, ok bool) { in.Reset() if in.Type() != NegotiateProtocolVersion { return @@ -31,7 +31,7 @@ func ReadNegotiateProtocolVersion(in zap.In) (minorProtocolVersion int32, unreco return } -func WriteNegotiateProtocolVersion(out zap.Out, minorProtocolVersion int32, unrecognizedOptions []string) { +func WriteNegotiateProtocolVersion(out zap.Builder, minorProtocolVersion int32, unrecognizedOptions []string) { out.Reset() out.Type(NegotiateProtocolVersion) out.Int32(minorProtocolVersion) diff --git a/lib/zap/packets/v3.0/parameterstatus.go b/lib/zap/packets/v3.0/parameterstatus.go index 09b304cf4700148761935b123d1077f2b72074aa..cff7246aa0c17a2a82ce6a401e12276285e246ef 100644 --- a/lib/zap/packets/v3.0/parameterstatus.go +++ b/lib/zap/packets/v3.0/parameterstatus.go @@ -1,10 +1,8 @@ package packets -import ( - "pggat2/lib/zap" -) +import "pggat2/lib/zap" -func ReadParameterStatus(in zap.In) (key, value string, ok bool) { +func ReadParameterStatus(in zap.Inspector) (key, value string, ok bool) { in.Reset() if in.Type() != ParameterStatus { return @@ -20,7 +18,7 @@ func ReadParameterStatus(in zap.In) (key, value string, ok bool) { return } -func WriteParameterStatus(out zap.Out, key, value string) { +func WriteParameterStatus(out zap.Builder, key, value string) { out.Reset() out.Type(ParameterStatus) out.String(key) diff --git a/lib/zap/packets/v3.0/parse.go b/lib/zap/packets/v3.0/parse.go index 087fe350b2a1bda4044575527f5aa5e1ca71d6cc..3d7c3f31be570c76bd26482a20cc7305014ccfd3 100644 --- a/lib/zap/packets/v3.0/parse.go +++ b/lib/zap/packets/v3.0/parse.go @@ -4,7 +4,7 @@ import ( "pggat2/lib/zap" ) -func ReadParse(in zap.In) (destination string, query string, parameterDataTypes []int32, ok bool) { +func ReadParse(in zap.Inspector) (destination string, query string, parameterDataTypes []int32, ok bool) { in.Reset() if in.Type() != Parse { return @@ -34,7 +34,7 @@ func ReadParse(in zap.In) (destination string, query string, parameterDataTypes return } -func WriteParse(out zap.Out, destination string, query string, parameterDataTypes []int32) { +func WriteParse(out zap.Builder, destination string, query string, parameterDataTypes []int32) { out.Reset() out.Type(Parse) out.String(destination) diff --git a/lib/zap/packets/v3.0/passwordmessage.go b/lib/zap/packets/v3.0/passwordmessage.go index 89d16ad41ebf1915f4699c0a8d80bc814bb64fa1..f777e94c95118f457b1e70ec426f00e8673ba746 100644 --- a/lib/zap/packets/v3.0/passwordmessage.go +++ b/lib/zap/packets/v3.0/passwordmessage.go @@ -4,7 +4,7 @@ import ( "pggat2/lib/zap" ) -func ReadPasswordMessage(in zap.In) (string, bool) { +func ReadPasswordMessage(in zap.Inspector) (string, bool) { in.Reset() if in.Type() != AuthenticationResponse { return "", false @@ -16,7 +16,7 @@ func ReadPasswordMessage(in zap.In) (string, bool) { return password, true } -func WritePasswordMessage(out zap.Out, password string) { +func WritePasswordMessage(out zap.Builder, password string) { out.Reset() out.Type(AuthenticationResponse) out.String(password) diff --git a/lib/zap/packets/v3.0/readyforquery.go b/lib/zap/packets/v3.0/readyforquery.go index b6d5ea82b71a5f2fd7d8df989e3b75f62a58b98b..f023ceb06cc5de23a20eee25300573e0a6d299b5 100644 --- a/lib/zap/packets/v3.0/readyforquery.go +++ b/lib/zap/packets/v3.0/readyforquery.go @@ -4,7 +4,7 @@ import ( "pggat2/lib/zap" ) -func ReadReadyForQuery(in zap.In) (byte, bool) { +func ReadReadyForQuery(in zap.Inspector) (byte, bool) { in.Reset() if in.Type() != ReadyForQuery { return 0, false @@ -16,7 +16,7 @@ func ReadReadyForQuery(in zap.In) (byte, bool) { return state, true } -func WriteReadyForQuery(out zap.Out, state uint8) { +func WriteReadyForQuery(out zap.Builder, state uint8) { out.Reset() out.Type(ReadyForQuery) out.Uint8(state) diff --git a/lib/zap/packets/v3.0/saslinitialresponse.go b/lib/zap/packets/v3.0/saslinitialresponse.go index 4946f9bebd8b963501ae8d36b93069b9ac5bba35..6cbcd94b694e7fcc5a74c19c931b8be81629a823 100644 --- a/lib/zap/packets/v3.0/saslinitialresponse.go +++ b/lib/zap/packets/v3.0/saslinitialresponse.go @@ -4,7 +4,7 @@ import ( "pggat2/lib/zap" ) -func ReadSASLInitialResponse(in zap.In) (mechanism string, initialResponse []byte, ok bool) { +func ReadSASLInitialResponse(in zap.Inspector) (mechanism string, initialResponse []byte, ok bool) { in.Reset() if in.Type() != AuthenticationResponse { return @@ -28,7 +28,7 @@ func ReadSASLInitialResponse(in zap.In) (mechanism string, initialResponse []byt return } -func WriteSASLInitialResponse(out zap.Out, mechanism string, initialResponse []byte) { +func WriteSASLInitialResponse(out zap.Builder, mechanism string, initialResponse []byte) { out.Reset() out.Type(AuthenticationResponse) out.String(mechanism) diff --git a/lib/zap/reader.go b/lib/zap/reader.go index 465f70ebb77d0a969398463fe5a13f7d8d1c5071..f16c4a554baf47b53231cd0eb8d022419549464e 100644 --- a/lib/zap/reader.go +++ b/lib/zap/reader.go @@ -1,14 +1,9 @@ package zap -import ( - "io" - "time" -) +import "time" type Reader interface { - io.ByteReader + ReadInto(buffer *Buffer, typed bool) error - SetReadDeadline(deadline time.Time) error - Read() (In, error) - ReadUntyped() (In, error) + SetReadDeadline(time time.Time) error } diff --git a/lib/zap/readwriter.go b/lib/zap/readwriter.go index cc1093f22ec39dbd86210719ef3fa17744244d1f..22b2a309250d2e694b5a5c9ef842a57780499e85 100644 --- a/lib/zap/readwriter.go +++ b/lib/zap/readwriter.go @@ -1,9 +1,6 @@ package zap -import "time" - type ReadWriter interface { - SetDeadline(deadline time.Time) error Reader Writer } diff --git a/lib/zap/regender.go b/lib/zap/regender.go deleted file mode 100644 index 9adb2ee7a3471c1b00320be04f6d750b93c28e7c..0000000000000000000000000000000000000000 --- a/lib/zap/regender.go +++ /dev/null @@ -1,15 +0,0 @@ -package zap - -func InToOut(in In) Out { - return Out{ - buf: in.buf, - rev: in.rev, - } -} - -func OutToIn(out Out) In { - return In{ - buf: out.buf, - rev: out.rev, - } -} diff --git a/lib/zap/writer.go b/lib/zap/writer.go index b58cbf7f6127f81cae9e5a209670e765dae9328e..aff1a982107897d4f5761d5a9945384a970a1bb9 100644 --- a/lib/zap/writer.go +++ b/lib/zap/writer.go @@ -1,14 +1,9 @@ package zap -import ( - "io" - "time" -) +import "time" type Writer interface { - io.ByteWriter + WriteFrom(buffer *Buffer) error - SetWriteDeadline(deadline time.Time) error - Write() Out - Send(Out) error + SetWriteDeadline(time time.Time) error } diff --git a/lib/zap/zio/reader.go b/lib/zap/zio/reader.go deleted file mode 100644 index c405d25e0bab71f67485fd6a17036a206e9edeaf..0000000000000000000000000000000000000000 --- a/lib/zap/zio/reader.go +++ /dev/null @@ -1,44 +0,0 @@ -package zio - -import ( - "io" - "time" - - "pggat2/lib/util/dio" - "pggat2/lib/zap" -) - -type Reader struct { - reader dio.Reader - r io.Reader - buf zap.Buf -} - -func MakeReader(reader dio.Reader) Reader { - return Reader{ - reader: reader, - r: reader, - } -} - -func (T *Reader) SetReadDeadline(deadline time.Time) error { - return T.reader.SetReadDeadline(deadline) -} - -func (T *Reader) ReadByte() (byte, error) { - return T.buf.ReadByte(T.r) -} - -func (T *Reader) Read() (zap.In, error) { - return T.buf.Read(T.r, true) -} - -func (T *Reader) ReadUntyped() (zap.In, error) { - return T.buf.Read(T.r, false) -} - -func (T *Reader) Done() { - T.buf.Done() -} - -var _ zap.Reader = (*Reader)(nil) diff --git a/lib/zap/zio/readwriter.go b/lib/zap/zio/readwriter.go index d4fedd451cc90240752e29a07fd0711e798adee7..f1fe3ba3d1b8b54564c47eed4b6e26cff5f62b79 100644 --- a/lib/zap/zio/readwriter.go +++ b/lib/zap/zio/readwriter.go @@ -9,60 +9,39 @@ import ( ) type ReadWriter struct { - rw dio.ReadWriter - // they are seperated out to prevent an expensive runtime.convI2I (which causes runtime.getitab) - r io.Reader - w io.Writer - buf zap.Buf + io dio.ReadWriter } -func MakeReadWriter(rw dio.ReadWriter) ReadWriter { +func MakeReadWriter(io dio.ReadWriter) ReadWriter { return ReadWriter{ - rw: rw, - r: rw, - w: rw, + io: io, } } -func (T *ReadWriter) SetDeadline(deadline time.Time) error { - return T.rw.SetDeadline(deadline) -} - -func (T *ReadWriter) SetReadDeadline(deadline time.Time) error { - return T.rw.SetReadDeadline(deadline) -} - -func (T *ReadWriter) SetWriteDeadline(deadline time.Time) error { - return T.rw.SetWriteDeadline(deadline) -} - -func (T *ReadWriter) ReadByte() (byte, error) { - return T.buf.ReadByte(T.r) -} - -func (T *ReadWriter) Read() (zap.In, error) { - return T.buf.Read(T.r, true) -} +func (T ReadWriter) ReadInto(buffer *zap.Buffer, typed bool) error { + builder := buffer.Build(typed) + _, err := io.ReadFull(T.io, builder.Header()) + if err != nil { + return err + } -func (T *ReadWriter) ReadUntyped() (zap.In, error) { - return T.buf.Read(T.r, false) -} + builder.Length(builder.GetLength()) -func (T *ReadWriter) WriteByte(b byte) error { - return T.buf.WriteByte(T.w, b) + _, err = io.ReadFull(T.io, builder.Payload()) + return err } -func (T *ReadWriter) Write() zap.Out { - return T.buf.Write() +func (T ReadWriter) WriteFrom(buffer *zap.Buffer) error { + _, err := T.io.Write(buffer.Full()) + return err } -func (T *ReadWriter) Send(out zap.Out) error { - _, err := T.rw.Write(out.Full()) - return err +func (T ReadWriter) SetReadDeadline(time time.Time) error { + return T.io.SetReadDeadline(time) } -func (T *ReadWriter) Done() { - T.buf.Done() +func (T ReadWriter) SetWriteDeadline(time time.Time) error { + return T.io.SetWriteDeadline(time) } -var _ zap.ReadWriter = (*ReadWriter)(nil) +var _ zap.ReadWriter = ReadWriter{} diff --git a/lib/zap/zio/writer.go b/lib/zap/zio/writer.go deleted file mode 100644 index 8d5b6c104cebe8885bd89a5ab37671ad7a0f599f..0000000000000000000000000000000000000000 --- a/lib/zap/zio/writer.go +++ /dev/null @@ -1,45 +0,0 @@ -package zio - -import ( - "io" - "time" - - "pggat2/lib/util/dio" - "pggat2/lib/zap" -) - -type Writer struct { - writer dio.Writer - w io.Writer - buf zap.Buf -} - -func MakeWriter(writer dio.Writer) Writer { - return Writer{ - writer: writer, - w: writer, - } -} - -func (T *Writer) SetWriteDeadline(deadline time.Time) error { - return T.writer.SetWriteDeadline(deadline) -} - -func (T *Writer) WriteByte(b byte) error { - return T.buf.WriteByte(T.w, b) -} - -func (T *Writer) Write() zap.Out { - return T.buf.Write() -} - -func (T *Writer) Send(out zap.Out) error { - _, err := T.writer.Write(out.Full()) - return err -} - -func (T *Writer) Done() { - T.buf.Done() -} - -var _ zap.Writer = (*Writer)(nil) diff --git a/lib/zap3/packet.go b/lib/zap3/packet.go new file mode 100644 index 0000000000000000000000000000000000000000..d80df9d70ce017faedce11af5d7c4a9bbecec908 --- /dev/null +++ b/lib/zap3/packet.go @@ -0,0 +1,273 @@ +package zap3 + +import ( + "encoding/binary" + "io" + "math" + "net" + + "pggat2/lib/util/slices" +) + +type Packets struct { + packets net.Buffers +} + +func (T *Packets) WriteTo(w io.Writer) (int64, error) { + return T.packets.WriteTo(w) +} + +func (T *Packets) Append(packet []byte) { + T.packets = append(T.packets, packet) +} + +func (T *Packets) Clear() { + T.packets = T.packets[:0] +} + +type PacketReader []byte + +func (T *PacketReader) ReadInt8() (int8, bool) { + if v, ok := T.ReadUint8(); ok { + return int8(v), true + } + return 0, false +} + +func (T *PacketReader) ReadInt16() (int16, bool) { + if v, ok := T.ReadUint16(); ok { + return int16(v), true + } + return 0, false +} + +func (T *PacketReader) ReadInt32() (int32, bool) { + if v, ok := T.ReadUint32(); ok { + return int32(v), true + } + return 0, false +} + +func (T *PacketReader) ReadInt64() (int64, bool) { + if v, ok := T.ReadUint64(); ok { + return int64(v), true + } + return 0, false +} + +func (T *PacketReader) ReadUint8() (uint8, bool) { + if len(*T) < 1 { + return 0, false + } + + v := (*T)[0] + *T = (*T)[1:] + return v, true +} + +func (T *PacketReader) ReadUint16() (uint16, bool) { + if len(*T) < 2 { + return 0, false + } + + v := binary.BigEndian.Uint16(*T) + *T = (*T)[2:] + return v, true +} + +func (T *PacketReader) ReadUint32() (uint32, bool) { + if len(*T) < 4 { + return 0, false + } + + v := binary.BigEndian.Uint32(*T) + *T = (*T)[4:] + return v, true +} + +func (T *PacketReader) ReadUint64() (uint64, bool) { + if len(*T) < 8 { + return 0, false + } + + v := binary.BigEndian.Uint64(*T) + *T = (*T)[8:] + return v, true +} + +func (T *PacketReader) ReadFloat32() (float32, bool) { + if v, ok := T.ReadUint32(); ok { + return math.Float32frombits(v), true + } + + return 0, false +} + +func (T *PacketReader) ReadFloat64() (float64, bool) { + if v, ok := T.ReadUint64(); ok { + return math.Float64frombits(v), true + } + + return 0, false +} + +func (T *PacketReader) ReadString() (string, bool) { + for i, b := range *T { + if b == 0 { + v := (*T)[:i] + *T = (*T)[i+1:] + return string(v), true + } + } + + return "", false +} + +func (T *PacketReader) ReadBytes(b []byte) bool { + if len(*T) < len(b) { + return false + } + + copy(b, *T) + *T = (*T)[len(b):] + return true +} + +type PacketWriter []byte + +func (T *PacketWriter) WriteInt8(v int8) { + T.WriteUint8(uint8(v)) +} + +func (T *PacketWriter) WriteInt16(v int16) { + T.WriteUint16(uint16(v)) +} + +func (T *PacketWriter) WriteInt32(v int32) { + T.WriteUint32(uint32(v)) +} + +func (T *PacketWriter) WriteInt64(v int64) { + T.WriteUint64(uint64(v)) +} + +func (T *PacketWriter) WriteUint8(v uint8) { + *T = append(*T, v) +} + +func (T *PacketWriter) WriteUint16(v uint16) { + *T = binary.BigEndian.AppendUint16(*T, v) +} + +func (T *PacketWriter) WriteUint32(v uint32) { + *T = binary.BigEndian.AppendUint32(*T, v) +} + +func (T *PacketWriter) WriteUint64(v uint64) { + *T = binary.BigEndian.AppendUint64(*T, v) +} + +func (T *PacketWriter) WriteFloat32(v float32) { + T.WriteUint32(math.Float32bits(v)) +} + +func (T *PacketWriter) WriteFloat64(v float64) { + T.WriteUint64(math.Float64bits(v)) +} + +func (T *PacketWriter) WriteString(v string) { + *T = append(*T, v...) + T.WriteUint8(0) +} + +func (T *PacketWriter) WriteBytes(v []byte) { + *T = append(*T, v...) +} + +type ReadablePacket struct { + PacketReader + typ Type +} + +func (T *ReadablePacket) ReadType() Type { + return T.typ +} + +type Packet struct { + PacketWriter +} + +func (T *Packet) ReadFrom(r io.Reader) (int64, error) { + T.PacketWriter = slices.Resize(T.PacketWriter, 5) + n, err := io.ReadFull(r, T.PacketWriter) + if err != nil { + return int64(n), err + } + length := T.Length() + T.PacketWriter = slices.Resize(T.PacketWriter, int(length)+1) + m, err := io.ReadFull(r, T.Payload()) + return int64(n + m), err +} + +func (T *Packet) WriteTo(w io.Writer) (int64, error) { + n, err := w.Write(T.PacketWriter) + return int64(n), err +} + +func (T *Packet) Length() uint32 { + return binary.BigEndian.Uint32(T.PacketWriter[1:]) +} + +func (T *Packet) Payload() []byte { + return T.PacketWriter[5:] +} + +func (T *Packet) WriteType(v Type) { + T.PacketWriter[0] = v +} + +func (T *Packet) Read() ReadablePacket { + return ReadablePacket{ + typ: T.PacketWriter[0], + PacketReader: T.Payload(), + } +} + +type UntypedReadablePacket struct { + PacketReader +} + +type UntypedPacket struct { + PacketWriter +} + +func (T *UntypedPacket) ReadFrom(r io.Reader) (int64, error) { + T.PacketWriter = slices.Resize(T.PacketWriter, 4) + n, err := io.ReadFull(r, T.PacketWriter) + if err != nil { + return int64(n), err + } + length := T.Length() + T.PacketWriter = slices.Resize(T.PacketWriter, int(length)) + m, err := io.ReadFull(r, T.Payload()) + return int64(n + m), err +} + +func (T *UntypedPacket) WriteTo(w io.Writer) (int64, error) { + n, err := w.Write(T.PacketWriter) + return int64(n), err +} + +func (T *UntypedPacket) Length() uint32 { + return binary.BigEndian.Uint32(T.PacketWriter) +} + +func (T *UntypedPacket) Payload() []byte { + return T.PacketWriter[4:] +} + +func (T *UntypedPacket) Read() UntypedReadablePacket { + return UntypedReadablePacket{ + PacketReader: PacketReader(T.Payload()), + } +} diff --git a/lib/zap3/packets/v3.0/authenticationcleartext.go b/lib/zap3/packets/v3.0/authenticationcleartext.go new file mode 100644 index 0000000000000000000000000000000000000000..594ca6eb4fde79bb389923037a16bb3ce294fa56 --- /dev/null +++ b/lib/zap3/packets/v3.0/authenticationcleartext.go @@ -0,0 +1,24 @@ +package packets + +import ( + "pggat2/lib/zap3" +) + +func ReadAuthenticationCleartext(in *zap3.ReadablePacket) bool { + if in.ReadType() != Authentication { + return false + } + method, ok := in.ReadInt32() + if !ok { + return false + } + if method != 3 { + return false + } + return true +} + +func WriteAuthenticationCleartext(out *zap3.Packet) { + out.WriteType(Authentication) + out.WriteInt32(3) +} diff --git a/lib/zap3/packets/v3.0/authenticationmd5.go b/lib/zap3/packets/v3.0/authenticationmd5.go new file mode 100644 index 0000000000000000000000000000000000000000..4837b36a49ee988e3a4c9dd5fd87be2f3b82172e --- /dev/null +++ b/lib/zap3/packets/v3.0/authenticationmd5.go @@ -0,0 +1,30 @@ +package packets + +import ( + "pggat2/lib/zap3" +) + +func ReadAuthenticationMD5(in *zap3.ReadablePacket) ([4]byte, bool) { + if in.ReadType() != Authentication { + return [4]byte{}, false + } + method, ok := in.ReadInt32() + if !ok { + return [4]byte{}, false + } + if method != 5 { + return [4]byte{}, false + } + var salt [4]byte + ok = in.ReadBytes(salt[:]) + if !ok { + return salt, false + } + return salt, true +} + +func WriteAuthenticationMD5(out *zap3.Packet, salt [4]byte) { + out.WriteType(Authentication) + out.WriteUint32(5) + out.WriteBytes(salt[:]) +} diff --git a/lib/zap3/packets/v3.0/authenticationok.go b/lib/zap3/packets/v3.0/authenticationok.go new file mode 100644 index 0000000000000000000000000000000000000000..ddd35f9f46939bbf86aabea32cabcbfb1d1339a5 --- /dev/null +++ b/lib/zap3/packets/v3.0/authenticationok.go @@ -0,0 +1,24 @@ +package packets + +import ( + "pggat2/lib/zap3" +) + +func ReadAuthenticationOk(in *zap3.ReadablePacket) bool { + if in.ReadType() != Authentication { + return false + } + method, ok := in.ReadInt32() + if !ok { + return false + } + if method != 0 { + return false + } + return true +} + +func WriteAuthenticationOk(out *zap3.Packet) { + out.WriteType(Authentication) + out.WriteInt32(0) +} diff --git a/lib/zap3/packets/v3.0/authenticationresponse.go b/lib/zap3/packets/v3.0/authenticationresponse.go new file mode 100644 index 0000000000000000000000000000000000000000..eec9dc69d5e9ab2990dbafd78aadba07afb98d52 --- /dev/null +++ b/lib/zap3/packets/v3.0/authenticationresponse.go @@ -0,0 +1,17 @@ +package packets + +import "pggat2/lib/zap" + +func ReadAuthenticationResponse(in zap.Inspector) ([]byte, bool) { + in.Reset() + if in.Type() != AuthenticationResponse { + return nil, false + } + return in.Remaining(), true +} + +func WriteAuthenticationResponse(out zap.Builder, resp []byte) { + out.Reset() + out.Type(AuthenticationResponse) + out.Bytes(resp) +} diff --git a/lib/zap3/packets/v3.0/authenticationsasl.go b/lib/zap3/packets/v3.0/authenticationsasl.go new file mode 100644 index 0000000000000000000000000000000000000000..dd9bc38e6b37b4b33a98978bdabe45d75a9b7dbd --- /dev/null +++ b/lib/zap3/packets/v3.0/authenticationsasl.go @@ -0,0 +1,55 @@ +package packets + +import "pggat2/lib/zap" + +func ReadAuthenticationSASL(in zap.Inspector) ([]string, bool) { + in.Reset() + if in.Type() != Authentication { + return nil, false + } + + method, ok := in.Int32() + if !ok { + return nil, false + } + + if method != 10 { + return nil, false + } + + // get count first to prevent reallocating the slice a bunch + var mechanismCount int + for { + mechanism, ok := in.String() + if !ok { + return nil, false + } + if mechanism == "" { + break + } + mechanismCount++ + } + + mechanisms := make([]string, 0, mechanismCount) + in.Reset() + in.Int32() + for i := 0; i < mechanismCount; i++ { + mechanism, ok := in.String() + if !ok { + return nil, false + } + mechanisms = append(mechanisms, mechanism) + } + + return mechanisms, true +} + +func WriteAuthenticationSASL(out zap.Builder, mechanisms []string) { + out.Reset() + out.Type(Authentication) + out.Int32(10) + for _, mechanism := range mechanisms { + out.String(mechanism) + } + out.Uint8(0) +} diff --git a/lib/zap3/packets/v3.0/authenticationsaslcontinue.go b/lib/zap3/packets/v3.0/authenticationsaslcontinue.go new file mode 100644 index 0000000000000000000000000000000000000000..cf7f18ff42f1125f9d4e3a7d92137fa506370276 --- /dev/null +++ b/lib/zap3/packets/v3.0/authenticationsaslcontinue.go @@ -0,0 +1,27 @@ +package packets + +import ( + "pggat2/lib/zap" +) + +func ReadAuthenticationSASLContinue(in zap.Inspector) ([]byte, bool) { + in.Reset() + if in.Type() != Authentication { + return nil, false + } + method, ok := in.Int32() + if !ok { + return nil, false + } + if method != 11 { + return nil, false + } + return in.Remaining(), true +} + +func WriteAuthenticationSASLContinue(out zap.Builder, resp []byte) { + out.Reset() + out.Type(Authentication) + out.Int32(11) + out.Bytes(resp) +} diff --git a/lib/zap3/packets/v3.0/authenticationsaslfinal.go b/lib/zap3/packets/v3.0/authenticationsaslfinal.go new file mode 100644 index 0000000000000000000000000000000000000000..b29ae94efd13ff8b0e3d01ce54ee487009e34ebb --- /dev/null +++ b/lib/zap3/packets/v3.0/authenticationsaslfinal.go @@ -0,0 +1,25 @@ +package packets + +import "pggat2/lib/zap" + +func ReadAuthenticationSASLFinal(in zap.Inspector) ([]byte, bool) { + in.Reset() + if in.Type() != Authentication { + return nil, false + } + method, ok := in.Int32() + if !ok { + return nil, false + } + if method != 12 { + return nil, false + } + return in.Remaining(), true +} + +func WriteAuthenticationSASLFinal(out zap.Builder, resp []byte) { + out.Reset() + out.Type(Authentication) + out.Int32(12) + out.Bytes(resp) +} diff --git a/lib/zap3/packets/v3.0/backendkeydata.go b/lib/zap3/packets/v3.0/backendkeydata.go new file mode 100644 index 0000000000000000000000000000000000000000..74040a54ce33a3eceb4331ba839cb81da2d144b5 --- /dev/null +++ b/lib/zap3/packets/v3.0/backendkeydata.go @@ -0,0 +1,22 @@ +package packets + +import "pggat2/lib/zap" + +func ReadBackendKeyData(in zap.Inspector) ([8]byte, bool) { + in.Reset() + if in.Type() != BackendKeyData { + return [8]byte{}, false + } + var cancellationKey [8]byte + ok := in.Bytes(cancellationKey[:]) + if !ok { + return cancellationKey, false + } + return cancellationKey, true +} + +func WriteBackendKeyData(out zap.Builder, cancellationKey [8]byte) { + out.Reset() + out.Type(BackendKeyData) + out.Bytes(cancellationKey[:]) +} diff --git a/lib/zap3/packets/v3.0/bind.go b/lib/zap3/packets/v3.0/bind.go new file mode 100644 index 0000000000000000000000000000000000000000..9691883930d7f2da0d67986cdae80bc471a5e43d --- /dev/null +++ b/lib/zap3/packets/v3.0/bind.go @@ -0,0 +1,95 @@ +package packets + +import ( + "pggat2/lib/zap" +) + +func ReadBind(in zap.Inspector) (destination string, source string, parameterFormatCodes []int16, parameterValues [][]byte, resultFormatCodes []int16, ok bool) { + in.Reset() + if in.Type() != Bind { + return + } + destination, ok = in.String() + if !ok { + return + } + source, ok = in.String() + if !ok { + return + } + var parameterFormatCodesLength uint16 + parameterFormatCodesLength, ok = in.Uint16() + if !ok { + return + } + parameterFormatCodes = make([]int16, 0, int(parameterFormatCodesLength)) + for i := 0; i < int(parameterFormatCodesLength); i++ { + var parameterFormatCode int16 + parameterFormatCode, ok = in.Int16() + if !ok { + return + } + parameterFormatCodes = append(parameterFormatCodes, parameterFormatCode) + } + var parameterValuesLength uint16 + parameterValuesLength, ok = in.Uint16() + if !ok { + return + } + parameterValues = make([][]byte, 0, int(parameterValuesLength)) + for i := 0; i < int(parameterValuesLength); i++ { + var parameterValueLength int32 + parameterValueLength, ok = in.Int32() + if !ok { + return + } + var parameterValue []byte + if parameterValueLength >= 0 { + parameterValue = make([]byte, int(parameterValueLength)) + ok = in.Bytes(parameterValue) + if !ok { + return + } + } + parameterValues = append(parameterValues, parameterValue) + } + var resultFormatCodesLength uint16 + resultFormatCodesLength, ok = in.Uint16() + if !ok { + return + } + resultFormatCodes = make([]int16, 0, int(resultFormatCodesLength)) + for i := 0; i < int(resultFormatCodesLength); i++ { + var resultFormatCode int16 + resultFormatCode, ok = in.Int16() + if !ok { + return + } + resultFormatCodes = append(resultFormatCodes, resultFormatCode) + } + return +} + +func WriteBind(out zap.Builder, destination, source string, parameterFormatCodes []int16, parameterValues [][]byte, resultFormatCodes []int16) { + out.Reset() + out.Type(Bind) + out.String(destination) + out.String(source) + out.Uint16(uint16(len(parameterFormatCodes))) + for _, v := range parameterFormatCodes { + out.Int16(v) + } + out.Uint16(uint16(len(parameterValues))) + for _, v := range parameterValues { + if v == nil { + out.Int32(-1) + continue + } + out.Int32(int32(len(v))) + out.Bytes(v) + } + out.Uint16(uint16(len(resultFormatCodes))) + for _, v := range resultFormatCodes { + out.Int16(v) + } +} diff --git a/lib/zap3/packets/v3.0/close.go b/lib/zap3/packets/v3.0/close.go new file mode 100644 index 0000000000000000000000000000000000000000..e382f3d3b65f8713910671e3b3eb0cbe2481d5ee --- /dev/null +++ b/lib/zap3/packets/v3.0/close.go @@ -0,0 +1,26 @@ +package packets + +import "pggat2/lib/zap" + +func ReadClose(in zap.Inspector) (which uint8, target string, ok bool) { + in.Reset() + if in.Type() != Close { + return + } + which, ok = in.Uint8() + if !ok { + return + } + target, ok = in.String() + if !ok { + return + } + return +} + +func WriteClose(out zap.Builder, which uint8, target string) { + out.Reset() + out.Type(Close) + out.Uint8(which) + out.String(target) +} diff --git a/lib/zap3/packets/v3.0/describe.go b/lib/zap3/packets/v3.0/describe.go new file mode 100644 index 0000000000000000000000000000000000000000..a5b52fff301e9ab392a61d7c94ed8957defd79df --- /dev/null +++ b/lib/zap3/packets/v3.0/describe.go @@ -0,0 +1,28 @@ +package packets + +import ( + "pggat2/lib/zap" +) + +func ReadDescribe(in zap.Inspector) (which uint8, target string, ok bool) { + in.Reset() + if in.Type() != Describe { + return + } + which, ok = in.Uint8() + if !ok { + return + } + target, ok = in.String() + if !ok { + return + } + return +} + +func WriteDescribe(out zap.Builder, which uint8, target string) { + out.Reset() + out.Type(Describe) + out.Uint8(which) + out.String(target) +} diff --git a/lib/zap3/packets/v3.0/errorresponse.go b/lib/zap3/packets/v3.0/errorresponse.go new file mode 100644 index 0000000000000000000000000000000000000000..d6f36cd59e88479df8888188f9e8d353591c65e3 --- /dev/null +++ b/lib/zap3/packets/v3.0/errorresponse.go @@ -0,0 +1,76 @@ +package packets + +import ( + "pggat2/lib/perror" + "pggat2/lib/zap" +) + +func ReadErrorResponse(in zap.Inspector) (perror.Error, bool) { + in.Reset() + if in.Type() != ErrorResponse { + return nil, false + } + + var severity perror.Severity + var code perror.Code + var message string + var extra []perror.ExtraField + + for { + typ, ok := in.Uint8() + if !ok { + return nil, false + } + + if typ == 0 { + break + } + + value, ok := in.String() + if !ok { + return nil, false + } + + switch typ { + case 'S': + severity = perror.Severity(value) + case 'C': + code = perror.Code(value) + case 'M': + message = value + default: + extra = append(extra, perror.ExtraField{ + Type: perror.Extra(typ), + Value: value, + }) + } + } + + return perror.New( + severity, + code, + message, + extra..., + ), true +} + +func WriteErrorResponse(out zap.Builder, err perror.Error) { + out.Reset() + out.Type(ErrorResponse) + + out.Uint8('S') + out.String(string(err.Severity())) + + out.Uint8('C') + out.String(string(err.Code())) + + out.Uint8('M') + out.String(err.Message()) + + for _, field := range err.Extra() { + out.Uint8(uint8(field.Type)) + out.String(field.Value) + } + + out.Uint8(0) +} diff --git a/lib/zap3/packets/v3.0/errors.go b/lib/zap3/packets/v3.0/errors.go new file mode 100644 index 0000000000000000000000000000000000000000..7fe488210c45ea31a7423b59c7de9da07c77515e --- /dev/null +++ b/lib/zap3/packets/v3.0/errors.go @@ -0,0 +1,16 @@ +package packets + +import "pggat2/lib/perror" + +var ( + ErrBadFormat = perror.New( + perror.FATAL, + perror.ProtocolViolation, + "Bad packet format", + ) + ErrUnexpectedPacket = perror.New( + perror.FATAL, + perror.ProtocolViolation, + "unexpected packet", + ) +) diff --git a/lib/zap3/packets/v3.0/execute.go b/lib/zap3/packets/v3.0/execute.go new file mode 100644 index 0000000000000000000000000000000000000000..7c32249a94cea7809e89b8e7223e3cbeb9e2a479 --- /dev/null +++ b/lib/zap3/packets/v3.0/execute.go @@ -0,0 +1,28 @@ +package packets + +import ( + "pggat2/lib/zap" +) + +func ReadExecute(in zap.Inspector) (target string, maxRows int32, ok bool) { + in.Reset() + if in.Type() != Execute { + return + } + target, ok = in.String() + if !ok { + return + } + maxRows, ok = in.Int32() + if !ok { + return + } + return +} + +func WriteExecute(out zap.Builder, target string, maxRows int32) { + out.Reset() + out.Type(Execute) + out.String(target) + out.Int32(maxRows) +} diff --git a/lib/zap3/packets/v3.0/negotiateprotocolversion.go b/lib/zap3/packets/v3.0/negotiateprotocolversion.go new file mode 100644 index 0000000000000000000000000000000000000000..73595713bb14c2490ccbc3559bc5245f70ca37a6 --- /dev/null +++ b/lib/zap3/packets/v3.0/negotiateprotocolversion.go @@ -0,0 +1,42 @@ +package packets + +import ( + "pggat2/lib/zap" +) + +func ReadNegotiateProtocolVersion(in zap.Inspector) (minorProtocolVersion int32, unrecognizedOptions []string, ok bool) { + in.Reset() + if in.Type() != NegotiateProtocolVersion { + return + } + minorProtocolVersion, ok = in.Int32() + if !ok { + return + } + var numUnrecognizedOptions int32 + numUnrecognizedOptions, ok = in.Int32() + if !ok { + return + } + unrecognizedOptions = make([]string, 0, numUnrecognizedOptions) + for i := 0; i < int(numUnrecognizedOptions); i++ { + var unrecognizedOption string + unrecognizedOption, ok = in.String() + if !ok { + return + } + unrecognizedOptions = append(unrecognizedOptions, unrecognizedOption) + } + ok = true + return +} + +func WriteNegotiateProtocolVersion(out zap.Builder, minorProtocolVersion int32, unrecognizedOptions []string) { + out.Reset() + out.Type(NegotiateProtocolVersion) + out.Int32(minorProtocolVersion) + out.Int32(int32(len(unrecognizedOptions))) + for _, option := range unrecognizedOptions { + out.String(option) + } +} diff --git a/lib/zap3/packets/v3.0/parameterstatus.go b/lib/zap3/packets/v3.0/parameterstatus.go new file mode 100644 index 0000000000000000000000000000000000000000..cff7246aa0c17a2a82ce6a401e12276285e246ef --- /dev/null +++ b/lib/zap3/packets/v3.0/parameterstatus.go @@ -0,0 +1,26 @@ +package packets + +import "pggat2/lib/zap" + +func ReadParameterStatus(in zap.Inspector) (key, value string, ok bool) { + in.Reset() + if in.Type() != ParameterStatus { + return + } + key, ok = in.String() + if !ok { + return + } + value, ok = in.String() + if !ok { + return + } + return +} + +func WriteParameterStatus(out zap.Builder, key, value string) { + out.Reset() + out.Type(ParameterStatus) + out.String(key) + out.String(value) +} diff --git a/lib/zap3/packets/v3.0/parse.go b/lib/zap3/packets/v3.0/parse.go new file mode 100644 index 0000000000000000000000000000000000000000..3d7c3f31be570c76bd26482a20cc7305014ccfd3 --- /dev/null +++ b/lib/zap3/packets/v3.0/parse.go @@ -0,0 +1,46 @@ +package packets + +import ( + "pggat2/lib/zap" +) + +func ReadParse(in zap.Inspector) (destination string, query string, parameterDataTypes []int32, ok bool) { + in.Reset() + if in.Type() != Parse { + return + } + destination, ok = in.String() + if !ok { + return + } + query, ok = in.String() + if !ok { + return + } + var parameterDataTypesCount int16 + parameterDataTypesCount, ok = in.Int16() + if !ok { + return + } + parameterDataTypes = make([]int32, 0, int(parameterDataTypesCount)) + for i := 0; i < int(parameterDataTypesCount); i++ { + var parameterDataType int32 + parameterDataType, ok = in.Int32() + if !ok { + return + } + parameterDataTypes = append(parameterDataTypes, parameterDataType) + } + return +} + +func WriteParse(out zap.Builder, destination string, query string, parameterDataTypes []int32) { + out.Reset() + out.Type(Parse) + out.String(destination) + out.String(query) + out.Int16(int16(len(parameterDataTypes))) + for _, v := range parameterDataTypes { + out.Int32(v) + } +} diff --git a/lib/zap3/packets/v3.0/passwordmessage.go b/lib/zap3/packets/v3.0/passwordmessage.go new file mode 100644 index 0000000000000000000000000000000000000000..f777e94c95118f457b1e70ec426f00e8673ba746 --- /dev/null +++ b/lib/zap3/packets/v3.0/passwordmessage.go @@ -0,0 +1,23 @@ +package packets + +import ( + "pggat2/lib/zap" +) + +func ReadPasswordMessage(in zap.Inspector) (string, bool) { + in.Reset() + if in.Type() != AuthenticationResponse { + return "", false + } + password, ok := in.String() + if !ok { + return "", false + } + return password, true +} + +func WritePasswordMessage(out zap.Builder, password string) { + out.Reset() + out.Type(AuthenticationResponse) + out.String(password) +} diff --git a/lib/zap3/packets/v3.0/readyforquery.go b/lib/zap3/packets/v3.0/readyforquery.go new file mode 100644 index 0000000000000000000000000000000000000000..f023ceb06cc5de23a20eee25300573e0a6d299b5 --- /dev/null +++ b/lib/zap3/packets/v3.0/readyforquery.go @@ -0,0 +1,23 @@ +package packets + +import ( + "pggat2/lib/zap" +) + +func ReadReadyForQuery(in zap.Inspector) (byte, bool) { + in.Reset() + if in.Type() != ReadyForQuery { + return 0, false + } + state, ok := in.Uint8() + if !ok { + return 0, false + } + return state, true +} + +func WriteReadyForQuery(out zap.Builder, state uint8) { + out.Reset() + out.Type(ReadyForQuery) + out.Uint8(state) +} diff --git a/lib/zap3/packets/v3.0/saslinitialresponse.go b/lib/zap3/packets/v3.0/saslinitialresponse.go new file mode 100644 index 0000000000000000000000000000000000000000..6cbcd94b694e7fcc5a74c19c931b8be81629a823 --- /dev/null +++ b/lib/zap3/packets/v3.0/saslinitialresponse.go @@ -0,0 +1,41 @@ +package packets + +import ( + "pggat2/lib/zap" +) + +func ReadSASLInitialResponse(in zap.Inspector) (mechanism string, initialResponse []byte, ok bool) { + in.Reset() + if in.Type() != AuthenticationResponse { + return + } + + mechanism, ok = in.String() + if !ok { + return + } + + var initialResponseSize int32 + initialResponseSize, ok = in.Int32() + if !ok { + return + } + if initialResponseSize == -1 { + return + } + + initialResponse, ok = in.UnsafeBytes(int(initialResponseSize)) + return +} + +func WriteSASLInitialResponse(out zap.Builder, mechanism string, initialResponse []byte) { + out.Reset() + out.Type(AuthenticationResponse) + out.String(mechanism) + if initialResponse == nil { + out.Int32(-1) + } else { + out.Int32(int32(len(initialResponse))) + out.Bytes(initialResponse) + } +} diff --git a/lib/zap3/packets/v3.0/types.go b/lib/zap3/packets/v3.0/types.go new file mode 100644 index 0000000000000000000000000000000000000000..68b3740658189194077803a62ce5e3d9e9d545a5 --- /dev/null +++ b/lib/zap3/packets/v3.0/types.go @@ -0,0 +1,43 @@ +package packets + +import "pggat2/lib/zap" + +const ( + None zap.Type = 0 + Authentication zap.Type = 'R' + BackendKeyData zap.Type = 'K' + Bind zap.Type = 'B' + BindComplete zap.Type = '2' + Close zap.Type = 'C' + CloseComplete zap.Type = '3' + CommandComplete zap.Type = 'C' + CopyData zap.Type = 'd' + CopyDone zap.Type = 'c' + CopyFail zap.Type = 'f' + CopyInResponse zap.Type = 'G' + CopyOutResponse zap.Type = 'H' + CopyBothResponse zap.Type = 'W' + DataRow zap.Type = 'D' + Describe zap.Type = 'D' + EmptyQueryResponse zap.Type = 'I' + ErrorResponse zap.Type = 'E' + Execute zap.Type = 'E' + Flush zap.Type = 'H' + FunctionCall zap.Type = 'F' + FunctionCallResponse zap.Type = 'V' + AuthenticationResponse zap.Type = 'p' + NegotiateProtocolVersion zap.Type = 'v' + NoData zap.Type = 'n' + NoticeResponse zap.Type = 'N' + NotificationResponse zap.Type = 'A' + ParameterDescription zap.Type = 't' + ParameterStatus zap.Type = 'S' + Parse zap.Type = 'P' + ParseComplete zap.Type = '1' + PortalSuspended zap.Type = 's' + Query zap.Type = 'Q' + ReadyForQuery zap.Type = 'Z' + RowDescription zap.Type = 'T' + Sync zap.Type = 'S' + Terminate zap.Type = 'X' +) diff --git a/lib/zap3/reader.go b/lib/zap3/reader.go new file mode 100644 index 0000000000000000000000000000000000000000..3ea1005b737d6994c39eb48ca87368fe62a4df12 --- /dev/null +++ b/lib/zap3/reader.go @@ -0,0 +1,24 @@ +package zap3 + +import "io" + +type Reader interface { + Read(*Packet) error + ReadUntyped(*UntypedPacket) error +} + +type IOReader struct { + Reader io.Reader +} + +func (T IOReader) Read(packet *Packet) error { + _, err := packet.ReadFrom(T.Reader) + return err +} + +func (T IOReader) ReadUntyped(packet *UntypedPacket) error { + _, err := packet.ReadFrom(T.Reader) + return err +} + +var _ Reader = IOReader{} diff --git a/lib/zap3/type.go b/lib/zap3/type.go new file mode 100644 index 0000000000000000000000000000000000000000..b1abf1fbf98917212e0fdf6f95ecb84c93d5e03a --- /dev/null +++ b/lib/zap3/type.go @@ -0,0 +1,3 @@ +package zap3 + +type Type int diff --git a/lib/zap3/writer.go b/lib/zap3/writer.go new file mode 100644 index 0000000000000000000000000000000000000000..40fc38bfab22a391c1cecc0390e384ccf0b0b013 --- /dev/null +++ b/lib/zap3/writer.go @@ -0,0 +1,30 @@ +package zap3 + +import "io" + +type Writer interface { + Write(*Packet) error + WriteUntyped(*UntypedPacket) error + WriteV(*Packets) error +} + +type IOWriter struct { + Writer io.Writer +} + +func (T IOWriter) Write(packet *Packet) error { + _, err := packet.WriteTo(T.Writer) + return err +} + +func (T IOWriter) WriteUntyped(packet *UntypedPacket) error { + _, err := packet.WriteTo(T.Writer) + return err +} + +func (T IOWriter) WriteV(packets *Packets) error { + _, err := packets.WriteTo(T.Writer) + return err +} + +var _ Writer = IOWriter{}