/
wire.go
151 lines (128 loc) · 2.98 KB
/
wire.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
package mindis
import (
"bufio"
"bytes"
"io"
"strconv"
)
/* reads a reply from the redis server, accepting single line replies, error messages, integers, bulk and multi-bulk replies */
func readMsg(br *bufio.Reader) (byte, [][]byte, error) {
var b []byte
var data [][]byte
flag, err := br.ReadByte()
switch flag {
case '+', '-', ':':
b, err = readLine(br)
data = [][]byte{b}
case '$':
b, err = readBulk(br)
data = [][]byte{b}
case '*':
data, err = readMultiBulk(br)
default:
err = PROTOCOL_ERROR
}
return flag, data, err
}
/* reads a multibulk reply, after the required '*' was read */
func readMultiBulk(br *bufio.Reader) ([][]byte, error) {
count, err := readInt(br)
switch {
case err != nil:
return nil, err
case count < 0:
return nil, PROTOCOL_ERROR
}
data := make([][]byte, 0, count)
for count > 0 {
f, err := br.ReadByte()
var b []byte
switch f {
case '$':
b, err = readBulk(br)
if err != nil {
return nil, err
}
case ':':
// coerce an integer into mere bytes
b, err = readLine(br)
if err != nil {
return nil, err
}
default:
return nil, PROTOCOL_ERROR
}
data = append(data, b)
count--
}
return data, nil
}
/* reads a bulk reply from br, after the required '$' was read */
func readBulk(br *bufio.Reader) ([]byte, error) {
size, err := readInt(br)
if err != nil {
return nil, err
}
if size < 0 {
return nil, nil
}
data := make([]byte, size+2)
_, err = readFull(br, data)
switch {
case err != nil:
return nil, err
case data[size] != '\r':
return nil, PROTOCOL_ERROR
case data[size+1] != '\n':
return nil, PROTOCOL_ERROR
}
return data[:size], nil
}
/* forces a full read of p or produces an error for readers that disobey the requirement that Read only return on error or fulfillment */
func readFull(r io.Reader, p []byte) (int, error) {
cur := 0
for cur < len(p) {
amt, err := r.Read(p[cur:])
cur += amt
if err != nil {
return cur, err
}
}
return cur, nil
}
/* reads a integer line from br */
func readInt(br *bufio.Reader) (int, error) {
line, err := readLine(br)
if err != nil {
return -1, err
}
return strconv.Atoi(string(line))
}
/* reads a crlf terminated line from br, tolerating embedded lf's which should never happen but exhaustively
analyzing redis seemed harder than just coding defensively; strips the trailing crlf off */
func readLine(br *bufio.Reader) ([]byte, error) {
line := make([]byte, 0, 64)
for {
part, err := br.ReadSlice('\n')
if err != nil {
return nil, err
}
if bytes.HasSuffix(part, crlf) {
line = append(line, part[:len(part)-2]...)
break
}
line = append(line, part...)
}
return line, nil
}
/*
When dealing with a network stack, you want to write full messages whenever possible; therefore, we take the extra copy costs of composing a complete message
*/
func writeRequest(w io.Writer, verb string, args ...interface{}) error {
b, err := marshalRequest(verb, args...)
if err != nil {
return err
}
_, err = w.Write(b)
return err
}