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