Home | History | Annotate | Download | only in tests
      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>
     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>
     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>
     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 
    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 
    164 ValidationTestInputParser::~ValidationTestInputParser() {
    165 }
    166 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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