OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h" | |
6 | |
7 #include <assert.h> | |
8 #include <stdio.h> | |
9 #include <string.h> | |
10 | |
11 #include <limits> | |
12 #include <map> | |
13 #include <utility> | |
14 | |
15 #include "mojo/public/c/system/macros.h" | |
16 | |
17 namespace mojo { | |
18 namespace test { | |
19 namespace { | |
20 | |
21 class ValidationTestInputParser { | |
22 public: | |
23 ValidationTestInputParser(const std::string& input, | |
24 std::vector<uint8_t>* parsed_input, | |
25 std::string* error_message); | |
26 ~ValidationTestInputParser(); | |
27 | |
28 bool Run(); | |
29 | |
30 private: | |
31 struct DataType; | |
32 | |
33 typedef std::pair<const char*, const char*> Range; | |
34 | |
35 typedef bool (ValidationTestInputParser::*ParseDataFunc)( | |
36 const DataType& type, const std::string& value_string); | |
37 | |
38 struct DataType { | |
39 const char* name; | |
40 size_t name_size; | |
41 size_t data_size; | |
42 ParseDataFunc parse_data_func; | |
43 }; | |
44 | |
45 // A dist4/8 item that hasn't been matched with an anchr item. | |
46 struct PendingDistanceItem { | |
47 // Where this data item is located in |parsed_input_|. | |
48 size_t pos; | |
49 // Either 4 or 8 (bytes). | |
50 size_t data_size; | |
51 }; | |
52 | |
53 bool GetNextItem(Range* range); | |
54 | |
55 bool ParseItem(const Range& range); | |
56 | |
57 bool ParseUnsignedInteger(const DataType& type, | |
58 const std::string& value_string); | |
59 bool ParseSignedInteger(const DataType& type, | |
60 const std::string& value_string); | |
61 bool ParseFloat(const DataType& type, const std::string& value_string); | |
62 bool ParseDouble(const DataType& type, const std::string& value_string); | |
63 bool ParseBinarySequence(const DataType& type, | |
64 const std::string& value_string); | |
65 bool ParseDistance(const DataType& type, const std::string& value_string); | |
66 bool ParseAnchor(const DataType& type, const std::string& value_string); | |
67 | |
68 bool StartsWith(const Range& range, const char* prefix, size_t prefix_length); | |
69 | |
70 template <typename T> | |
71 void AppendData(T data) { | |
72 size_t pos = parsed_input_->size(); | |
73 parsed_input_->resize(pos + sizeof(T)); | |
74 memcpy(&(*parsed_input_)[pos], &data, sizeof(T)); | |
75 } | |
76 | |
77 template <typename TargetType, typename InputType> | |
78 bool ConvertAndAppendData(InputType value) { | |
79 if (value > std::numeric_limits<TargetType>::max() || | |
80 value < std::numeric_limits<TargetType>::min()) { | |
81 return false; | |
82 } | |
83 AppendData(static_cast<TargetType>(value)); | |
84 return true; | |
85 } | |
86 | |
87 template <typename TargetType, typename InputType> | |
88 bool ConvertAndFillData(size_t pos, InputType value) { | |
89 if (value > std::numeric_limits<TargetType>::max() || | |
90 value < std::numeric_limits<TargetType>::min()) { | |
91 return false; | |
92 } | |
93 TargetType target_value = static_cast<TargetType>(value); | |
94 assert(pos + sizeof(TargetType) <= parsed_input_->size()); | |
95 memcpy(&(*parsed_input_)[pos], &target_value, sizeof(TargetType)); | |
96 return true; | |
97 } | |
98 | |
99 static const DataType kDataTypes[]; | |
100 static const size_t kDataTypeCount; | |
101 | |
102 const std::string& input_; | |
103 size_t input_cursor_; | |
104 | |
105 std::vector<uint8_t>* parsed_input_; | |
106 std::string* error_message_; | |
107 | |
108 std::map<std::string, PendingDistanceItem> pending_distance_items_; | |
109 }; | |
110 | |
111 #define DATA_TYPE(name, data_size, parse_data_func) \ | |
112 {name, sizeof(name) - 1, data_size, parse_data_func} | |
113 | |
114 const ValidationTestInputParser::DataType | |
115 ValidationTestInputParser::kDataTypes[] = { | |
116 DATA_TYPE("[u1]", 1, &ValidationTestInputParser::ParseUnsignedInteger), | |
117 DATA_TYPE("[u2]", 2, &ValidationTestInputParser::ParseUnsignedInteger), | |
118 DATA_TYPE("[u4]", 4, &ValidationTestInputParser::ParseUnsignedInteger), | |
119 DATA_TYPE("[u8]", 8, &ValidationTestInputParser::ParseUnsignedInteger), | |
120 DATA_TYPE("[s1]", 1, &ValidationTestInputParser::ParseSignedInteger), | |
121 DATA_TYPE("[s2]", 2, &ValidationTestInputParser::ParseSignedInteger), | |
122 DATA_TYPE("[s4]", 4, &ValidationTestInputParser::ParseSignedInteger), | |
123 DATA_TYPE("[s8]", 8, &ValidationTestInputParser::ParseSignedInteger), | |
124 DATA_TYPE("[b]", 1, &ValidationTestInputParser::ParseBinarySequence), | |
125 DATA_TYPE("[f]", 4, &ValidationTestInputParser::ParseFloat), | |
126 DATA_TYPE("[d]", 8, &ValidationTestInputParser::ParseDouble), | |
127 DATA_TYPE("[dist4]", 4, &ValidationTestInputParser::ParseDistance), | |
128 DATA_TYPE("[dist8]", 8, &ValidationTestInputParser::ParseDistance), | |
129 DATA_TYPE("[anchr]", 0, &ValidationTestInputParser::ParseAnchor) | |
130 }; | |
131 | |
132 const size_t ValidationTestInputParser::kDataTypeCount = | |
133 sizeof(ValidationTestInputParser::kDataTypes) / | |
134 sizeof(ValidationTestInputParser::kDataTypes[0]); | |
135 | |
136 ValidationTestInputParser::ValidationTestInputParser( | |
137 const std::string& input, | |
138 std::vector<uint8_t>* parsed_input, | |
139 std::string* error_message) | |
140 : input_(input), | |
141 input_cursor_(0), | |
142 parsed_input_(parsed_input), | |
143 error_message_(error_message) { | |
144 assert(parsed_input_); | |
145 assert(error_message_); | |
146 parsed_input_->clear(); | |
147 error_message_->clear(); | |
148 } | |
149 | |
150 ValidationTestInputParser::~ValidationTestInputParser() { | |
151 } | |
152 | |
153 bool ValidationTestInputParser::Run() { | |
154 Range range; | |
155 bool result = true; | |
156 while (GetNextItem(&range) && result) | |
157 result = ParseItem(range); | |
158 | |
159 if (!result) { | |
160 *error_message_ = "Error occurred when parsing " + | |
161 std::string(range.first, range.second); | |
162 } else if (!pending_distance_items_.empty()) { | |
163 // We have parsed all the contents in |input_| successfully, but there are | |
164 // unmatched dist4/8 items. | |
165 *error_message_ = "Error occurred when matching [dist4/8] and [anchr]."; | |
166 result = false; | |
167 } | |
168 if (!result) | |
169 parsed_input_->clear(); | |
170 else | |
171 assert(error_message_->empty()); | |
172 | |
173 return result; | |
174 } | |
175 | |
176 bool ValidationTestInputParser::GetNextItem(Range* range) { | |
177 const char kWhitespaceChars[] = " \t\n\r"; | |
178 const char kItemDelimiters[] = " \t\n\r/"; | |
179 const char kEndOfLineChars[] = "\n\r"; | |
180 while (true) { | |
181 // Skip leading whitespaces. | |
182 // If there are no non-whitespace characters left, |input_cursor_| will be | |
183 // set to std::npos. | |
184 input_cursor_ = input_.find_first_not_of(kWhitespaceChars, input_cursor_); | |
185 | |
186 if (input_cursor_ >= input_.size()) | |
187 return false; | |
188 | |
189 if (StartsWith(Range(&input_[0] + input_cursor_, | |
190 &input_[0] + input_.size()), | |
191 "//", 2)) { | |
192 // Skip contents until the end of the line. | |
193 input_cursor_ = input_.find_first_of(kEndOfLineChars, input_cursor_); | |
194 } else { | |
195 range->first = &input_[0] + input_cursor_; | |
196 input_cursor_ = input_.find_first_of(kItemDelimiters, input_cursor_); | |
197 range->second = input_cursor_ >= input_.size() ? | |
198 &input_[0] + input_.size() : &input_[0] + input_cursor_; | |
199 return true; | |
200 } | |
201 } | |
202 return false; | |
203 } | |
204 | |
205 bool ValidationTestInputParser::ParseItem(const Range& range) { | |
206 for (size_t i = 0; i < kDataTypeCount; ++i) { | |
207 if (StartsWith(range, kDataTypes[i].name, kDataTypes[i].name_size)) { | |
208 return (this->*kDataTypes[i].parse_data_func)( | |
209 kDataTypes[i], | |
210 std::string(range.first + kDataTypes[i].name_size, range.second)); | |
211 } | |
212 } | |
213 | |
214 // "[u1]" is optional. | |
215 return ParseUnsignedInteger(kDataTypes[0], | |
216 std::string(range.first, range.second)); | |
217 } | |
218 | |
219 bool ValidationTestInputParser::ParseUnsignedInteger( | |
220 const DataType& type, const std::string& value_string) { | |
221 uintmax_t value; | |
222 const char* format = NULL; | |
223 if (value_string.find_first_of("xX") != std::string::npos) | |
224 format = "%jx"; | |
225 else | |
226 format = "%ju"; | |
227 if (sscanf(value_string.c_str(), format, &value) != 1) | |
228 return false; | |
229 | |
230 switch (type.data_size) { | |
231 case 1: | |
232 return ConvertAndAppendData<uint8_t>(value); | |
233 case 2: | |
234 return ConvertAndAppendData<uint16_t>(value); | |
235 case 4: | |
236 return ConvertAndAppendData<uint32_t>(value); | |
237 case 8: | |
238 return ConvertAndAppendData<uint64_t>(value); | |
239 default: | |
240 assert(false); | |
241 return false; | |
242 } | |
243 } | |
244 | |
245 bool ValidationTestInputParser::ParseSignedInteger( | |
246 const DataType& type, const std::string& value_string) { | |
247 intmax_t value; | |
248 if (sscanf(value_string.c_str(), "%ji", &value) != 1) | |
249 return false; | |
250 | |
251 switch (type.data_size) { | |
252 case 1: | |
253 return ConvertAndAppendData<int8_t>(value); | |
254 case 2: | |
255 return ConvertAndAppendData<int16_t>(value); | |
256 case 4: | |
257 return ConvertAndAppendData<int32_t>(value); | |
258 case 8: | |
259 return ConvertAndAppendData<int64_t>(value); | |
260 default: | |
261 assert(false); | |
262 return false; | |
263 } | |
264 } | |
265 | |
266 bool ValidationTestInputParser::ParseFloat( | |
267 const DataType& type, const std::string& value_string) { | |
268 MOJO_COMPILE_ASSERT(sizeof(float) == 4, float_size_is_not_4); | |
269 | |
270 float value; | |
271 if (sscanf(value_string.c_str(), "%f", &value) != 1) | |
272 return false; | |
273 | |
274 AppendData(value); | |
275 return true; | |
276 } | |
277 | |
278 bool ValidationTestInputParser::ParseDouble(const DataType& type, | |
279 const std::string& value_string) { | |
280 MOJO_COMPILE_ASSERT(sizeof(double) == 8, double_size_is_not_8); | |
281 | |
282 double value; | |
283 if (sscanf(value_string.c_str(), "%lf", &value) != 1) | |
284 return false; | |
285 | |
286 AppendData(value); | |
287 return true; | |
288 } | |
289 | |
290 bool ValidationTestInputParser::ParseBinarySequence( | |
291 const DataType& type, const std::string& value_string) { | |
292 if (value_string.size() != 8) | |
293 return false; | |
294 | |
295 uint8_t value = 0; | |
296 for (std::string::const_iterator iter = value_string.begin(); | |
297 iter != value_string.end(); | |
298 ++iter) { | |
299 value <<= 1; | |
300 if (*iter == '1') | |
301 value++; | |
302 else if (*iter != '0') | |
303 return false; | |
304 } | |
305 AppendData(value); | |
306 return true; | |
307 } | |
308 | |
309 bool ValidationTestInputParser::ParseDistance(const DataType& type, | |
310 const std::string& value_string) { | |
311 if (pending_distance_items_.find(value_string) != | |
312 pending_distance_items_.end()) | |
313 return false; | |
314 | |
315 PendingDistanceItem item = {parsed_input_->size(), type.data_size}; | |
316 parsed_input_->resize(parsed_input_->size() + type.data_size); | |
317 pending_distance_items_[value_string] = item; | |
318 | |
319 return true; | |
320 } | |
321 | |
322 bool ValidationTestInputParser::ParseAnchor(const DataType& type, | |
323 const std::string& value_string) { | |
324 std::map<std::string, PendingDistanceItem>::iterator iter = | |
325 pending_distance_items_.find(value_string); | |
326 if (iter == pending_distance_items_.end()) | |
327 return false; | |
328 | |
329 PendingDistanceItem dist_item = iter->second; | |
330 pending_distance_items_.erase(iter); | |
331 | |
332 size_t distance = parsed_input_->size() - dist_item.pos; | |
333 switch (dist_item.data_size) { | |
334 case 4: | |
335 return ConvertAndFillData<uint32_t>(dist_item.pos, distance); | |
336 case 8: | |
337 return ConvertAndFillData<uint64_t>(dist_item.pos, distance); | |
338 default: | |
339 assert(false); | |
340 return false; | |
341 } | |
342 } | |
343 | |
344 bool ValidationTestInputParser::StartsWith(const Range& range, | |
345 const char* prefix, | |
346 size_t prefix_length) { | |
347 if (range.second - range.first < prefix_length) | |
348 return false; | |
darin (slow to review)
2014/06/08 03:44:33
nit: indentation
yzshen1
2014/06/08 06:59:13
Done.
| |
349 | |
350 return memcmp(range.first, prefix, prefix_length) == 0; | |
351 } | |
352 | |
353 } // namespace | |
354 | |
355 bool ParseValidationTestInput(const std::string& input, | |
356 std::vector<uint8_t>* parsed_input, | |
357 std::string* error_message) { | |
358 ValidationTestInputParser parser(input, parsed_input, error_message); | |
359 return parser.Run(); | |
360 } | |
361 | |
362 } // namespace test | |
363 } // namespace mojo | |
OLD | NEW |