xframe/vendor/github.com/alicebob/miniredis/v2/server/proto.go

158 lines
2.8 KiB
Go

package server
import (
"bufio"
"errors"
"strconv"
)
type Simple string
// ErrProtocol is the general error for unexpected input
var ErrProtocol = errors.New("invalid request")
// client always sends arrays with bulk strings
func readArray(rd *bufio.Reader) ([]string, error) {
line, err := rd.ReadString('\n')
if err != nil {
return nil, err
}
if len(line) < 3 {
return nil, ErrProtocol
}
switch line[0] {
default:
return nil, ErrProtocol
case '*':
l, err := strconv.Atoi(line[1 : len(line)-2])
if err != nil {
return nil, err
}
// l can be -1
var fields []string
for ; l > 0; l-- {
s, err := readString(rd)
if err != nil {
return nil, err
}
fields = append(fields, s)
}
return fields, nil
}
}
func readString(rd *bufio.Reader) (string, error) {
line, err := rd.ReadString('\n')
if err != nil {
return "", err
}
if len(line) < 3 {
return "", ErrProtocol
}
switch line[0] {
default:
return "", ErrProtocol
case '+', '-', ':':
// +: simple string
// -: errors
// :: integer
// Simple line based replies.
return string(line[1 : len(line)-2]), nil
case '$':
// bulk strings are: `$5\r\nhello\r\n`
length, err := strconv.Atoi(line[1 : len(line)-2])
if err != nil {
return "", err
}
if length < 0 {
// -1 is a nil response
return "", nil
}
var (
buf = make([]byte, length+2)
pos = 0
)
for pos < length+2 {
n, err := rd.Read(buf[pos:])
if err != nil {
return "", err
}
pos += n
}
return string(buf[:length]), nil
}
}
// parse a reply
func ParseReply(rd *bufio.Reader) (interface{}, error) {
line, err := rd.ReadString('\n')
if err != nil {
return nil, err
}
if len(line) < 3 {
return nil, ErrProtocol
}
switch line[0] {
default:
return nil, ErrProtocol
case '+':
// +: simple string
return Simple(line[1 : len(line)-2]), nil
case '-':
// -: errors
return nil, errors.New(string(line[1 : len(line)-2]))
case ':':
// :: integer
v := line[1 : len(line)-2]
if v == "" {
return 0, nil
}
n, err := strconv.Atoi(v)
if err != nil {
return nil, ErrProtocol
}
return n, nil
case '$':
// bulk strings are: `$5\r\nhello\r\n`
length, err := strconv.Atoi(line[1 : len(line)-2])
if err != nil {
return "", err
}
if length < 0 {
// -1 is a nil response
return nil, nil
}
var (
buf = make([]byte, length+2)
pos = 0
)
for pos < length+2 {
n, err := rd.Read(buf[pos:])
if err != nil {
return "", err
}
pos += n
}
return string(buf[:length]), nil
case '*':
// array
l, err := strconv.Atoi(line[1 : len(line)-2])
if err != nil {
return nil, ErrProtocol
}
// l can be -1
var fields []interface{}
for ; l > 0; l-- {
s, err := ParseReply(rd)
if err != nil {
return nil, err
}
fields = append(fields, s)
}
return fields, nil
}
}