Repository navigation
Expand file tree
/
Copy pathresp.cpp
More file actions
160 lines (146 loc) · 5.15 KB
/
Copy pathresp.cpp
File metadata and controls
160 lines (146 loc) · 5.15 KB
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
#include "resp.h"
#include <array>
#include <limits>
#include <stdexcept>
#include <utility>
namespace {
struct LengthResult {
RespParseStatus status;
std::size_t next = 0;
std::size_t value = 0;
const char *error = "";
};
LengthResult parse_length(std::string_view input, std::size_t position,
char marker, std::size_t maximum) {
const auto start = position;
if (position >= resp_max_frame_size) {
return {RespParseStatus::Error, 0, 0, "RESP request exceeds maximum size"};
}
if (position >= input.size()) {
return {RespParseStatus::Incomplete};
}
if (input[position] != marker) {
return {RespParseStatus::Error, 0, 0, "Expected RESP array of bulk strings"};
}
++position;
std::size_t value = 0;
bool has_digits = false;
while (position < input.size()) {
if (position - start >= resp_max_header_size) {
return {RespParseStatus::Error, 0, 0, "RESP length header exceeds maximum size"};
}
if (position >= resp_max_frame_size) {
return {RespParseStatus::Error, 0, 0, "RESP request exceeds maximum size"};
}
const char byte = input[position];
if (byte == '\r') {
if (!has_digits) {
return {RespParseStatus::Error, 0, 0, "Invalid RESP length"};
}
if (position - start + 2 > resp_max_header_size) {
return {RespParseStatus::Error, 0, 0,
"RESP length header exceeds maximum size"};
}
if (position + 2 > resp_max_frame_size) {
return {RespParseStatus::Error, 0, 0, "RESP request exceeds maximum size"};
}
if (position + 1 >= input.size()) {
return {RespParseStatus::Incomplete};
}
if (input[position + 1] != '\n') {
return {RespParseStatus::Error, 0, 0, "Expected CRLF after RESP length"};
}
return {RespParseStatus::Complete, position + 2, value};
}
if (byte < '0' || byte > '9') {
return {RespParseStatus::Error, 0, 0, "Invalid RESP length"};
}
const auto digit = static_cast<std::size_t>(byte - '0');
if (value > (std::numeric_limits<std::size_t>::max() - digit) / 10) {
return {RespParseStatus::Error, 0, 0, "RESP length overflow"};
}
value = value * 10 + digit;
if (value > maximum) {
return {RespParseStatus::Error, 0, 0, "RESP length exceeds maximum size"};
}
has_digits = true;
++position;
}
if (position - start >= resp_max_header_size) {
return {RespParseStatus::Error, 0, 0, "RESP length header exceeds maximum size"};
}
if (position >= resp_max_frame_size) {
return {RespParseStatus::Error, 0, 0, "RESP request exceeds maximum size"};
}
return {RespParseStatus::Incomplete};
}
RespParseResult parse_error(const char *message) {
return {RespParseStatus::Error, 0, {}, message};
}
void validate_line(const std::string &value) {
if (value.find_first_of("\r\n") != std::string::npos) {
throw std::invalid_argument("RESP line must not contain CR or LF");
}
}
}
RespParseResult parse_resp_request(std::string_view input) {
const auto array = parse_length(input, 0, '*', 64);
if (array.status == RespParseStatus::Error) {
return parse_error(array.error);
}
if (array.status == RespParseStatus::Incomplete) {
return {RespParseStatus::Incomplete, 0, {}, {}};
}
if (array.value == 0) {
return parse_error("RESP command must contain at least one argument");
}
std::array<std::pair<std::size_t, std::size_t>, 64> arguments;
std::size_t position = array.next;
for (std::size_t index = 0; index < array.value; ++index) {
const auto bulk = parse_length(input, position, '$', resp_max_bulk_size);
if (bulk.status == RespParseStatus::Error) {
return parse_error(bulk.error);
}
if (bulk.status == RespParseStatus::Incomplete) {
return {RespParseStatus::Incomplete, 0, {}, {}};
}
if (bulk.next > resp_max_frame_size - 2 ||
bulk.value > resp_max_frame_size - bulk.next - 2) {
return parse_error("RESP request exceeds maximum size");
}
const auto data_end = bulk.next + bulk.value;
if (data_end >= input.size()) {
return {RespParseStatus::Incomplete, 0, {}, {}};
}
if (input[data_end] != '\r') {
return parse_error("Expected CRLF after RESP bulk string");
}
if (data_end + 1 >= input.size()) {
return {RespParseStatus::Incomplete, 0, {}, {}};
}
if (input[data_end + 1] != '\n') {
return parse_error("Expected CRLF after RESP bulk string");
}
arguments[index] = {bulk.next, bulk.value};
position = data_end + 2;
}
RespParseResult result{RespParseStatus::Complete, position, {}, {}};
result.arguments.reserve(array.value);
for (std::size_t index = 0; index < array.value; ++index) {
result.arguments.emplace_back(
input.substr(arguments[index].first, arguments[index].second));
}
return result;
}
std::string resp_simple(const std::string &value) {
validate_line(value);
return "+" + value + "\r\n";
}
std::string resp_error(const std::string &value) {
validate_line(value);
return "-" + value + "\r\n";
}
std::string resp_bulk(const std::string &value) {
return "$" + std::to_string(value.size()) + "\r\n" + value + "\r\n";
}
std::string resp_null() { return "$-1\r\n"; }