forked from scylladb/scylladb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprotocol_parser.rl
137 lines (114 loc) · 2.9 KB
/
protocol_parser.rl
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
/*
* Copyright (C) 2019 pengjian.uestc @ gmail.com
*/
/*
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
*/
#include <memory>
#include <iostream>
#include <algorithm>
#include <functional>
#include "bytes.hh"
#include "redis/request.hh"
#include <seastar/core/ragel.hh>
using namespace seastar;
using namespace redis;
%%{
machine redis_resp_protocol;
access _fsm_;
action mark {
g.mark_start(p);
}
action start_blob {
g.mark_start(p);
_bytes_left = _bytes_count;
}
action start_command {
g.mark_start(p);
_bytes_left = _bytes_count;
}
action advance_blob {
auto len = std::min(static_cast<uint32_t>(pe - p), _bytes_left);
_bytes_left -= len;
p += len;
if (_bytes_left == 0) {
_req._args.push_back(str());
p--;
fret;
}
p--;
}
action advance_command {
auto len = std::min(static_cast<uint32_t>(pe - p), _bytes_left);
_bytes_left -= len;
p += len;
if (_bytes_left == 0) {
_req._command = str();
p--;
fret;
}
p--;
}
crlf = '\r\n';
u32 = digit+ >{ _u32 = 0;} ${ _u32 *= 10; _u32 += fc - '0';};
args_count = '*' u32 crlf ${_req._args_count = _u32 - 1;};
blob := any+ >start_blob $advance_blob;
command := any+ >start_command $advance_command;
arg = '$' u32 crlf ${ _bytes_count = _u32;};
main := (args_count (arg @{fcall command; } crlf) (arg @{fcall blob; } crlf)+) ${_req._state = request_state::ok;} >eof{_req._state = request_state::eof;};
prepush {
prepush();
}
postpop {
postpop();
}
}%%
class redis_protocol_parser : public ragel_parser_base<redis_protocol_parser> {
%% write data nofinal noprefix;
public:
redis::request _req;
uint32_t _u32;
uint32_t _bytes_count;
uint32_t _bytes_left;
public:
virtual void init() {
init_base();
_req._state = request_state::error;
_req._args.clear();
_req._args_count = 0;
_bytes_left = 0;
_bytes_count = 0;
%% write init;
}
char* parse(char* p, char* pe, char* eof) {
sstring_builder::guard g(_builder, p, pe);
auto str = [this, &g, &p] {
g.mark_end(p);
auto s = get_str();
return to_bytes(s);
};
%% write exec;
// does not reach to the tail of the message, continue reading
if (_bytes_left || _req._args_count - _req._args.size()) {
return nullptr;
}
if (_req._state != request_state::error) {
return p;
}
if (p != pe) {
p = pe;
return p;
}
return nullptr;
}
bool eof() const {
return _req._state == request_state::eof;
}
bool failed() const {
return _req._state == request_state::error;
}
redis::request& get_request() {
std::transform(_req._command.begin(), _req._command.end(), _req._command.begin(), ::tolower);
return _req;
}
};