From 3307715a8d8bdeac1b2d7df66ec2abb6e503ba9a Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Fri, 6 Mar 2026 13:01:12 +0800 Subject: format/pktline: Add pktline --- format/pktline/encoder.go | 145 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 format/pktline/encoder.go (limited to 'format/pktline/encoder.go') diff --git a/format/pktline/encoder.go b/format/pktline/encoder.go new file mode 100644 index 00000000..b4c6dbf0 --- /dev/null +++ b/format/pktline/encoder.go @@ -0,0 +1,145 @@ +package pktline + +import ( + "fmt" + "io" +) + +// WriteFlusher is the output transport contract required by Encoder. +// +// Write emits framed bytes and Flush pushes buffered transport state. +type WriteFlusher interface { + io.Writer + Flush() error +} + +// Encoder writes pkt-line frames to a flush-capable output transport. +// +// It writes exactly one frame per method call and does not auto-chunk data. +type Encoder struct { + w WriteFlusher + maxData int +} + +// NewEncoder creates an encoder over w. +func NewEncoder(w WriteFlusher) *Encoder { + return &Encoder{ + w: w, + maxData: LargePacketDataMax, + } +} + +// SetMaxData sets the maximum payload size accepted by WriteData. +// +// Non-positive n resets to LargePacketDataMax. +func (e *Encoder) SetMaxData(n int) { + if n <= 0 { + e.maxData = LargePacketDataMax + + return + } + + e.maxData = n +} + +func writeAll(w io.Writer, b []byte) error { + for len(b) > 0 { + n, err := w.Write(b) + if err != nil { + return err + } + + if n <= 0 { + return io.ErrShortWrite + } + + b = b[n:] + } + + return nil +} + +// WriteData writes one data frame. +// +// Empty payload is encoded as 0004. +func (e *Encoder) WriteData(p []byte) error { + maxData := e.effectiveMaxData() + if len(p) > maxData { + return fmt.Errorf("%w: %d > %d", ErrTooLarge, len(p), maxData) + } + + var hdr [4]byte + + err := EncodeLengthHeader(&hdr, len(p)+4) + if err != nil { + return err + } + + err = writeAll(e.w, hdr[:]) + if err != nil { + return err + } + + return writeAll(e.w, p) +} + +// WriteString writes one data frame containing s and returns len(s) on success. +func (e *Encoder) WriteString(s string) (int, error) { + err := e.WriteData([]byte(s)) + if err != nil { + return 0, err + } + + return len(s), nil +} + +// WriteFlush writes control frame 0000 (flush-pkt). +func (e *Encoder) WriteFlush() error { + return e.writeControl(0) +} + +// WriteDelim writes control frame 0001 (delim-pkt). +func (e *Encoder) WriteDelim() error { + return e.writeControl(1) +} + +// WriteResponseEnd writes control frame 0002 (response-end-pkt). +func (e *Encoder) WriteResponseEnd() error { + return e.writeControl(2) +} + +// FlushIO flushes buffered output in the underlying transport. +// +// FlushIO does not emit any pkt-line control frame. +func (e *Encoder) FlushIO() error { + return e.w.Flush() +} + +// WriteFlushAndFlushIO writes a flush-pkt (0000) then flushes transport I/O. +func (e *Encoder) WriteFlushAndFlushIO() error { + err := e.WriteFlush() + if err != nil { + return err + } + + return e.FlushIO() +} + +func (e *Encoder) writeControl(n int) error { + var hdr [4]byte + + err := EncodeLengthHeader(&hdr, n) + if err != nil { + return err + } + + return writeAll(e.w, hdr[:]) +} + +func (e *Encoder) effectiveMaxData() int { + if e.maxData <= 0 || e.maxData > LargePacketDataMax { + return LargePacketDataMax + } + + return e.maxData +} -- cgit v1.3.1-10-gc9f91