-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathresp.go
159 lines (141 loc) · 3.93 KB
/
resp.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
152
153
154
155
156
157
158
159
// package resp implements the Redis Serialization Protocol (RESP) for the Redis server.
// ref: https://redis.io/docs/latest/develop/reference/protocol-spec/
package resp
import (
"errors"
"fmt"
"io"
"strconv"
"strings"
"syscall"
"github.com/codecrafters-io/redis-starter-go/app/database"
)
const (
TypeSimpleString = '+'
TypeBulkString = '$'
TypeArray = '*'
TypeError = '-'
TypeInt = ':'
)
type reader interface {
ReadString(delim byte) (string, error)
ReadByte() (byte, error)
}
// HandleRESPArray reads a RESP array from the reader and returns a slice of strings.
func HandleRESPArray(r reader) ([]string, error) {
elCountMsg, err := readRESPMsg(r)
if err != nil {
return nil, fmt.Errorf("error reading from connection: %w", err)
}
// base-10 value.
elCount, err := strconv.ParseInt(elCountMsg, 10, 64)
if err != nil {
return nil, fmt.Errorf("unable to parse element count: %w", err)
}
res := make([]string, elCount)
for i := int64(0); i < elCount; i++ {
typmsg, err := readRESPMsg(r)
if err != nil {
return nil, fmt.Errorf("error reading from connection: %w", err)
}
switch typmsg[0] {
case TypeBulkString:
len, err := strconv.ParseInt(typmsg[1:], 10, 64)
if err != nil {
return nil, fmt.Errorf("unable to parse string length: %w", err)
}
s, err := readRESPMsg(r)
if err != nil {
return nil, fmt.Errorf("error reading string: %w", err)
}
res[i] = s[:len]
// TODO
case TypeSimpleString:
// TODO
case TypeArray:
// TODO
}
}
return res, nil
}
// readRESPMsg reads a RESP message from the reader and trim the line break.
func readRESPMsg(r reader) (string, error) {
msg, err := r.ReadString('\n')
if err != nil {
return "", fmt.Errorf("error reading from connection: %s", err.Error())
}
// The \r\n (CRLF) is the protocol's terminator, which always separates its parts.
return strings.TrimRight(msg, "\r\n"), nil
}
// CheckDataType reads the first byte from the reader and returns the type of the RESP message.
func CheckDataType(r reader) (byte, error) {
b, err := r.ReadByte()
if err != nil {
if err == io.EOF {
return 0, io.EOF
}
if errors.Is(err, syscall.ECONNRESET) {
fmt.Println("connection reset by peer")
return 0, io.EOF
}
return 0, fmt.Errorf("error reading byte from connection 5: %v", err.Error())
}
switch b {
case TypeSimpleString:
return TypeSimpleString, nil
case TypeBulkString:
return TypeBulkString, nil
case TypeArray:
return TypeArray, nil
default:
return 0, fmt.Errorf("unknown type: %c", b)
}
}
func NewSimpleString(msg string) []byte {
return []byte(fmt.Sprintf("%c%s\r\n", TypeSimpleString, msg))
}
func NewBulkString(msg string) []byte {
return []byte(fmt.Sprintf("%c%d\r\n%s\r\n", TypeBulkString, len(msg), msg))
}
func NewNullBulkString() []byte {
return []byte(fmt.Sprintf("%c-1\r\n", TypeBulkString))
}
func NewArray(arr [][]byte) []byte {
prefix := []byte(fmt.Sprintf("%c%d\r\n", TypeArray, len(arr)))
for _, v := range arr {
prefix = append(prefix, v...)
}
return prefix
}
func NewErrorMSG(msg string) []byte {
return []byte(fmt.Sprintf("%cERR %s\r\n", TypeError, msg))
}
func NewRDBFile(f []byte) []byte {
prefix := []byte(fmt.Sprintf("%c%d\r\n", TypeBulkString, len(f)))
return append(prefix, f...)
}
func NewSetCmd(arr []string) []byte {
a := [][]byte{NewBulkString("SET"), NewBulkString(arr[1]), NewBulkString(arr[2])}
if len(arr) > 3 {
a = append(a, NewBulkString(arr[3]), NewBulkString(arr[4]))
}
return NewArray(a)
}
func NewInt(i int) []byte {
return []byte(fmt.Sprintf("%c%d\r\n", TypeInt, i))
}
func NewStreamEntries(ents []database.Entry) []byte {
res := make([][]byte, len(ents))
for i, e := range ents {
kvs := make([][]byte, len(e.KVs)*2)
for j, kv := range e.KVs {
kvs[j*2] = NewBulkString(kv.Key)
kvs[j*2+1] = NewBulkString(kv.Value)
}
res[i] = NewArray([][]byte{
NewBulkString(database.StreamEntryID(e.Ts, e.Seq)),
NewArray(kvs),
})
}
return NewArray(res)
}