1 package proto
2
3 import (
4 "bufio"
5 "fmt"
6 "io"
7 "strconv"
8
9 "github.com/go-redis/redis/internal/util"
10 )
11
12 const (
13 ErrorReply = '-'
14 StatusReply = '+'
15 IntReply = ':'
16 StringReply = '$'
17 ArrayReply = '*'
18 )
19
20
21
22 const Nil = RedisError("redis: nil")
23
24 type RedisError string
25
26 func (e RedisError) Error() string { return string(e) }
27
28
29
30 type MultiBulkParse func(*Reader, int64) (interface{}, error)
31
32 type Reader struct {
33 rd *bufio.Reader
34 _buf []byte
35 }
36
37 func NewReader(rd io.Reader) *Reader {
38 return &Reader{
39 rd: bufio.NewReader(rd),
40 _buf: make([]byte, 64),
41 }
42 }
43
44 func (r *Reader) Reset(rd io.Reader) {
45 r.rd.Reset(rd)
46 }
47
48 func (r *Reader) ReadLine() ([]byte, error) {
49 line, isPrefix, err := r.rd.ReadLine()
50 if err != nil {
51 return nil, err
52 }
53 if isPrefix {
54 return nil, bufio.ErrBufferFull
55 }
56 if len(line) == 0 {
57 return nil, fmt.Errorf("redis: reply is empty")
58 }
59 if isNilReply(line) {
60 return nil, Nil
61 }
62 return line, nil
63 }
64
65 func (r *Reader) ReadReply(m MultiBulkParse) (interface{}, error) {
66 line, err := r.ReadLine()
67 if err != nil {
68 return nil, err
69 }
70
71 switch line[0] {
72 case ErrorReply:
73 return nil, ParseErrorReply(line)
74 case StatusReply:
75 return string(line[1:]), nil
76 case IntReply:
77 return util.ParseInt(line[1:], 10, 64)
78 case StringReply:
79 return r.readStringReply(line)
80 case ArrayReply:
81 n, err := parseArrayLen(line)
82 if err != nil {
83 return nil, err
84 }
85 return m(r, n)
86 }
87 return nil, fmt.Errorf("redis: can't parse %.100q", line)
88 }
89
90 func (r *Reader) ReadIntReply() (int64, error) {
91 line, err := r.ReadLine()
92 if err != nil {
93 return 0, err
94 }
95 switch line[0] {
96 case ErrorReply:
97 return 0, ParseErrorReply(line)
98 case IntReply:
99 return util.ParseInt(line[1:], 10, 64)
100 default:
101 return 0, fmt.Errorf("redis: can't parse int reply: %.100q", line)
102 }
103 }
104
105 func (r *Reader) ReadString() (string, error) {
106 line, err := r.ReadLine()
107 if err != nil {
108 return "", err
109 }
110 switch line[0] {
111 case ErrorReply:
112 return "", ParseErrorReply(line)
113 case StringReply:
114 return r.readStringReply(line)
115 case StatusReply:
116 return string(line[1:]), nil
117 case IntReply:
118 return string(line[1:]), nil
119 default:
120 return "", fmt.Errorf("redis: can't parse reply=%.100q reading string", line)
121 }
122 }
123
124 func (r *Reader) readStringReply(line []byte) (string, error) {
125 if isNilReply(line) {
126 return "", Nil
127 }
128
129 replyLen, err := strconv.Atoi(string(line[1:]))
130 if err != nil {
131 return "", err
132 }
133
134 b := make([]byte, replyLen+2)
135 _, err = io.ReadFull(r.rd, b)
136 if err != nil {
137 return "", err
138 }
139
140 return util.BytesToString(b[:replyLen]), nil
141 }
142
143 func (r *Reader) ReadArrayReply(m MultiBulkParse) (interface{}, error) {
144 line, err := r.ReadLine()
145 if err != nil {
146 return nil, err
147 }
148 switch line[0] {
149 case ErrorReply:
150 return nil, ParseErrorReply(line)
151 case ArrayReply:
152 n, err := parseArrayLen(line)
153 if err != nil {
154 return nil, err
155 }
156 return m(r, n)
157 default:
158 return nil, fmt.Errorf("redis: can't parse array reply: %.100q", line)
159 }
160 }
161
162 func (r *Reader) ReadArrayLen() (int64, error) {
163 line, err := r.ReadLine()
164 if err != nil {
165 return 0, err
166 }
167 switch line[0] {
168 case ErrorReply:
169 return 0, ParseErrorReply(line)
170 case ArrayReply:
171 return parseArrayLen(line)
172 default:
173 return 0, fmt.Errorf("redis: can't parse array reply: %.100q", line)
174 }
175 }
176
177 func (r *Reader) ReadScanReply() ([]string, uint64, error) {
178 n, err := r.ReadArrayLen()
179 if err != nil {
180 return nil, 0, err
181 }
182 if n != 2 {
183 return nil, 0, fmt.Errorf("redis: got %d elements in scan reply, expected 2", n)
184 }
185
186 cursor, err := r.ReadUint()
187 if err != nil {
188 return nil, 0, err
189 }
190
191 n, err = r.ReadArrayLen()
192 if err != nil {
193 return nil, 0, err
194 }
195
196 keys := make([]string, n)
197 for i := int64(0); i < n; i++ {
198 key, err := r.ReadString()
199 if err != nil {
200 return nil, 0, err
201 }
202 keys[i] = key
203 }
204
205 return keys, cursor, err
206 }
207
208 func (r *Reader) ReadInt() (int64, error) {
209 b, err := r.readTmpBytesReply()
210 if err != nil {
211 return 0, err
212 }
213 return util.ParseInt(b, 10, 64)
214 }
215
216 func (r *Reader) ReadUint() (uint64, error) {
217 b, err := r.readTmpBytesReply()
218 if err != nil {
219 return 0, err
220 }
221 return util.ParseUint(b, 10, 64)
222 }
223
224 func (r *Reader) ReadFloatReply() (float64, error) {
225 b, err := r.readTmpBytesReply()
226 if err != nil {
227 return 0, err
228 }
229 return util.ParseFloat(b, 64)
230 }
231
232 func (r *Reader) readTmpBytesReply() ([]byte, error) {
233 line, err := r.ReadLine()
234 if err != nil {
235 return nil, err
236 }
237 switch line[0] {
238 case ErrorReply:
239 return nil, ParseErrorReply(line)
240 case StringReply:
241 return r._readTmpBytesReply(line)
242 case StatusReply:
243 return line[1:], nil
244 default:
245 return nil, fmt.Errorf("redis: can't parse string reply: %.100q", line)
246 }
247 }
248
249 func (r *Reader) _readTmpBytesReply(line []byte) ([]byte, error) {
250 if isNilReply(line) {
251 return nil, Nil
252 }
253
254 replyLen, err := strconv.Atoi(string(line[1:]))
255 if err != nil {
256 return nil, err
257 }
258
259 buf := r.buf(replyLen + 2)
260 _, err = io.ReadFull(r.rd, buf)
261 if err != nil {
262 return nil, err
263 }
264
265 return buf[:replyLen], nil
266 }
267
268 func (r *Reader) buf(n int) []byte {
269 if d := n - cap(r._buf); d > 0 {
270 r._buf = append(r._buf, make([]byte, d)...)
271 }
272 return r._buf[:n]
273 }
274
275 func isNilReply(b []byte) bool {
276 return len(b) == 3 &&
277 (b[0] == StringReply || b[0] == ArrayReply) &&
278 b[1] == '-' && b[2] == '1'
279 }
280
281 func ParseErrorReply(line []byte) error {
282 return RedisError(string(line[1:]))
283 }
284
285 func parseArrayLen(line []byte) (int64, error) {
286 if isNilReply(line) {
287 return 0, Nil
288 }
289 return util.ParseInt(line[1:], 10, 64)
290 }
291
View as plain text