-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsproto.go
131 lines (118 loc) · 2.03 KB
/
sproto.go
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
package sproto
import (
"errors"
"fmt"
"io"
)
type state uint8
const (
START state = iota
READ
ESCAPE
END
)
const (
sof byte = 0x7d
eof byte = sof
esc byte = 0x7e
escxor byte = 0x20
)
func NewFrame(buffer []byte) *Frame {
return &Frame{
data: buffer[:0:len(buffer)],
}
}
func (f *Frame) SetData(data []byte) error {
if len(data) > f.Size() {
return errors.New("datagram length greater than buffer capacity")
}
f.setLen(len(data))
copy(f.data, data)
return nil
}
func (f *Frame) Size() int { return cap(f.data) }
func (f *Frame) setLen(l int) { f.data = f.data[:l] }
type Frame struct {
ptr int
data []byte
}
func (f *Frame) ParseNext(r io.ByteReader) (n int, err error) {
status := START
f.setLen(f.Size()) // Max out size.
readloop:
for dataPtr := 0; dataPtr < f.Size(); {
c, err := r.ReadByte()
if err != nil {
f.setLen(0)
break
}
n++
switch status {
case START:
if c == sof {
status = READ
}
case READ:
if c == esc {
status = ESCAPE
} else if c == eof {
// Succesfully read entire frame.
status = END
f.setLen(dataPtr)
f.ptr = 0
break readloop
} else {
f.data[dataPtr] = c
dataPtr++
}
case ESCAPE:
f.data[dataPtr] = c ^ escxor
dataPtr++
status = READ
default:
panic("unexpected state")
}
}
if status != END {
if len(f.data) == 0 {
err = fmt.Errorf("buffer too small (%d) for datagram", f.Size())
} else {
err = fmt.Errorf("did not reach END byte(%d):%w", status, err)
}
}
return n, err
}
func (f *Frame) Read(b []byte) (n int, err error) {
frameLen := len(f.data)
dataPtr := f.ptr
toWrite := frameLen - dataPtr
if toWrite == 0 {
return 0, io.EOF
}
if dataPtr == 0 {
b[0] = sof
n++
}
for ; dataPtr < frameLen; dataPtr++ {
c := f.data[dataPtr]
remaining := len(b) - n
if remaining < 2 {
break
}
switch c {
case sof, esc:
b[n] = esc
b[n+1] = c ^ escxor
n += 2
default:
b[n] = c
n += 1
}
}
if dataPtr == toWrite {
b[n] = eof
n++
}
f.ptr = dataPtr
return n, nil
}