diff options
| author | 2026-03-25 16:22:03 +0000 | |
|---|---|---|
| committer | 2026-03-25 16:22:03 +0000 | |
| commit | 311edcd50f3a84f4b860bde3cb887451d74eaa11 (patch) | |
| tree | be7aa5e9a51e636358f33b1c90637b5024b70dc3 /network/protocol/pktline/encoder.go | |
| parent | README: Split off contrib, benchmarks, remove history for now I guess, etc. (diff) | |
| signature | No signature | |
network/protocol: Rename from protocol v0.1.110
Diffstat (limited to 'network/protocol/pktline/encoder.go')
| -rw-r--r-- | network/protocol/pktline/encoder.go | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/network/protocol/pktline/encoder.go b/network/protocol/pktline/encoder.go new file mode 100644 index 00000000..b4c6dbf0 --- /dev/null +++ b/network/protocol/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 +} |
