aboutsummaryrefslogtreecommitdiff
path: root/format/pktline/encoder.go
blob: b4c6dbf000d8236b9dfbfd123131f5fe925a11b5 (about) (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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
}