/Users/deen/code/yugabyte-db/src/yb/yql/redis/redisserver/redis_parser.h
Line | Count | Source |
1 | | // Copyright (c) YugaByte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | // |
13 | | #ifndef YB_YQL_REDIS_REDISSERVER_REDIS_PARSER_H_ |
14 | | #define YB_YQL_REDIS_REDISSERVER_REDIS_PARSER_H_ |
15 | | |
16 | | #include <memory> |
17 | | #include <string> |
18 | | |
19 | | #include <boost/container/small_vector.hpp> |
20 | | |
21 | | #include "yb/client/client_fwd.h" |
22 | | |
23 | | #include "yb/util/status_fwd.h" |
24 | | #include "yb/util/net/socket.h" |
25 | | #include "yb/util/size_literals.h" |
26 | | |
27 | | #include "yb/yql/redis/redisserver/redis_fwd.h" |
28 | | |
29 | | namespace yb { |
30 | | namespace redisserver { |
31 | | |
32 | | constexpr size_t kMaxRedisValueSize = 512_MB; |
33 | | constexpr int64_t kNoneTtl = -1; |
34 | | |
35 | | CHECKED_STATUS ParseSet(client::YBRedisWriteOp *op, const RedisClientCommand& args); |
36 | | CHECKED_STATUS ParseGet(client::YBRedisReadOp* op, const RedisClientCommand& args); |
37 | | |
38 | | // TODO: make additional command support here |
39 | | |
40 | | // RedisParser is a finite state machine with memory. |
41 | | // It could remember current parsing state and be invoked again when new data arrives. |
42 | | // In this case parsing will be continued from the last position. |
43 | | class RedisParser { |
44 | | public: |
45 | | explicit RedisParser(const IoVecs& source) |
46 | 211k | : source_(source), full_size_(IoVecsFullSize(source)) { |
47 | 211k | } |
48 | | |
49 | | void SetArgs(boost::container::small_vector_base<Slice>* args); |
50 | | |
51 | | // Begin of input is going to be consumed, so we should adjust our pointers. |
52 | | // Since the beginning of input is being consumed by shifting the remaining bytes to the |
53 | | // beginning of the buffer. |
54 | | void Consume(size_t count); |
55 | | |
56 | | // New data arrived, so update the end of available bytes. |
57 | | void Update(const IoVecs& source); |
58 | | |
59 | | // Parse next command. |
60 | | Result<size_t> NextCommand(); |
61 | | |
62 | | private: |
63 | | // Redis command could be of 2 types. |
64 | | // First one is just single line that terminates with \r\n. |
65 | | // Second one is bulk command, that has form: |
66 | | // *<BULK_ARGUMENTS>\r\n |
67 | | // $<SIZE_IN_BYTES_OF_BULK_ARGUMENT_1>\r\n |
68 | | // <BULK_ARGUMENT_1>\r\n |
69 | | // ... |
70 | | // $<SIZE_IN_BYTES_OF_BULK_ARGUMENT_N>\r\n |
71 | | // <BULK_ARGUMENT_N>\r\n |
72 | | enum class State { |
73 | | // Initial state of parser. |
74 | | INITIAL, |
75 | | // We determined that command is single line command and waiting for \r\n |
76 | | SINGLE_LINE, |
77 | | // We are parsing the first line of a bulk command. |
78 | | BULK_HEADER, |
79 | | // We are parsing bulk argument size. arguments_left_ has valid value. |
80 | | BULK_ARGUMENT_SIZE, |
81 | | // We are parsing bulk argument body. arguments_left_, current_argument_size_ have valid values. |
82 | | BULK_ARGUMENT_BODY, |
83 | | // Just mark that we finished parsing of command. Will become INITIAL in NextCommand. |
84 | | FINISHED, |
85 | | }; |
86 | | |
87 | | CHECKED_STATUS AdvanceToNextToken(); |
88 | | CHECKED_STATUS Initial(); |
89 | | CHECKED_STATUS SingleLine(); |
90 | | CHECKED_STATUS BulkHeader(); |
91 | | CHECKED_STATUS BulkArgumentSize(); |
92 | | CHECKED_STATUS BulkArgumentBody(); |
93 | | CHECKED_STATUS FindEndOfLine(); |
94 | | |
95 | | // Parses number with specified bounds. |
96 | | // Number is located in separate line, and contain prefix before actual number. |
97 | | // Line starts at token_begin_ and pos_ is a start of next line. |
98 | | Result<ptrdiff_t> ParseNumber(char prefix, |
99 | | ptrdiff_t min, |
100 | | ptrdiff_t max, |
101 | | const char* name); |
102 | | |
103 | | // Returns pointer to byte with specified offset in all iovecs of source_. |
104 | | // Pointer byte is valid, the end of valid range should be determined separately if required. |
105 | | const char* offset_to_pointer(size_t offset) const; |
106 | | |
107 | | std::pair<size_t, size_t> offset_to_idx_and_local_offset(size_t offset) const; |
108 | | |
109 | 7.31M | char char_at_offset(size_t offset) const { |
110 | 7.31M | return *offset_to_pointer(offset); |
111 | 7.31M | } |
112 | | |
113 | | static constexpr size_t kNoToken = std::numeric_limits<size_t>::max(); |
114 | | |
115 | | // Data to parse. |
116 | | IoVecs source_; |
117 | | |
118 | | size_t full_size_; |
119 | | |
120 | | // Current parsing position. |
121 | | size_t pos_ = 0; |
122 | | |
123 | | // Command arguments. |
124 | | boost::container::small_vector_base<Slice>* args_ = nullptr; |
125 | | |
126 | | // Parser state. |
127 | | State state_ = State::INITIAL; |
128 | | |
129 | | // Beginning of last token. |
130 | | size_t token_begin_ = kNoToken; |
131 | | |
132 | | // Mark that current token is incomplete. |
133 | | bool incomplete_ = false; |
134 | | |
135 | | // Number of arguments left in bulk command. |
136 | | size_t arguments_left_ = 0; |
137 | | |
138 | | // Size of the current argument in bulk command. |
139 | | size_t current_argument_size_ = 0; |
140 | | |
141 | | std::vector<char> number_buffer_; |
142 | | }; |
143 | | |
144 | | } // namespace redisserver |
145 | | } // namespace yb |
146 | | |
147 | | #endif // YB_YQL_REDIS_REDISSERVER_REDIS_PARSER_H_ |