aboutsummaryrefslogtreecommitdiff
path: root/protocol/pktline
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-25 16:22:03 +0000
committerGravatar Runxi Yu2026-03-25 16:22:03 +0000
commit311edcd50f3a84f4b860bde3cb887451d74eaa11 (patch)
treebe7aa5e9a51e636358f33b1c90637b5024b70dc3 /protocol/pktline
parentREADME: Split off contrib, benchmarks, remove history for now I guess, etc. (diff)
signatureNo signature
network/protocol: Rename from protocol v0.1.110
Diffstat (limited to 'protocol/pktline')
-rw-r--r--protocol/pktline/append.go39
-rw-r--r--protocol/pktline/append_data_preserves_dst_on_error_test.go25
-rw-r--r--protocol/pktline/append_helpers_test.go24
-rw-r--r--protocol/pktline/chunk_writer.go65
-rw-r--r--protocol/pktline/chunk_writer_write_and_read_from_test.go60
-rw-r--r--protocol/pktline/constants.go12
-rw-r--r--protocol/pktline/decoder.go187
-rw-r--r--protocol/pktline/decoder_data_control_and_0004_test.go60
-rw-r--r--protocol/pktline/decoder_invalid_0003_test.go20
-rw-r--r--protocol/pktline/decoder_peek_test.go32
-rw-r--r--protocol/pktline/decoder_rejects_over_maximum_length_test.go22
-rw-r--r--protocol/pktline/decoder_resync_after_over_max_data_test.go51
-rw-r--r--protocol/pktline/decoder_resync_after_over_wire_max_test.go37
-rw-r--r--protocol/pktline/decoder_unexpected_eof_test.go21
-rw-r--r--protocol/pktline/doc.go2
-rw-r--r--protocol/pktline/encode_length_header_test.go28
-rw-r--r--protocol/pktline/encoder.go145
-rw-r--r--protocol/pktline/encoder_buffered_flush_and_f_flush_test.go50
-rw-r--r--protocol/pktline/encoder_buffered_flush_behavior_test.go86
-rw-r--r--protocol/pktline/encoder_set_max_data_cannot_exceed_wire_limit_test.go26
-rw-r--r--protocol/pktline/encoder_writes_frames_test.go51
-rw-r--r--protocol/pktline/errors.go31
-rw-r--r--protocol/pktline/frame.go10
-rw-r--r--protocol/pktline/header.go57
-rw-r--r--protocol/pktline/parse_length_header_test.go26
-rw-r--r--protocol/pktline/type.go15
26 files changed, 0 insertions, 1182 deletions
diff --git a/protocol/pktline/append.go b/protocol/pktline/append.go
deleted file mode 100644
index 9425e58e..00000000
--- a/protocol/pktline/append.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package pktline
-
-import "fmt"
-
-// AppendData appends one data frame to dst.
-//
-// Empty payload is encoded as 0004.
-func AppendData(dst, payload []byte) ([]byte, error) {
- if len(payload) > LargePacketDataMax {
- return dst, fmt.Errorf("%w: %d > %d", ErrTooLarge, len(payload), LargePacketDataMax)
- }
-
- var hdr [4]byte
-
- err := EncodeLengthHeader(&hdr, len(payload)+4)
- if err != nil {
- return dst, err
- }
-
- dst = append(dst, hdr[:]...)
- dst = append(dst, payload...)
-
- return dst, nil
-}
-
-// AppendFlushPkt appends control frame 0000 (flush-pkt).
-func AppendFlushPkt(dst []byte) []byte {
- return append(dst, '0', '0', '0', '0')
-}
-
-// AppendDelimPkt appends control frame 0001 (delim-pkt).
-func AppendDelimPkt(dst []byte) []byte {
- return append(dst, '0', '0', '0', '1')
-}
-
-// AppendResponseEndPkt appends control frame 0002 (response-end-pkt).
-func AppendResponseEndPkt(dst []byte) []byte {
- return append(dst, '0', '0', '0', '2')
-}
diff --git a/protocol/pktline/append_data_preserves_dst_on_error_test.go b/protocol/pktline/append_data_preserves_dst_on_error_test.go
deleted file mode 100644
index 2888be16..00000000
--- a/protocol/pktline/append_data_preserves_dst_on_error_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package pktline_test
-
-import (
- "bytes"
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestAppendDataPreservesDstOnError(t *testing.T) {
- t.Parallel()
-
- orig := []byte("seed")
- dst := append([]byte(nil), orig...)
-
- out, err := pktline.AppendData(dst, bytes.Repeat([]byte{'x'}, pktline.LargePacketDataMax+1))
- if !errors.Is(err, pktline.ErrTooLarge) {
- t.Fatalf("got err %v, want ErrTooLarge", err)
- }
-
- if !bytes.Equal(out, orig) {
- t.Fatalf("got %q, want %q", string(out), string(orig))
- }
-}
diff --git a/protocol/pktline/append_helpers_test.go b/protocol/pktline/append_helpers_test.go
deleted file mode 100644
index ae5f4748..00000000
--- a/protocol/pktline/append_helpers_test.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package pktline_test
-
-import (
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestAppendHelpers(t *testing.T) {
- t.Parallel()
-
- out, err := pktline.AppendData(nil, []byte("ok"))
- if err != nil {
- t.Fatalf("AppendData: %v", err)
- }
-
- out = pktline.AppendFlushPkt(out)
- out = pktline.AppendDelimPkt(out)
- out = pktline.AppendResponseEndPkt(out)
-
- if got, want := string(out), "0006ok000000010002"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-}
diff --git a/protocol/pktline/chunk_writer.go b/protocol/pktline/chunk_writer.go
deleted file mode 100644
index b258ff20..00000000
--- a/protocol/pktline/chunk_writer.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package pktline
-
-import "io"
-
-// ChunkWriter packetizes arbitrary stream bytes into data pkt-lines.
-// It never writes control packets automatically.
-type ChunkWriter struct {
- enc *Encoder
-}
-
-// NewChunkWriter creates a chunking adapter over enc.
-func NewChunkWriter(enc *Encoder) *ChunkWriter {
- return &ChunkWriter{enc: enc}
-}
-
-// Write splits p into data frames not larger than enc's maxData.
-//
-// It implements io.Writer.
-func (cw *ChunkWriter) Write(p []byte) (int, error) {
- total := 0
- maxData := cw.enc.effectiveMaxData()
-
- for len(p) > 0 {
- n := min(len(p), maxData)
-
- err := cw.enc.WriteData(p[:n])
- if err != nil {
- return total, err
- }
-
- total += n
- p = p[n:]
- }
-
- return total, nil
-}
-
-// ReadFrom reads from r and writes pkt-line data frames to the encoder.
-//
-// It implements io.ReaderFrom.
-func (cw *ChunkWriter) ReadFrom(r io.Reader) (int64, error) {
- buf := make([]byte, cw.enc.effectiveMaxData())
-
- var total int64
-
- for {
- n, err := r.Read(buf)
- if n > 0 {
- werr := cw.enc.WriteData(buf[:n])
- if werr != nil {
- return total, werr
- }
-
- total += int64(n)
- }
-
- if err != nil {
- if err == io.EOF {
- return total, nil
- }
-
- return total, err
- }
- }
-}
diff --git a/protocol/pktline/chunk_writer_write_and_read_from_test.go b/protocol/pktline/chunk_writer_write_and_read_from_test.go
deleted file mode 100644
index f34aa754..00000000
--- a/protocol/pktline/chunk_writer_write_and_read_from_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package pktline_test
-
-import (
- "bufio"
- "bytes"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestChunkWriterWriteAndReadFrom(t *testing.T) {
- t.Parallel()
-
- var out bytes.Buffer
-
- bw := bufio.NewWriter(&out)
-
- enc := pktline.NewEncoder(bw)
- enc.SetMaxData(3)
- cw := pktline.NewChunkWriter(enc)
-
- n, err := cw.Write([]byte("abcdefg"))
- if err != nil {
- t.Fatalf("Write: %v", err)
- }
-
- if n != 7 {
- t.Fatalf("Write n=%d, want 7", n)
- }
-
- err = enc.FlushIO()
- if err != nil {
- t.Fatalf("FlushIO: %v", err)
- }
-
- if got, want := out.String(), "0007abc0007def0005g"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-
- out.Reset()
-
- rn, err := cw.ReadFrom(strings.NewReader("wxyz"))
- if err != nil {
- t.Fatalf("ReadFrom: %v", err)
- }
-
- if rn != 4 {
- t.Fatalf("ReadFrom n=%d, want 4", rn)
- }
-
- err = enc.FlushIO()
- if err != nil {
- t.Fatalf("FlushIO: %v", err)
- }
-
- if got, want := out.String(), "0007wxy0005z"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-}
diff --git a/protocol/pktline/constants.go b/protocol/pktline/constants.go
deleted file mode 100644
index 811eb3c6..00000000
--- a/protocol/pktline/constants.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package pktline
-
-const (
- // DefaultPacketMax is a conservative packet size commonly used by
- // line-oriented protocol messages.
- DefaultPacketMax = 1000
- // LargePacketMax is the maximum on-wire packet size including the
- // 4-byte hexadecimal length header.
- LargePacketMax = 65520
- // LargePacketDataMax is the maximum payload size in one packet.
- LargePacketDataMax = LargePacketMax - 4
-)
diff --git a/protocol/pktline/decoder.go b/protocol/pktline/decoder.go
deleted file mode 100644
index 898d8ad6..00000000
--- a/protocol/pktline/decoder.go
+++ /dev/null
@@ -1,187 +0,0 @@
-package pktline
-
-import (
- "errors"
- "fmt"
- "io"
-)
-
-// ReadOptions controls decoding behavior.
-type ReadOptions struct {
- // ChompLF removes one trailing '\n' from PacketData payloads.
- ChompLF bool
-}
-
-// Decoder reads pkt-line frames from an io.Reader.
-//
-// It is advisable to supply a buffered reader.
-//
-// It preserves frame boundaries and supports one-frame lookahead via PeekFrame.
-type Decoder struct {
- r io.Reader
- maxData int
- opts ReadOptions
-
- peeked bool
- peek Frame
- peekErr error
-}
-
-// NewDecoder creates a decoder over r.
-func NewDecoder(r io.Reader, opts ReadOptions) *Decoder {
- return &Decoder{
- r: r,
- maxData: LargePacketDataMax,
- opts: opts,
- }
-}
-
-// SetMaxData sets maximum payload size accepted for one data packet.
-//
-// Non-positive n resets to LargePacketDataMax.
-func (d *Decoder) SetMaxData(n int) {
- if n <= 0 {
- d.maxData = LargePacketDataMax
-
- return
- }
-
- d.maxData = n
-}
-
-func cloneFrame(f Frame) Frame {
- if f.Type != PacketData {
- return Frame{Type: f.Type}
- }
-
- out := Frame{Type: f.Type}
- if f.Payload != nil {
- out.Payload = append([]byte(nil), f.Payload...)
- }
-
- return out
-}
-
-// ReadFrame reads one frame.
-//
-// 0000 is a PacketFlush
-// 0001 is a PacketDelim
-// 0002 is a PacketResponseEnd
-// 0004 is a PacketData with empty payload
-//
-// 0003 and malformed headers return *ProtocolError.
-func (d *Decoder) ReadFrame() (Frame, error) {
- if d.peeked {
- d.peeked = false
-
- return cloneFrame(d.peek), d.peekErr
- }
-
- return d.readFrame()
-}
-
-// PeekFrame returns the next frame without consuming it.
-//
-// A subsequent ReadFrame returns the same frame.
-func (d *Decoder) PeekFrame() (Frame, error) {
- if !d.peeked {
- d.peek, d.peekErr = d.readFrame()
- d.peeked = true
- }
-
- return cloneFrame(d.peek), d.peekErr
-}
-
-func (d *Decoder) readFrame() (Frame, error) {
- var hdr [4]byte
-
- _, err := io.ReadFull(d.r, hdr[:])
- if err != nil {
- if errors.Is(err, io.EOF) {
- return Frame{}, io.EOF
- }
-
- if errors.Is(err, io.ErrUnexpectedEOF) {
- return Frame{}, io.ErrUnexpectedEOF
- }
-
- return Frame{}, err
- }
-
- n, err := ParseLengthHeader(hdr)
- if err != nil {
- return Frame{}, &ProtocolError{Header: hdr, Reason: err.Error()}
- }
-
- switch n {
- case 0:
- return Frame{Type: PacketFlush}, nil
- case 1:
- return Frame{Type: PacketDelim}, nil
- case 2:
- return Frame{Type: PacketResponseEnd}, nil
- case 3:
- return Frame{}, &ProtocolError{Header: hdr, Reason: "invalid pkt-line length 3"}
- }
-
- if n < 4 {
- return Frame{}, &ProtocolError{Header: hdr, Reason: fmt.Sprintf("invalid pkt-line length %d", n)}
- }
-
- if n > LargePacketMax {
- perr := &ProtocolError{Header: hdr, Reason: fmt.Sprintf("pkt-line length %d exceeds max %d", n, LargePacketMax)}
-
- err := d.discardPayload(n - 4)
- if err != nil {
- return Frame{}, errors.Join(perr, err)
- }
-
- return Frame{}, perr
- }
-
- payloadLen := n - 4
- if payloadLen > d.maxData {
- serr := fmt.Errorf("%w: %d > %d", ErrTooLarge, payloadLen, d.maxData)
-
- err := d.discardPayload(payloadLen)
- if err != nil {
- return Frame{}, errors.Join(serr, err)
- }
-
- return Frame{}, serr
- }
-
- payload := make([]byte, payloadLen)
-
- _, err = io.ReadFull(d.r, payload)
- if err != nil {
- if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
- return Frame{}, io.ErrUnexpectedEOF
- }
-
- return Frame{}, err
- }
-
- if d.opts.ChompLF && len(payload) > 0 && payload[len(payload)-1] == '\n' {
- payload = payload[:len(payload)-1]
- }
-
- return Frame{Type: PacketData, Payload: payload}, nil
-}
-
-func (d *Decoder) discardPayload(n int) error {
- if n <= 0 {
- return nil
- }
-
- _, err := io.CopyN(io.Discard, d.r, int64(n))
- if err == nil {
- return nil
- }
-
- if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
- return io.ErrUnexpectedEOF
- }
-
- return err
-}
diff --git a/protocol/pktline/decoder_data_control_and_0004_test.go b/protocol/pktline/decoder_data_control_and_0004_test.go
deleted file mode 100644
index 727e1063..00000000
--- a/protocol/pktline/decoder_data_control_and_0004_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package pktline_test
-
-import (
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestDecoderDataControlAnd0004(t *testing.T) {
- t.Parallel()
-
- input := "0006a\n0004000100020000"
- dec := pktline.NewDecoder(strings.NewReader(input), pktline.ReadOptions{ChompLF: true})
-
- f, err := dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #1: %v", err)
- }
-
- if f.Type != pktline.PacketData || string(f.Payload) != "a" {
- t.Fatalf("frame #1 = %#v", f)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #2: %v", err)
- }
-
- if f.Type != pktline.PacketData || len(f.Payload) != 0 {
- t.Fatalf("frame #2 = %#v, want empty data", f)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #3: %v", err)
- }
-
- if f.Type != pktline.PacketDelim {
- t.Fatalf("frame #3 type = %v, want PacketDelim", f.Type)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #4: %v", err)
- }
-
- if f.Type != pktline.PacketResponseEnd {
- t.Fatalf("frame #4 type = %v, want PacketResponseEnd", f.Type)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #5: %v", err)
- }
-
- if f.Type != pktline.PacketFlush {
- t.Fatalf("frame #5 type = %v, want PacketFlush", f.Type)
- }
-}
diff --git a/protocol/pktline/decoder_invalid_0003_test.go b/protocol/pktline/decoder_invalid_0003_test.go
deleted file mode 100644
index 9414d715..00000000
--- a/protocol/pktline/decoder_invalid_0003_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package pktline_test
-
-import (
- "errors"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestDecoderInvalid0003(t *testing.T) {
- t.Parallel()
-
- dec := pktline.NewDecoder(strings.NewReader("0003"), pktline.ReadOptions{})
- _, err := dec.ReadFrame()
-
- if _, ok := errors.AsType[*pktline.ProtocolError](err); !ok {
- t.Fatalf("got err %v, want ProtocolError", err)
- }
-}
diff --git a/protocol/pktline/decoder_peek_test.go b/protocol/pktline/decoder_peek_test.go
deleted file mode 100644
index 4b2fe2d9..00000000
--- a/protocol/pktline/decoder_peek_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package pktline_test
-
-import (
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestDecoderPeek(t *testing.T) {
- t.Parallel()
-
- dec := pktline.NewDecoder(strings.NewReader("0005x0000"), pktline.ReadOptions{})
-
- f, err := dec.PeekFrame()
- if err != nil {
- t.Fatalf("PeekFrame: %v", err)
- }
-
- if f.Type != pktline.PacketData || string(f.Payload) != "x" {
- t.Fatalf("peek frame = %#v", f)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame: %v", err)
- }
-
- if f.Type != pktline.PacketData || string(f.Payload) != "x" {
- t.Fatalf("read frame = %#v", f)
- }
-}
diff --git a/protocol/pktline/decoder_rejects_over_maximum_length_test.go b/protocol/pktline/decoder_rejects_over_maximum_length_test.go
deleted file mode 100644
index 67dd5053..00000000
--- a/protocol/pktline/decoder_rejects_over_maximum_length_test.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package pktline_test
-
-import (
- "errors"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestDecoderRejectsOverMaximumLength(t *testing.T) {
- t.Parallel()
-
- dec := pktline.NewDecoder(strings.NewReader("fffe"), pktline.ReadOptions{})
- dec.SetMaxData(70000)
-
- _, err := dec.ReadFrame()
-
- if _, ok := errors.AsType[*pktline.ProtocolError](err); !ok {
- t.Fatalf("got err %v, want ProtocolError", err)
- }
-}
diff --git a/protocol/pktline/decoder_resync_after_over_max_data_test.go b/protocol/pktline/decoder_resync_after_over_max_data_test.go
deleted file mode 100644
index 798ca91f..00000000
--- a/protocol/pktline/decoder_resync_after_over_max_data_test.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package pktline_test
-
-import (
- "bufio"
- "bytes"
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestDecoderResyncAfterOverMaxData(t *testing.T) {
- t.Parallel()
-
- var b bytes.Buffer
-
- bw := bufio.NewWriter(&b)
- enc := pktline.NewEncoder(bw)
-
- err := enc.WriteData([]byte("abcd"))
- if err != nil {
- t.Fatalf("WriteData #1: %v", err)
- }
-
- err = enc.WriteData([]byte("z"))
- if err != nil {
- t.Fatalf("WriteData #2: %v", err)
- }
-
- err = enc.FlushIO()
- if err != nil {
- t.Fatalf("FlushIO: %v", err)
- }
-
- dec := pktline.NewDecoder(bytes.NewReader(b.Bytes()), pktline.ReadOptions{})
- dec.SetMaxData(1)
-
- _, err = dec.ReadFrame()
- if !errors.Is(err, pktline.ErrTooLarge) {
- t.Fatalf("got err %v, want ErrTooLarge", err)
- }
-
- f, err := dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #2: %v", err)
- }
-
- if f.Type != pktline.PacketData || string(f.Payload) != "z" {
- t.Fatalf("got frame %#v, want data z", f)
- }
-}
diff --git a/protocol/pktline/decoder_resync_after_over_wire_max_test.go b/protocol/pktline/decoder_resync_after_over_wire_max_test.go
deleted file mode 100644
index 3ba98d62..00000000
--- a/protocol/pktline/decoder_resync_after_over_wire_max_test.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package pktline_test
-
-import (
- "bytes"
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestDecoderResyncAfterOverWireMax(t *testing.T) {
- t.Parallel()
-
- var b bytes.Buffer
-
- _, _ = b.WriteString("ffff")
- _, _ = b.Write(bytes.Repeat([]byte{'a'}, 65531))
- _, _ = b.WriteString("0005z")
-
- dec := pktline.NewDecoder(bytes.NewReader(b.Bytes()), pktline.ReadOptions{})
- dec.SetMaxData(70000)
-
- _, err := dec.ReadFrame()
-
- if _, ok := errors.AsType[*pktline.ProtocolError](err); !ok {
- t.Fatalf("got err %v, want ProtocolError", err)
- }
-
- f, err := dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #2: %v", err)
- }
-
- if f.Type != pktline.PacketData || string(f.Payload) != "z" {
- t.Fatalf("got frame %#v, want data z", f)
- }
-}
diff --git a/protocol/pktline/decoder_unexpected_eof_test.go b/protocol/pktline/decoder_unexpected_eof_test.go
deleted file mode 100644
index b86b5540..00000000
--- a/protocol/pktline/decoder_unexpected_eof_test.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package pktline_test
-
-import (
- "errors"
- "io"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestDecoderUnexpectedEOF(t *testing.T) {
- t.Parallel()
-
- dec := pktline.NewDecoder(strings.NewReader("0006a"), pktline.ReadOptions{})
-
- _, err := dec.ReadFrame()
- if !errors.Is(err, io.ErrUnexpectedEOF) {
- t.Fatalf("got err %v, want io.ErrUnexpectedEOF", err)
- }
-}
diff --git a/protocol/pktline/doc.go b/protocol/pktline/doc.go
deleted file mode 100644
index 3f7cca89..00000000
--- a/protocol/pktline/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package pktline implements the pkt-line format specified in gitprotocol-common(5).
-package pktline
diff --git a/protocol/pktline/encode_length_header_test.go b/protocol/pktline/encode_length_header_test.go
deleted file mode 100644
index 5ad76daf..00000000
--- a/protocol/pktline/encode_length_header_test.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package pktline_test
-
-import (
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestEncodeLengthHeader(t *testing.T) {
- t.Parallel()
-
- var hdr [4]byte
-
- err := pktline.EncodeLengthHeader(&hdr, 4)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
-
- if got := string(hdr[:]); got != "0004" {
- t.Fatalf("got %q, want %q", got, "0004")
- }
-
- err = pktline.EncodeLengthHeader(&hdr, pktline.LargePacketMax+1)
- if !errors.Is(err, pktline.ErrInvalidLength) {
- t.Fatalf("got err %v, want ErrInvalidLength", err)
- }
-}
diff --git a/protocol/pktline/encoder.go b/protocol/pktline/encoder.go
deleted file mode 100644
index b4c6dbf0..00000000
--- a/protocol/pktline/encoder.go
+++ /dev/null
@@ -1,145 +0,0 @@
-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
-}
diff --git a/protocol/pktline/encoder_buffered_flush_and_f_flush_test.go b/protocol/pktline/encoder_buffered_flush_and_f_flush_test.go
deleted file mode 100644
index 395d7310..00000000
--- a/protocol/pktline/encoder_buffered_flush_and_f_flush_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package pktline_test
-
-import (
- "bufio"
- "bytes"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestEncoderBufferedFlushAndFFlush(t *testing.T) {
- t.Parallel()
-
- var out bytes.Buffer
-
- bw := bufio.NewWriter(&out)
- enc := pktline.NewEncoder(bw)
-
- err := enc.WriteData([]byte("x"))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- if out.Len() != 0 {
- t.Fatalf("unexpected immediate output: %q", out.String())
- }
-
- err = enc.FlushIO()
- if err != nil {
- t.Fatalf("FlushIO: %v", err)
- }
-
- if out.String() != "0005x" {
- t.Fatalf("got %q, want %q", out.String(), "0005x")
- }
-
- out.Reset()
- bw = bufio.NewWriter(&out)
-
- enc = pktline.NewEncoder(bw)
-
- err = enc.WriteFlushAndFlushIO()
- if err != nil {
- t.Fatalf("WriteFlushAndFlushIO: %v", err)
- }
-
- if out.String() != "0000" {
- t.Fatalf("got %q, want %q", out.String(), "0000")
- }
-}
diff --git a/protocol/pktline/encoder_buffered_flush_behavior_test.go b/protocol/pktline/encoder_buffered_flush_behavior_test.go
deleted file mode 100644
index 89ae5fcf..00000000
--- a/protocol/pktline/encoder_buffered_flush_behavior_test.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package pktline_test
-
-import (
- "bufio"
- "bytes"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestEncoderBufferedFlushBehavior(t *testing.T) {
- t.Parallel()
-
- var out bytes.Buffer
-
- bw := bufio.NewWriter(&out)
- enc := pktline.NewEncoder(bw)
-
- err := enc.WriteData([]byte("hello"))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- err = enc.WriteFlush()
- if err != nil {
- t.Fatalf("WriteFlush: %v", err)
- }
-
- if out.Len() != 0 {
- t.Fatalf("WriteFlush should not flush I/O, got %q", out.String())
- }
-
- err = enc.FlushIO()
- if err != nil {
- t.Fatalf("FlushIO: %v", err)
- }
-
- if got, want := out.String(), "0009hello0000"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-
- out.Reset()
- bw = bufio.NewWriter(&out)
- enc = pktline.NewEncoder(bw)
-
- err = enc.WriteData([]byte("ok"))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- err = enc.WriteFlush()
- if err != nil {
- t.Fatalf("WriteFlush: %v", err)
- }
-
- if out.Len() != 0 {
- t.Fatalf("WriteFlush should not flush I/O, got %q", out.String())
- }
-
- err = enc.FlushIO()
- if err != nil {
- t.Fatalf("FlushIO: %v", err)
- }
-
- if got, want := out.String(), "0006ok0000"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-
- out.Reset()
- bw = bufio.NewWriter(&out)
- enc = pktline.NewEncoder(bw)
-
- err = enc.WriteData([]byte("yo"))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- err = enc.WriteFlushAndFlushIO()
- if err != nil {
- t.Fatalf("WriteFlushAndFlushIO: %v", err)
- }
-
- if got, want := out.String(), "0006yo0000"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-}
diff --git a/protocol/pktline/encoder_set_max_data_cannot_exceed_wire_limit_test.go b/protocol/pktline/encoder_set_max_data_cannot_exceed_wire_limit_test.go
deleted file mode 100644
index c4c4f5c9..00000000
--- a/protocol/pktline/encoder_set_max_data_cannot_exceed_wire_limit_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package pktline_test
-
-import (
- "bufio"
- "bytes"
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestEncoderSetMaxDataCannotExceedWireLimit(t *testing.T) {
- t.Parallel()
-
- var out bytes.Buffer
-
- bw := bufio.NewWriter(&out)
-
- enc := pktline.NewEncoder(bw)
- enc.SetMaxData(pktline.LargePacketDataMax + 100)
-
- err := enc.WriteData(bytes.Repeat([]byte{'x'}, pktline.LargePacketDataMax+1))
- if !errors.Is(err, pktline.ErrTooLarge) {
- t.Fatalf("got err %v, want ErrTooLarge", err)
- }
-}
diff --git a/protocol/pktline/encoder_writes_frames_test.go b/protocol/pktline/encoder_writes_frames_test.go
deleted file mode 100644
index 2a595730..00000000
--- a/protocol/pktline/encoder_writes_frames_test.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package pktline_test
-
-import (
- "bufio"
- "bytes"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestEncoderWritesFrames(t *testing.T) {
- t.Parallel()
-
- var b bytes.Buffer
-
- bw := bufio.NewWriter(&b)
-
- enc := pktline.NewEncoder(bw)
-
- err := enc.WriteData([]byte("hi"))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- err = enc.WriteFlush()
- if err != nil {
- t.Fatalf("WriteFlush: %v", err)
- }
-
- err = enc.WriteDelim()
- if err != nil {
- t.Fatalf("WriteDelim: %v", err)
- }
-
- err = enc.WriteResponseEnd()
- if err != nil {
- t.Fatalf("WriteResponseEnd: %v", err)
- }
-
- err = enc.FlushIO()
- if err != nil {
- t.Fatalf("FlushIO: %v", err)
- }
-
- got := b.String()
-
- want := "0006hi000000010002"
- if got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-}
diff --git a/protocol/pktline/errors.go b/protocol/pktline/errors.go
deleted file mode 100644
index 866ff467..00000000
--- a/protocol/pktline/errors.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package pktline
-
-import "errors"
-
-var (
- // ErrInvalidLength indicates a malformed 4-byte hexadecimal length header.
- ErrInvalidLength = errors.New("pktline: invalid length header")
- // ErrTooLarge indicates a payload exceeds configured packet data limits.
- ErrTooLarge = errors.New("pktline: payload too large")
-)
-
-// ProtocolError reports invalid pkt-line framing.
-//
-// It is returned for protocol violations such as invalid control values
-// (for example 0003) or non-hex length headers.
-type ProtocolError struct {
- Header [4]byte
- Reason string
-}
-
-func (e *ProtocolError) Error() string {
- if e == nil {
- return "<nil>"
- }
-
- if e.Reason == "" {
- return "pktline: protocol error"
- }
-
- return "pktline: protocol error: " + e.Reason
-}
diff --git a/protocol/pktline/frame.go b/protocol/pktline/frame.go
deleted file mode 100644
index a1cf708c..00000000
--- a/protocol/pktline/frame.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package pktline
-
-// Frame is one decoded pkt-line frame.
-//
-// For PacketData, Payload holds frame bytes (possibly empty for 0004).
-// For control frames, Payload is nil.
-type Frame struct {
- Type PacketType
- Payload []byte
-}
diff --git a/protocol/pktline/header.go b/protocol/pktline/header.go
deleted file mode 100644
index 41e50e04..00000000
--- a/protocol/pktline/header.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package pktline
-
-import "fmt"
-
-func hexval(b byte) int {
- switch {
- case b >= '0' && b <= '9':
- return int(b - '0')
- case b >= 'a' && b <= 'f':
- return int(b-'a') + 10
- case b >= 'A' && b <= 'F':
- return int(b-'A') + 10
- default:
- return -1
- }
-}
-
-// ParseLengthHeader parses a 4-byte hexadecimal pkt-line length header.
-//
-// The returned value is the full on-wire packet size, including the 4-byte
-// header. Semantic interpretation (data/control/error) is done by Decoder.
-//
-// The 4-byte header is only an actual length when above or equal to 4.
-// Otherwise, it indicates some control packet.
-func ParseLengthHeader(h [4]byte) (int, error) {
- a := hexval(h[0])
- b := hexval(h[1])
- c := hexval(h[2])
- d := hexval(h[3])
-
- if a < 0 || b < 0 || c < 0 || d < 0 {
- return 0, fmt.Errorf("%w: %q", ErrInvalidLength, string(h[:]))
- }
-
- return (a << 12) | (b << 8) | (c << 4) | d, nil
-}
-
-// EncodeLengthHeader encodes n as a 4-byte hexadecimal pkt-line header.
-//
-// n is the full on-wire packet size including the 4-byte header.
-//
-// The 4-byte header is only an actual length when above or equal to 4.
-// Otherwise, it indicates some control packet.
-func EncodeLengthHeader(dst *[4]byte, n int) error {
- if n < 0 || n > LargePacketMax {
- return fmt.Errorf("%w: %d", ErrInvalidLength, n)
- }
-
- const hex = "0123456789abcdef"
-
- dst[0] = hex[(n>>12)&0xf]
- dst[1] = hex[(n>>8)&0xf]
- dst[2] = hex[(n>>4)&0xf]
- dst[3] = hex[n&0xf]
-
- return nil
-}
diff --git a/protocol/pktline/parse_length_header_test.go b/protocol/pktline/parse_length_header_test.go
deleted file mode 100644
index 4bc743dd..00000000
--- a/protocol/pktline/parse_length_header_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package pktline_test
-
-import (
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/protocol/pktline"
-)
-
-func TestParseLengthHeader(t *testing.T) {
- t.Parallel()
-
- n, err := pktline.ParseLengthHeader([4]byte{'0', '0', '0', '4'})
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
-
- if n != 4 {
- t.Fatalf("got %d, want 4", n)
- }
-
- _, err = pktline.ParseLengthHeader([4]byte{'0', '0', '0', 'x'})
- if !errors.Is(err, pktline.ErrInvalidLength) {
- t.Fatalf("got err %v, want ErrInvalidLength", err)
- }
-}
diff --git a/protocol/pktline/type.go b/protocol/pktline/type.go
deleted file mode 100644
index 641d1c6c..00000000
--- a/protocol/pktline/type.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package pktline
-
-// PacketType identifies the kind of pkt-line frame.
-type PacketType uint8
-
-const (
- // PacketData is a regular data frame whose payload is application-defined.
- PacketData PacketType = iota
- // PacketFlush is control frame 0000 and marks end of a message.
- PacketFlush
- // PacketDelim is control frame 0001 and separates sections in protocol v2.
- PacketDelim
- // PacketResponseEnd is control frame 0002 and marks response end on stateless v2 transports.
- PacketResponseEnd
-)