| Index: mojo/public/cpp/bindings/tests/validation_test_input_parser.cc
|
| diff --git a/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc b/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..382c4847a0709a8a2f93393020317e52ff5a134b
|
| --- /dev/null
|
| +++ b/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc
|
| @@ -0,0 +1,363 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h"
|
| +
|
| +#include <assert.h>
|
| +#include <stdio.h>
|
| +#include <string.h>
|
| +
|
| +#include <limits>
|
| +#include <map>
|
| +#include <utility>
|
| +
|
| +#include "mojo/public/c/system/macros.h"
|
| +
|
| +namespace mojo {
|
| +namespace test {
|
| +namespace {
|
| +
|
| +class ValidationTestInputParser {
|
| + public:
|
| + ValidationTestInputParser(const std::string& input,
|
| + std::vector<uint8_t>* parsed_input,
|
| + std::string* error_message);
|
| + ~ValidationTestInputParser();
|
| +
|
| + bool Run();
|
| +
|
| + private:
|
| + struct DataType;
|
| +
|
| + typedef std::pair<const char*, const char*> Range;
|
| +
|
| + typedef bool (ValidationTestInputParser::*ParseDataFunc)(
|
| + const DataType& type, const std::string& value_string);
|
| +
|
| + struct DataType {
|
| + const char* name;
|
| + size_t name_size;
|
| + size_t data_size;
|
| + ParseDataFunc parse_data_func;
|
| + };
|
| +
|
| + // A dist4/8 item that hasn't been matched with an anchr item.
|
| + struct PendingDistanceItem {
|
| + // Where this data item is located in |parsed_input_|.
|
| + size_t pos;
|
| + // Either 4 or 8 (bytes).
|
| + size_t data_size;
|
| + };
|
| +
|
| + bool GetNextItem(Range* range);
|
| +
|
| + bool ParseItem(const Range& range);
|
| +
|
| + bool ParseUnsignedInteger(const DataType& type,
|
| + const std::string& value_string);
|
| + bool ParseSignedInteger(const DataType& type,
|
| + const std::string& value_string);
|
| + bool ParseFloat(const DataType& type, const std::string& value_string);
|
| + bool ParseDouble(const DataType& type, const std::string& value_string);
|
| + bool ParseBinarySequence(const DataType& type,
|
| + const std::string& value_string);
|
| + bool ParseDistance(const DataType& type, const std::string& value_string);
|
| + bool ParseAnchor(const DataType& type, const std::string& value_string);
|
| +
|
| + bool StartsWith(const Range& range, const char* prefix, size_t prefix_length);
|
| +
|
| + template <typename T>
|
| + void AppendData(T data) {
|
| + size_t pos = parsed_input_->size();
|
| + parsed_input_->resize(pos + sizeof(T));
|
| + memcpy(&(*parsed_input_)[pos], &data, sizeof(T));
|
| + }
|
| +
|
| + template <typename TargetType, typename InputType>
|
| + bool ConvertAndAppendData(InputType value) {
|
| + if (value > std::numeric_limits<TargetType>::max() ||
|
| + value < std::numeric_limits<TargetType>::min()) {
|
| + return false;
|
| + }
|
| + AppendData(static_cast<TargetType>(value));
|
| + return true;
|
| + }
|
| +
|
| + template <typename TargetType, typename InputType>
|
| + bool ConvertAndFillData(size_t pos, InputType value) {
|
| + if (value > std::numeric_limits<TargetType>::max() ||
|
| + value < std::numeric_limits<TargetType>::min()) {
|
| + return false;
|
| + }
|
| + TargetType target_value = static_cast<TargetType>(value);
|
| + assert(pos + sizeof(TargetType) <= parsed_input_->size());
|
| + memcpy(&(*parsed_input_)[pos], &target_value, sizeof(TargetType));
|
| + return true;
|
| + }
|
| +
|
| + static const DataType kDataTypes[];
|
| + static const size_t kDataTypeCount;
|
| +
|
| + const std::string& input_;
|
| + size_t input_cursor_;
|
| +
|
| + std::vector<uint8_t>* parsed_input_;
|
| + std::string* error_message_;
|
| +
|
| + std::map<std::string, PendingDistanceItem> pending_distance_items_;
|
| +};
|
| +
|
| +#define DATA_TYPE(name, data_size, parse_data_func) \
|
| + {name, sizeof(name) - 1, data_size, parse_data_func}
|
| +
|
| +const ValidationTestInputParser::DataType
|
| + ValidationTestInputParser::kDataTypes[] = {
|
| + DATA_TYPE("[u1]", 1, &ValidationTestInputParser::ParseUnsignedInteger),
|
| + DATA_TYPE("[u2]", 2, &ValidationTestInputParser::ParseUnsignedInteger),
|
| + DATA_TYPE("[u4]", 4, &ValidationTestInputParser::ParseUnsignedInteger),
|
| + DATA_TYPE("[u8]", 8, &ValidationTestInputParser::ParseUnsignedInteger),
|
| + DATA_TYPE("[s1]", 1, &ValidationTestInputParser::ParseSignedInteger),
|
| + DATA_TYPE("[s2]", 2, &ValidationTestInputParser::ParseSignedInteger),
|
| + DATA_TYPE("[s4]", 4, &ValidationTestInputParser::ParseSignedInteger),
|
| + DATA_TYPE("[s8]", 8, &ValidationTestInputParser::ParseSignedInteger),
|
| + DATA_TYPE("[b]", 1, &ValidationTestInputParser::ParseBinarySequence),
|
| + DATA_TYPE("[f]", 4, &ValidationTestInputParser::ParseFloat),
|
| + DATA_TYPE("[d]", 8, &ValidationTestInputParser::ParseDouble),
|
| + DATA_TYPE("[dist4]", 4, &ValidationTestInputParser::ParseDistance),
|
| + DATA_TYPE("[dist8]", 8, &ValidationTestInputParser::ParseDistance),
|
| + DATA_TYPE("[anchr]", 0, &ValidationTestInputParser::ParseAnchor)
|
| +};
|
| +
|
| +const size_t ValidationTestInputParser::kDataTypeCount =
|
| + sizeof(ValidationTestInputParser::kDataTypes) /
|
| + sizeof(ValidationTestInputParser::kDataTypes[0]);
|
| +
|
| +ValidationTestInputParser::ValidationTestInputParser(
|
| + const std::string& input,
|
| + std::vector<uint8_t>* parsed_input,
|
| + std::string* error_message)
|
| + : input_(input),
|
| + input_cursor_(0),
|
| + parsed_input_(parsed_input),
|
| + error_message_(error_message) {
|
| + assert(parsed_input_);
|
| + assert(error_message_);
|
| + parsed_input_->clear();
|
| + error_message_->clear();
|
| +}
|
| +
|
| +ValidationTestInputParser::~ValidationTestInputParser() {
|
| +}
|
| +
|
| +bool ValidationTestInputParser::Run() {
|
| + Range range;
|
| + bool result = true;
|
| + while (result && GetNextItem(&range))
|
| + result = ParseItem(range);
|
| +
|
| + if (!result) {
|
| + *error_message_ = "Error occurred when parsing " +
|
| + std::string(range.first, range.second);
|
| + } else if (!pending_distance_items_.empty()) {
|
| + // We have parsed all the contents in |input_| successfully, but there are
|
| + // unmatched dist4/8 items.
|
| + *error_message_ = "Error occurred when matching [dist4/8] and [anchr].";
|
| + result = false;
|
| + }
|
| + if (!result)
|
| + parsed_input_->clear();
|
| + else
|
| + assert(error_message_->empty());
|
| +
|
| + return result;
|
| +}
|
| +
|
| +bool ValidationTestInputParser::GetNextItem(Range* range) {
|
| + const char kWhitespaceChars[] = " \t\n\r";
|
| + const char kItemDelimiters[] = " \t\n\r/";
|
| + const char kEndOfLineChars[] = "\n\r";
|
| + while (true) {
|
| + // Skip leading whitespaces.
|
| + // If there are no non-whitespace characters left, |input_cursor_| will be
|
| + // set to std::npos.
|
| + input_cursor_ = input_.find_first_not_of(kWhitespaceChars, input_cursor_);
|
| +
|
| + if (input_cursor_ >= input_.size())
|
| + return false;
|
| +
|
| + if (StartsWith(Range(&input_[0] + input_cursor_,
|
| + &input_[0] + input_.size()),
|
| + "//", 2)) {
|
| + // Skip contents until the end of the line.
|
| + input_cursor_ = input_.find_first_of(kEndOfLineChars, input_cursor_);
|
| + } else {
|
| + range->first = &input_[0] + input_cursor_;
|
| + input_cursor_ = input_.find_first_of(kItemDelimiters, input_cursor_);
|
| + range->second = input_cursor_ >= input_.size() ?
|
| + &input_[0] + input_.size() : &input_[0] + input_cursor_;
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +bool ValidationTestInputParser::ParseItem(const Range& range) {
|
| + for (size_t i = 0; i < kDataTypeCount; ++i) {
|
| + if (StartsWith(range, kDataTypes[i].name, kDataTypes[i].name_size)) {
|
| + return (this->*kDataTypes[i].parse_data_func)(
|
| + kDataTypes[i],
|
| + std::string(range.first + kDataTypes[i].name_size, range.second));
|
| + }
|
| + }
|
| +
|
| + // "[u1]" is optional.
|
| + return ParseUnsignedInteger(kDataTypes[0],
|
| + std::string(range.first, range.second));
|
| +}
|
| +
|
| +bool ValidationTestInputParser::ParseUnsignedInteger(
|
| + const DataType& type, const std::string& value_string) {
|
| + unsigned long long int value;
|
| + const char* format = NULL;
|
| + if (value_string.find_first_of("xX") != std::string::npos)
|
| + format = "%llx";
|
| + else
|
| + format = "%llu";
|
| + if (sscanf(value_string.c_str(), format, &value) != 1)
|
| + return false;
|
| +
|
| + switch (type.data_size) {
|
| + case 1:
|
| + return ConvertAndAppendData<uint8_t>(value);
|
| + case 2:
|
| + return ConvertAndAppendData<uint16_t>(value);
|
| + case 4:
|
| + return ConvertAndAppendData<uint32_t>(value);
|
| + case 8:
|
| + return ConvertAndAppendData<uint64_t>(value);
|
| + default:
|
| + assert(false);
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +bool ValidationTestInputParser::ParseSignedInteger(
|
| + const DataType& type, const std::string& value_string) {
|
| + long long int value;
|
| + if (sscanf(value_string.c_str(), "%lli", &value) != 1)
|
| + return false;
|
| +
|
| + switch (type.data_size) {
|
| + case 1:
|
| + return ConvertAndAppendData<int8_t>(value);
|
| + case 2:
|
| + return ConvertAndAppendData<int16_t>(value);
|
| + case 4:
|
| + return ConvertAndAppendData<int32_t>(value);
|
| + case 8:
|
| + return ConvertAndAppendData<int64_t>(value);
|
| + default:
|
| + assert(false);
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +bool ValidationTestInputParser::ParseFloat(
|
| + const DataType& type, const std::string& value_string) {
|
| + MOJO_COMPILE_ASSERT(sizeof(float) == 4, float_size_is_not_4);
|
| +
|
| + float value;
|
| + if (sscanf(value_string.c_str(), "%f", &value) != 1)
|
| + return false;
|
| +
|
| + AppendData(value);
|
| + return true;
|
| +}
|
| +
|
| +bool ValidationTestInputParser::ParseDouble(const DataType& type,
|
| + const std::string& value_string) {
|
| + MOJO_COMPILE_ASSERT(sizeof(double) == 8, double_size_is_not_8);
|
| +
|
| + double value;
|
| + if (sscanf(value_string.c_str(), "%lf", &value) != 1)
|
| + return false;
|
| +
|
| + AppendData(value);
|
| + return true;
|
| +}
|
| +
|
| +bool ValidationTestInputParser::ParseBinarySequence(
|
| + const DataType& type, const std::string& value_string) {
|
| + if (value_string.size() != 8)
|
| + return false;
|
| +
|
| + uint8_t value = 0;
|
| + for (std::string::const_iterator iter = value_string.begin();
|
| + iter != value_string.end();
|
| + ++iter) {
|
| + value <<= 1;
|
| + if (*iter == '1')
|
| + value++;
|
| + else if (*iter != '0')
|
| + return false;
|
| + }
|
| + AppendData(value);
|
| + return true;
|
| +}
|
| +
|
| +bool ValidationTestInputParser::ParseDistance(const DataType& type,
|
| + const std::string& value_string) {
|
| + if (pending_distance_items_.find(value_string) !=
|
| + pending_distance_items_.end())
|
| + return false;
|
| +
|
| + PendingDistanceItem item = {parsed_input_->size(), type.data_size};
|
| + parsed_input_->resize(parsed_input_->size() + type.data_size);
|
| + pending_distance_items_[value_string] = item;
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool ValidationTestInputParser::ParseAnchor(const DataType& type,
|
| + const std::string& value_string) {
|
| + std::map<std::string, PendingDistanceItem>::iterator iter =
|
| + pending_distance_items_.find(value_string);
|
| + if (iter == pending_distance_items_.end())
|
| + return false;
|
| +
|
| + PendingDistanceItem dist_item = iter->second;
|
| + pending_distance_items_.erase(iter);
|
| +
|
| + size_t distance = parsed_input_->size() - dist_item.pos;
|
| + switch (dist_item.data_size) {
|
| + case 4:
|
| + return ConvertAndFillData<uint32_t>(dist_item.pos, distance);
|
| + case 8:
|
| + return ConvertAndFillData<uint64_t>(dist_item.pos, distance);
|
| + default:
|
| + assert(false);
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +bool ValidationTestInputParser::StartsWith(const Range& range,
|
| + const char* prefix,
|
| + size_t prefix_length) {
|
| + if (static_cast<size_t>(range.second - range.first) < prefix_length)
|
| + return false;
|
| +
|
| + return memcmp(range.first, prefix, prefix_length) == 0;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +bool ParseValidationTestInput(const std::string& input,
|
| + std::vector<uint8_t>* parsed_input,
|
| + std::string* error_message) {
|
| + ValidationTestInputParser parser(input, parsed_input, error_message);
|
| + return parser.Run();
|
| +}
|
| +
|
| +} // namespace test
|
| +} // namespace mojo
|
|
|