-
Notifications
You must be signed in to change notification settings - Fork 0
/
cmd_ms.go
256 lines (218 loc) · 5.32 KB
/
cmd_ms.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
package memdproto
import (
"bufio"
"encoding/base64"
"fmt"
"io"
"strconv"
"strings"
)
type MetaSetCmd struct {
key string
data []byte
b64 *FlagKeyAsBase64
rkey *FlagRetrieveKey
opaque FlagOpaque
mode *MetaSetMode
noreply *FlagNoReply
}
var _ Cmd = (*MetaSetCmd)(nil)
func NewMetaSetCmd(key string, data []byte) *MetaSetCmd {
return &MetaSetCmd{
key: key,
data: data,
}
}
func (cmd *MetaSetCmd) Key() string {
return cmd.key
}
func (cmd *MetaSetCmd) SetKeyAsBase64(b64 bool) *MetaSetCmd {
if b64 {
cmd.b64 = &FlagKeyAsBase64{}
} else {
cmd.b64 = nil
}
return cmd
}
func (cmd *MetaSetCmd) SetRetrieveKey(v bool) *MetaSetCmd {
if v {
cmd.rkey = &FlagRetrieveKey{}
} else {
cmd.rkey = nil
}
return cmd
}
func (cmd *MetaSetCmd) SetMode(mode MetaSetMode) *MetaSetCmd {
cmd.mode = &mode
return cmd
}
func (cmd *MetaSetCmd) SetOpaque(opaque []byte) *MetaSetCmd {
cmd.opaque = FlagOpaque(opaque)
return cmd
}
func (cmd *MetaSetCmd) SetNoReply(noreply bool) *MetaSetCmd {
if noreply {
cmd.noreply = &FlagNoReply{}
} else {
cmd.noreply = nil
}
return cmd
}
func (cmd *MetaSetCmd) WriteTo(dst io.Writer) (int64, error) {
var key string
if cmd.b64 != nil {
key = base64.StdEncoding.EncodeToString([]byte(cmd.key))
} else {
key = cmd.key
}
ldata := len(cmd.data)
var written int64
n, err := fmt.Fprintf(dst, "ms %s %d", key, ldata)
written += int64(n)
if err != nil {
return written, err
}
n64, err := writeFlags(dst, cmd.b64, cmd.rkey, cmd.mode, cmd.opaque, cmd.noreply)
written += n64
if err != nil {
return written, err
}
n, err = dst.Write(crlf)
written += int64(n)
if err != nil {
return written, err
}
n, err = dst.Write(cmd.data)
written += int64(n)
if err != nil {
return written, err
}
n, err = dst.Write(crlf)
written += int64(n)
if err != nil {
return written, err
}
return written, err
}
func (cmd *MetaSetCmd) UnmarshalText(data []byte) error {
return fmt.Errorf("not implemented")
}
type MetaSetCmdStatus int
const (
MetaSetCmdStatusInvalid MetaSetCmdStatus = iota
// Set operation was successful (command=HD)
MetaSetCmdStatusStored
// Data _not_ stored, but not because of an error (command=NS)
MetaSetCmdStatusNotStored
// Under CAS semantics, item has been modified since your
// last fetch (as specified by the CAS value. command=EX)
MetaSetCmdStatusExists
// Under CAS semantics, item did not exist (command=NF)
MetaSetCmdStatusNotFound
)
type MetaSetReply struct {
status MetaSetCmdStatus
b64 *FlagKeyAsBase64
cas *FlagRetrieveCas
ccas *FlagCompareCas
flags *FlagSetClientFlags
invalidate *FlagInvalidateOnOldCas
rkey *FlagRetrieveKey
opaque *FlagOpaque
noreply *FlagNoReply
size *FlagRetrieveSize
ttl *FlagSetTTL
mode *MetaSetMode
vivify *FlagVivifyOnMiss
}
func (reply *MetaSetReply) Key() string {
if reply.rkey == nil {
return ""
}
return *reply.rkey.key
}
func (reply *MetaSetReply) Status() MetaSetCmdStatus {
return reply.status
}
func (reply *MetaSetReply) WriteTo(dst io.Writer) (int64, error) {
return 0, nil
}
func (reply *MetaSetReply) ReadFrom(src io.Reader) (int64, error) {
line, err := bufio.NewReader(src).ReadBytes('\n')
lline := len(line)
if err != nil {
return int64(lline), fmt.Errorf(`failed to read reply: %w`, err)
}
// we need at least 4 bytes for <CD>\r\n
if lline < 4 {
return int64(lline), fmt.Errorf(`invalid response for ms command`)
}
if line[lline-2] != '\r' || line[lline-1] != '\n' {
return int64(lline), fmt.Errorf(`expected CRLF in response for ms command`)
}
line = line[:lline-2]
// First two bytes is <CD>, where CD can be one of
// HD, NS, EX, NF
if line[0] == 'H' && line[1] == 'D' {
reply.status = MetaSetCmdStatusStored
} else if line[0] == 'N' && line[1] == 'S' {
reply.status = MetaSetCmdStatusNotStored
} else if line[0] == 'E' && line[1] == 'X' {
reply.status = MetaSetCmdStatusExists
} else if line[0] == 'N' && line[1] == 'F' {
reply.status = MetaSetCmdStatusNotFound
} else {
return int64(lline), fmt.Errorf(`expected HD/NS/EX/NF: invalid response for ms command`)
}
if lline == 4 {
return int64(lline), nil
}
if err := reply.readFlags(line[2:]); err != nil {
return int64(lline), fmt.Errorf(`failed to read flags: %w`, err)
}
return int64(lline), nil
}
func (reply *MetaSetReply) readFlags(data []byte) error {
rb := readbuf{data: data}
for rb.Len() > 0 {
// first, a space
if rb.data[0] != ' ' {
return fmt.Errorf(`expected space`)
}
rb.Advance()
switch rb.data[0] {
case 'b':
rb.Advance()
reply.b64 = &FlagKeyAsBase64{}
case 'c':
rb.Advance()
var val strings.Builder
for rb.Len() > 0 && rb.data[0] != ' ' {
val.WriteByte(rb.data[0])
rb.Advance()
}
if val.Len() == 0 {
return fmt.Errorf(`expected value after c flag`)
}
u64, err := strconv.ParseUint(val.String(), 64, 10)
if err != nil {
return fmt.Errorf(`expected numeric value after c flag: %w`, err)
}
casval := FlagRetrieveCas(u64)
reply.cas = &casval
case 'k':
rb.Advance()
var val strings.Builder
for rb.Len() > 0 && rb.data[0] != ' ' {
val.WriteByte(rb.data[0])
rb.Advance()
}
if val.Len() == 0 {
return fmt.Errorf(`expected value after k flag`)
}
key := val.String()
reply.rkey = &FlagRetrieveKey{key: &key}
}
}
return nil
}