mirror of https://github.com/gogits/gogs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
5.4 KiB
262 lines
5.4 KiB
package redis |
|
|
|
import ( |
|
"errors" |
|
"fmt" |
|
"strconv" |
|
|
|
"gopkg.in/bufio.v1" |
|
) |
|
|
|
type multiBulkParser func(rd *bufio.Reader, n int64) (interface{}, error) |
|
|
|
var ( |
|
errReaderTooSmall = errors.New("redis: reader is too small") |
|
) |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
func appendArgs(buf []byte, args []string) []byte { |
|
buf = append(buf, '*') |
|
buf = strconv.AppendUint(buf, uint64(len(args)), 10) |
|
buf = append(buf, '\r', '\n') |
|
for _, arg := range args { |
|
buf = append(buf, '$') |
|
buf = strconv.AppendUint(buf, uint64(len(arg)), 10) |
|
buf = append(buf, '\r', '\n') |
|
buf = append(buf, arg...) |
|
buf = append(buf, '\r', '\n') |
|
} |
|
return buf |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
func readLine(rd *bufio.Reader) ([]byte, error) { |
|
line, isPrefix, err := rd.ReadLine() |
|
if err != nil { |
|
return line, err |
|
} |
|
if isPrefix { |
|
return line, errReaderTooSmall |
|
} |
|
return line, nil |
|
} |
|
|
|
func readN(rd *bufio.Reader, n int) ([]byte, error) { |
|
b, err := rd.ReadN(n) |
|
if err == bufio.ErrBufferFull { |
|
tmp := make([]byte, n) |
|
r := copy(tmp, b) |
|
b = tmp |
|
|
|
for { |
|
nn, err := rd.Read(b[r:]) |
|
r += nn |
|
if r >= n { |
|
// Ignore error if we read enough. |
|
break |
|
} |
|
if err != nil { |
|
return nil, err |
|
} |
|
} |
|
} else if err != nil { |
|
return nil, err |
|
} |
|
return b, nil |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
func parseReq(rd *bufio.Reader) ([]string, error) { |
|
line, err := readLine(rd) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if line[0] != '*' { |
|
return []string{string(line)}, nil |
|
} |
|
numReplies, err := strconv.ParseInt(string(line[1:]), 10, 64) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
args := make([]string, 0, numReplies) |
|
for i := int64(0); i < numReplies; i++ { |
|
line, err = readLine(rd) |
|
if err != nil { |
|
return nil, err |
|
} |
|
if line[0] != '$' { |
|
return nil, fmt.Errorf("redis: expected '$', but got %q", line) |
|
} |
|
|
|
argLen, err := strconv.ParseInt(string(line[1:]), 10, 32) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
arg, err := readN(rd, int(argLen)+2) |
|
if err != nil { |
|
return nil, err |
|
} |
|
args = append(args, string(arg[:argLen])) |
|
} |
|
return args, nil |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
func parseReply(rd *bufio.Reader, p multiBulkParser) (interface{}, error) { |
|
line, err := readLine(rd) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
switch line[0] { |
|
case '-': |
|
return nil, errorf(string(line[1:])) |
|
case '+': |
|
return string(line[1:]), nil |
|
case ':': |
|
v, err := strconv.ParseInt(string(line[1:]), 10, 64) |
|
if err != nil { |
|
return nil, err |
|
} |
|
return v, nil |
|
case '$': |
|
if len(line) == 3 && line[1] == '-' && line[2] == '1' { |
|
return nil, Nil |
|
} |
|
|
|
replyLen, err := strconv.Atoi(string(line[1:])) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
b, err := readN(rd, replyLen+2) |
|
if err != nil { |
|
return nil, err |
|
} |
|
return string(b[:replyLen]), nil |
|
case '*': |
|
if len(line) == 3 && line[1] == '-' && line[2] == '1' { |
|
return nil, Nil |
|
} |
|
|
|
repliesNum, err := strconv.ParseInt(string(line[1:]), 10, 64) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
return p(rd, repliesNum) |
|
} |
|
return nil, fmt.Errorf("redis: can't parse %q", line) |
|
} |
|
|
|
func parseSlice(rd *bufio.Reader, n int64) (interface{}, error) { |
|
vals := make([]interface{}, 0, n) |
|
for i := int64(0); i < n; i++ { |
|
v, err := parseReply(rd, parseSlice) |
|
if err == Nil { |
|
vals = append(vals, nil) |
|
} else if err != nil { |
|
return nil, err |
|
} else { |
|
vals = append(vals, v) |
|
} |
|
} |
|
return vals, nil |
|
} |
|
|
|
func parseStringSlice(rd *bufio.Reader, n int64) (interface{}, error) { |
|
vals := make([]string, 0, n) |
|
for i := int64(0); i < n; i++ { |
|
viface, err := parseReply(rd, nil) |
|
if err != nil { |
|
return nil, err |
|
} |
|
v, ok := viface.(string) |
|
if !ok { |
|
return nil, fmt.Errorf("got %T, expected string", viface) |
|
} |
|
vals = append(vals, v) |
|
} |
|
return vals, nil |
|
} |
|
|
|
func parseBoolSlice(rd *bufio.Reader, n int64) (interface{}, error) { |
|
vals := make([]bool, 0, n) |
|
for i := int64(0); i < n; i++ { |
|
viface, err := parseReply(rd, nil) |
|
if err != nil { |
|
return nil, err |
|
} |
|
v, ok := viface.(int64) |
|
if !ok { |
|
return nil, fmt.Errorf("got %T, expected int64", viface) |
|
} |
|
vals = append(vals, v == 1) |
|
} |
|
return vals, nil |
|
} |
|
|
|
func parseStringStringMap(rd *bufio.Reader, n int64) (interface{}, error) { |
|
m := make(map[string]string, n/2) |
|
for i := int64(0); i < n; i += 2 { |
|
keyiface, err := parseReply(rd, nil) |
|
if err != nil { |
|
return nil, err |
|
} |
|
key, ok := keyiface.(string) |
|
if !ok { |
|
return nil, fmt.Errorf("got %T, expected string", keyiface) |
|
} |
|
|
|
valueiface, err := parseReply(rd, nil) |
|
if err != nil { |
|
return nil, err |
|
} |
|
value, ok := valueiface.(string) |
|
if !ok { |
|
return nil, fmt.Errorf("got %T, expected string", valueiface) |
|
} |
|
|
|
m[key] = value |
|
} |
|
return m, nil |
|
} |
|
|
|
func parseZSlice(rd *bufio.Reader, n int64) (interface{}, error) { |
|
zz := make([]Z, n/2) |
|
for i := int64(0); i < n; i += 2 { |
|
z := &zz[i/2] |
|
|
|
memberiface, err := parseReply(rd, nil) |
|
if err != nil { |
|
return nil, err |
|
} |
|
member, ok := memberiface.(string) |
|
if !ok { |
|
return nil, fmt.Errorf("got %T, expected string", memberiface) |
|
} |
|
z.Member = member |
|
|
|
scoreiface, err := parseReply(rd, nil) |
|
if err != nil { |
|
return nil, err |
|
} |
|
scorestr, ok := scoreiface.(string) |
|
if !ok { |
|
return nil, fmt.Errorf("got %T, expected string", scoreiface) |
|
} |
|
score, err := strconv.ParseFloat(scorestr, 64) |
|
if err != nil { |
|
return nil, err |
|
} |
|
z.Score = score |
|
} |
|
return zz, nil |
|
}
|
|
|