mirror of
https://github.com/HDT3213/godis.git
synced 2025-10-06 17:26:52 +08:00
refactor code structure for gnet
This commit is contained in:
122
redis/parser/parserv2.go
Normal file
122
redis/parser/parserv2.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ParseV2(r io.Reader) ([][]byte, error) {
|
||||
// 读取起始字符 '*'
|
||||
buf := make([]byte, 1)
|
||||
_, err := io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf[0] != '*' {
|
||||
// try text protocol
|
||||
buf2, err := readLine(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf = append(buf, buf2...)
|
||||
line := strings.Split(string(buf), " ")
|
||||
result := make([][]byte, len(line))
|
||||
for i, s := range line {
|
||||
result[i] = []byte(s)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 读取参数数量
|
||||
count, err := readInteger(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count < 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 读取每个参数
|
||||
result := make([][]byte, count)
|
||||
for i := 0; i < count; i++ {
|
||||
// 读取类型前缀
|
||||
_, err := io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch buf[0] {
|
||||
case '$': // Bulk String
|
||||
strLen, err := readInteger(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if strLen < 0 {
|
||||
result[i] = nil // Null Bulk String
|
||||
continue
|
||||
}
|
||||
|
||||
data := make([]byte, strLen+2)
|
||||
_, err = io.ReadFull(r, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result[i] = data[:strLen]
|
||||
|
||||
// case '+', ':': // Simple String or Integer
|
||||
// simpleStr, err := readLine(r)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// result[i] = []byte(simpleStr)
|
||||
|
||||
// case '-': // Error
|
||||
// errStr, err := readLine(r)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// result[i] = []byte(errStr)
|
||||
|
||||
default:
|
||||
return nil, errors.New("unsupported RESP type")
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func readInteger(r io.Reader) (int, error) {
|
||||
line, err := readLine(r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return strconv.Atoi(string(line))
|
||||
}
|
||||
|
||||
func readLine(r io.Reader) ([]byte, error) {
|
||||
var line []byte
|
||||
buf := make([]byte, 1)
|
||||
|
||||
for {
|
||||
_, err := io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch buf[0] {
|
||||
case '\r':
|
||||
_, err := io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
return nil, errors.New("unexpected EOF after \\r")
|
||||
}
|
||||
if buf[0] != '\n' {
|
||||
return nil, errors.New("expected \\n after \\r")
|
||||
}
|
||||
return line, nil
|
||||
default:
|
||||
line = append(line, buf[0])
|
||||
}
|
||||
}
|
||||
}
|
55
redis/parser/parserv2_test.go
Normal file
55
redis/parser/parserv2_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkParseSETCommand(b *testing.B) {
|
||||
valueSizes := []int{10, 100, 1000, 10000}
|
||||
|
||||
for _, size := range valueSizes {
|
||||
value := bytes.Repeat([]byte("a"), size)
|
||||
cmd := []byte(fmt.Sprintf("*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$%d\r\n%s\r\n", len(value), value))
|
||||
|
||||
b.Run("value_size_"+formatSize(size), func(subB *testing.B) {
|
||||
subB.ResetTimer()
|
||||
|
||||
for i := 0; i < subB.N; i++ {
|
||||
reader := bytes.NewReader(cmd)
|
||||
_, err := ParseV2(reader)
|
||||
if err != nil {
|
||||
subB.Fatalf("parse failed: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseV2(t *testing.T) {
|
||||
value := bytes.Repeat([]byte("a"), 100)
|
||||
data := []byte(fmt.Sprintf("*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$%d\r\n%s\r\n", len(value), value))
|
||||
cmdLine, err := ParseV2(bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if len(cmdLine) != 3 || string(cmdLine[0]) != "SET" || string(cmdLine[1]) != "key" || string(cmdLine[2]) != string(value) {
|
||||
t.Error("parse error")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func formatSize(size int) string {
|
||||
units := []string{"B", "KB", "MB"}
|
||||
unitIndex := 0
|
||||
floatSize := float64(size)
|
||||
|
||||
for floatSize >= 1024 && unitIndex < len(units)-1 {
|
||||
floatSize /= 1024
|
||||
unitIndex++
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%.0f%s", floatSize, units[unitIndex])
|
||||
}
|
Reference in New Issue
Block a user