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