@@ -18,13 +18,19 @@ auto RedisParser::Parse(Buffer str, uint32_t* consumed, RespExpr::Vec* res) -> R
18
18
*consumed = 0 ;
19
19
res->clear ();
20
20
21
- if (str.size () < 2 ) {
22
- return INPUT_PENDING;
23
- }
24
-
25
21
if (state_ == CMD_COMPLETE_S) {
26
- InitStart (str[0 ], res);
27
- } else {
22
+ if (InitStart (str[0 ], res)) {
23
+ // We recognized a non-INLINE state, starting with a special char.
24
+ str.remove_prefix (1 );
25
+ *consumed += 1 ;
26
+ if (server_mode_ && state_ == PARSE_ARG_S) { // server requests start with ARRAY_LEN_S.
27
+ state_ = CMD_COMPLETE_S; // reject and reset the state.
28
+ return BAD_ARRAYLEN;
29
+ }
30
+ if (str.empty ())
31
+ return INPUT_PENDING;
32
+ }
33
+ } else { // INLINE mode, aka PING\n
28
34
// We continue parsing in the middle.
29
35
if (!cached_expr_)
30
36
cached_expr_ = res;
@@ -39,12 +45,15 @@ auto RedisParser::Parse(Buffer str, uint32_t* consumed, RespExpr::Vec* res) -> R
39
45
case ARRAY_LEN_S:
40
46
resultc = ConsumeArrayLen (str);
41
47
break ;
48
+ case PARSE_ARG_TYPE:
49
+ arg_c_ = str[0 ];
50
+ if (server_mode_ && arg_c_ != ' $' ) // server side only supports bulk strings.
51
+ return BAD_BULKLEN;
52
+ resultc.second = 1 ;
53
+ state_ = PARSE_ARG_S;
54
+ break ;
42
55
case PARSE_ARG_S:
43
- if (str.size () == 0 || (str.size () < 4 && str[0 ] != ' _' )) {
44
- resultc.first = INPUT_PENDING;
45
- } else {
46
- resultc = ParseArg (str);
47
- }
56
+ resultc = ParseArg (str);
48
57
break ;
49
58
case INLINE_S:
50
59
DCHECK (parse_stack_.empty ());
@@ -84,7 +93,7 @@ auto RedisParser::Parse(Buffer str, uint32_t* consumed, RespExpr::Vec* res) -> R
84
93
return resultc.first ;
85
94
}
86
95
87
- void RedisParser::InitStart (char prefix_b, RespExpr::Vec* res) {
96
+ bool RedisParser::InitStart (char prefix_b, RespExpr::Vec* res) {
88
97
buf_stash_.clear ();
89
98
stash_.clear ();
90
99
cached_expr_ = res;
@@ -101,18 +110,19 @@ void RedisParser::InitStart(char prefix_b, RespExpr::Vec* res) {
101
110
case ' ,' : // Resp3 DOUBLE
102
111
state_ = PARSE_ARG_S;
103
112
parse_stack_.emplace_back (1 , cached_expr_); // expression of length 1.
104
- break ;
113
+ arg_c_ = prefix_b;
114
+ return true ;
105
115
case ' *' :
106
116
case ' ~' : // Resp3 SET
107
117
state_ = ARRAY_LEN_S;
108
- break ;
118
+ return true ;
109
119
case ' %' : // Resp3 MAP
110
120
state_ = MAP_LEN_S;
111
- break ;
112
- default :
113
- state_ = INLINE_S;
114
- break ;
121
+ return true ;
115
122
}
123
+
124
+ state_ = INLINE_S;
125
+ return false ;
116
126
}
117
127
118
128
void RedisParser::StashState (RespExpr::Vec* res) {
@@ -160,62 +170,66 @@ auto RedisParser::ParseInline(Buffer str) -> ResultConsumed {
160
170
uint8_t * end = str.end ();
161
171
uint8_t * token_start = ptr;
162
172
163
- auto find_token_end = [&] {
173
+ auto find_token_end = []( uint8_t * ptr, uint8_t * end) {
164
174
while (ptr != end && *ptr > 32 )
165
175
++ptr;
176
+ return ptr;
166
177
};
167
178
168
179
if (is_broken_token_) {
169
- find_token_end ();
180
+ ptr = find_token_end (ptr, end );
170
181
size_t len = ptr - token_start;
171
182
172
183
ExtendLastString (Buffer (token_start, len));
173
- if (ptr ! = end) {
174
- is_broken_token_ = false ;
184
+ if (ptr = = end) {
185
+ return {INPUT_PENDING, ptr - token_start} ;
175
186
}
187
+ is_broken_token_ = false ;
176
188
}
177
189
178
- auto is_finish = [&] { return ptr == end || *ptr == ' \n ' ; };
190
+ while (ptr != end) {
191
+ // For inline input we only require \n.
192
+ if (*ptr == ' \n ' ) {
193
+ if (cached_expr_->empty ()) {
194
+ ++ptr;
195
+ continue ; // skip empty line
196
+ }
197
+ break ;
198
+ }
179
199
180
- while (true ) {
181
- while (!is_finish () && *ptr <= 32 ) {
200
+ if (*ptr <= 32 ) { // skip ws/control chars
182
201
++ptr;
202
+ continue ;
183
203
}
184
- // We do not test for \r in order to accept 'nc' input.
185
- if (is_finish ())
186
- break ;
187
204
205
+ // token start
188
206
DCHECK (!is_broken_token_);
189
207
190
208
token_start = ptr;
191
- find_token_end ();
209
+ ptr = find_token_end (ptr, end );
192
210
193
211
cached_expr_->emplace_back (RespExpr::STRING);
194
212
cached_expr_->back ().u = Buffer{token_start, size_t (ptr - token_start)};
195
213
}
196
214
197
215
uint32_t last_consumed = ptr - str.data ();
198
- if (ptr == end) { // we have not finished parsing.
199
- if (ptr[-1 ] > 32 ) {
200
- // we stopped in the middle of the token.
201
- is_broken_token_ = true ;
202
- }
203
-
216
+ if (ptr == end) { // we have not finished parsing.
217
+ is_broken_token_ = ptr[-1 ] > 32 ; // we stopped in the middle of the token.
204
218
return {INPUT_PENDING, last_consumed};
205
219
}
206
220
207
- ++last_consumed; // consume the delimiter as well.
221
+ DCHECK_EQ (' \n ' , *ptr);
222
+
223
+ ++last_consumed; // consume \n as well.
208
224
state_ = CMD_COMPLETE_S;
209
225
210
226
return {OK, last_consumed};
211
227
}
212
228
213
- // Parse lines like:'$5\r\n' or '*2\r\n'
229
+ // Parse lines like:'$5\r\n' or '*2\r\n'. The first character is already consumed by the caller.
214
230
auto RedisParser::ParseLen (Buffer str, int64_t * res) -> ResultConsumed {
215
231
DCHECK (!str.empty ());
216
232
217
- DCHECK (str[0 ] == ' $' || str[0 ] == ' *' || str[0 ] == ' %' || str[0 ] == ' ~' );
218
-
219
233
const char * s = reinterpret_cast <const char *>(str.data ());
220
234
const char * pos = reinterpret_cast <const char *>(memchr (s, ' \n ' , str.size ()));
221
235
if (!pos) {
@@ -227,15 +241,15 @@ auto RedisParser::ParseLen(Buffer str, int64_t* res) -> ResultConsumed {
227
241
return {r, 0 };
228
242
}
229
243
244
+ unsigned consumed = pos - s + 1 ;
230
245
if (pos[-1 ] != ' \r ' ) {
231
- return {BAD_ARRAYLEN, 0 };
246
+ return {BAD_ARRAYLEN, consumed };
232
247
}
233
248
234
249
// Skip the first character and 2 last ones (\r\n).
235
- string_view len_token{s + 1 , size_t (pos - 1 - s)};
250
+ string_view len_token{s, size_t (pos - 1 - s)};
236
251
bool success = absl::SimpleAtoi (len_token, res);
237
252
238
- unsigned consumed = pos - s + 1 ;
239
253
if (success && *res >= -1 ) {
240
254
return ResultConsumed{OK, consumed};
241
255
}
@@ -268,11 +282,12 @@ auto RedisParser::ConsumeArrayLen(Buffer str) -> ResultConsumed {
268
282
return {BAD_STRING, res.second };
269
283
270
284
if (len <= 0 ) {
271
- cached_expr_-> emplace_back (len == - 1 ? RespExpr::NIL_ARRAY : RespExpr::ARRAY);
272
- if (len < 0 )
285
+ if (len < 0 ) {
286
+ cached_expr_-> emplace_back (RespExpr::NIL_ARRAY);
273
287
cached_expr_->back ().u .emplace <RespVec*>(nullptr ); // nil
274
- else {
288
+ } else {
275
289
static RespVec empty_vec;
290
+ cached_expr_->emplace_back (RespExpr::ARRAY);
276
291
cached_expr_->back ().u = &empty_vec;
277
292
}
278
293
if (parse_stack_.empty ()) {
@@ -293,9 +308,8 @@ auto RedisParser::ConsumeArrayLen(Buffer str) -> ResultConsumed {
293
308
arr->reserve (len);
294
309
cached_expr_->back ().u = arr;
295
310
cached_expr_ = arr;
296
- } else {
297
- state_ = PARSE_ARG_S;
298
311
}
312
+ state_ = PARSE_ARG_TYPE;
299
313
300
314
DVLOG (1 ) << " PushStack: (" << len << " , " << cached_expr_ << " )" ;
301
315
parse_stack_.emplace_back (len, cached_expr_);
@@ -306,14 +320,13 @@ auto RedisParser::ConsumeArrayLen(Buffer str) -> ResultConsumed {
306
320
auto RedisParser::ParseArg (Buffer str) -> ResultConsumed {
307
321
DCHECK (!str.empty ());
308
322
309
- char c = str[0 ];
310
- unsigned min_len = 3 + int (c != ' _' );
323
+ unsigned min_len = 2 + int (arg_c_ != ' _' );
311
324
312
325
if (str.size () < min_len) {
313
326
return {INPUT_PENDING, 0 };
314
327
}
315
328
316
- if (c == ' $' ) {
329
+ if (arg_c_ == ' $' ) {
317
330
int64_t len;
318
331
319
332
ResultConsumed res = ParseLen (str, &len);
@@ -338,33 +351,27 @@ auto RedisParser::ParseArg(Buffer str) -> ResultConsumed {
338
351
return {OK, res.second };
339
352
}
340
353
341
- if (server_mode_) {
342
- return {BAD_BULKLEN, 0 };
343
- }
344
-
345
- if (c == ' _' ) { // Resp3 NIL
346
- // '_','\r','\n'
347
- DCHECK_GE (str.size (), 3u );
348
-
349
- unsigned consumed = 3 ;
350
- if (str[1 ] != ' \r ' || str[2 ] != ' \n ' ) {
354
+ DCHECK (!server_mode_);
355
+ if (arg_c_ == ' _' ) { // Resp3 NIL
356
+ // '\r','\n'
357
+ if (str[0 ] != ' \r ' || str[1 ] != ' \n ' ) {
351
358
return {BAD_STRING, 0 };
352
359
}
353
360
354
361
cached_expr_->emplace_back (RespExpr::NIL);
355
362
cached_expr_->back ().u = Buffer{};
356
363
HandleFinishArg ();
357
- return {OK, consumed };
364
+ return {OK, 2 };
358
365
}
359
366
360
- if (c == ' *' ) {
367
+ if (arg_c_ == ' *' ) {
361
368
return ConsumeArrayLen (str);
362
369
}
363
370
364
- char * s = reinterpret_cast <char *>(str.data () + 1 );
365
- char * eol = reinterpret_cast <char *>(memchr (s, ' \n ' , str.size () - 1 ));
371
+ char * s = reinterpret_cast <char *>(str.data ());
372
+ char * eol = reinterpret_cast <char *>(memchr (s, ' \n ' , str.size ()));
366
373
367
- if (c == ' +' || c == ' -' ) { // Simple string or error.
374
+ if (arg_c_ == ' +' || arg_c_ == ' -' ) { // Simple string or error.
368
375
DCHECK (!server_mode_);
369
376
if (!eol) {
370
377
Result r = str.size () < 256 ? INPUT_PENDING : BAD_STRING;
@@ -374,9 +381,9 @@ auto RedisParser::ParseArg(Buffer str) -> ResultConsumed {
374
381
if (eol[-1 ] != ' \r ' )
375
382
return {BAD_STRING, 0 };
376
383
377
- cached_expr_->emplace_back (c == ' +' ? RespExpr::STRING : RespExpr::ERROR);
384
+ cached_expr_->emplace_back (arg_c_ == ' +' ? RespExpr::STRING : RespExpr::ERROR);
378
385
cached_expr_->back ().u = Buffer{reinterpret_cast <uint8_t *>(s), size_t ((eol - 1 ) - s)};
379
- } else if (c == ' :' ) {
386
+ } else if (arg_c_ == ' :' ) {
380
387
DCHECK (!server_mode_);
381
388
if (!eol) {
382
389
Result r = str.size () < 32 ? INPUT_PENDING : BAD_INT;
@@ -390,7 +397,7 @@ auto RedisParser::ParseArg(Buffer str) -> ResultConsumed {
390
397
391
398
cached_expr_->emplace_back (RespExpr::INT64);
392
399
cached_expr_->back ().u = ival;
393
- } else if (c == ' ,' ) {
400
+ } else if (arg_c_ == ' ,' ) {
394
401
DCHECK (!server_mode_);
395
402
if (!eol) {
396
403
Result r = str.size () < 32 ? INPUT_PENDING : BAD_DOUBLE;
@@ -410,7 +417,7 @@ auto RedisParser::ParseArg(Buffer str) -> ResultConsumed {
410
417
411
418
HandleFinishArg ();
412
419
413
- return {OK, (eol - s) + 2 };
420
+ return {OK, (eol - s) + 1 };
414
421
}
415
422
416
423
auto RedisParser::ConsumeBulk (Buffer str) -> ResultConsumed {
@@ -466,10 +473,10 @@ auto RedisParser::ConsumeBulk(Buffer str) -> ResultConsumed {
466
473
}
467
474
468
475
void RedisParser::HandleFinishArg () {
469
- state_ = PARSE_ARG_S;
470
476
DCHECK (!parse_stack_.empty ());
471
477
DCHECK_GT (parse_stack_.back ().first , 0u );
472
478
479
+ state_ = PARSE_ARG_TYPE;
473
480
while (true ) {
474
481
--parse_stack_.back ().first ;
475
482
if (parse_stack_.back ().first != 0 )
0 commit comments