-
Notifications
You must be signed in to change notification settings - Fork 3
/
scanner.go
187 lines (158 loc) · 3.54 KB
/
scanner.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package octetcounting
import (
"bufio"
"bytes"
"io"
"strconv"
"sync"
)
var readerPool sync.Pool
// getReader returns a *bufio.Reader that is guaranteed
// to have a buffer of at least `size`.
func getReader(r io.Reader, size int) *bufio.Reader {
if buf := readerPool.Get(); buf != nil {
buf := buf.(*bufio.Reader)
// If the buffer we get is smaller than the requested buffer, put it back
// and create a new one. stdlib has multiple buckets for various sizes to
// make this more efficient, but that's overkill here.
if buf.Size() < size {
readerPool.Put(buf)
buf = bufio.NewReaderSize(r, size)
} else {
buf.Reset(r)
}
return buf
}
return bufio.NewReaderSize(r, size)
}
// putReader returns the given bufio.Reader to the pool to be used again.
func putReader(r *bufio.Reader) {
readerPool.Put(r)
}
// eof represents a marker byte for the end of the reader
var eof = byte(0)
// lf represents the NewLine
var lf = byte(10)
// ws represents the whitespace
var ws = byte(32)
// lt represents the "<" character
var lt = byte(60)
// isDigit returns true if the byte represents a number in [0,9]
func isDigit(ch byte) bool {
return (ch >= 47 && ch <= 57)
}
// isNonZeroDigit returns true if the byte represents a number in ]0,9]
func isNonZeroDigit(ch byte) bool {
return (ch >= 48 && ch <= 57)
}
// Scanner represents the lexical scanner for octet counting transport.
type Scanner struct {
r *bufio.Reader
msglen uint64
ready bool
}
// NewScanner returns a pointer to a new instance of Scanner.
func NewScanner(r io.Reader, maxLength int) *Scanner {
return &Scanner{
r: getReader(r, maxLength+20), // max uint64 is 19 characters + a space
}
}
// read reads the next byte from the buffered reader
// it returns the byte(0) if an error occurs (or io.EOF is returned)
func (s *Scanner) read() byte {
b, err := s.r.ReadByte()
if err != nil {
return eof
}
return b
}
// unread places the previously read byte back on the reader
func (s *Scanner) unread() {
_ = s.r.UnreadByte()
}
// Scan returns the next token.
func (s *Scanner) Scan() (tok Token) {
// Read the next byte.
b := s.read()
if isNonZeroDigit(b) {
s.unread()
s.ready = false
return s.scanMsgLen()
}
// Otherwise read the individual character
switch b {
case eof:
s.ready = false
return Token{
typ: EOF,
}
case lf:
s.ready = true
return Token{
typ: LF,
lit: []byte{lf},
}
case ws:
s.ready = true
return Token{
typ: WS,
lit: []byte{ws},
}
case lt:
if s.msglen > 0 && s.ready {
s.unread()
return s.scanSyslogMsg()
}
}
return Token{
typ: ILLEGAL,
lit: []byte{b},
}
}
func (s *Scanner) scanMsgLen() Token {
// Create a buffer and read the current character into it
var buf bytes.Buffer
buf.WriteByte(s.read())
// Read every subsequent digit character into the buffer
// Non-digit characters and EOF will cause the loop to exit
for {
if b := s.read(); b == eof {
break
} else if !isDigit(b) {
s.unread()
break
} else {
buf.WriteByte(b)
}
}
msglen := buf.String()
s.msglen, _ = strconv.ParseUint(msglen, 10, 64)
return Token{
typ: MSGLEN,
lit: buf.Bytes(),
}
}
func (s *Scanner) scanSyslogMsg() Token {
// Check the reader contains almost MSGLEN characters
n := int(s.msglen)
b, err := s.r.Peek(n)
if err != nil {
return Token{
typ: EOF,
lit: b,
}
}
// Advance the reader of MSGLEN characters
s.r.Discard(n)
// Reset status
s.ready = false
s.msglen = 0
// Return SYSLOGMSG token
return Token{
typ: SYSLOGMSG,
lit: b,
}
}
func (s *Scanner) Release() {
putReader(s.r)
}