// 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 #include #include #include #include #include #include #include #include #include "mojo/public/c/system/macros.h" namespace mojo { namespace test { namespace { class ValidationTestInputParser { public: ValidationTestInputParser(const std::string& input, std::vector* data, size_t* num_handles, std::string* error_message); ~ValidationTestInputParser(); bool Run(); private: struct DataType; typedef std::pair 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 |data_|. 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 ParseHandles(const DataType& type, const std::string& value_string); bool StartsWith(const Range& range, const char* prefix, size_t prefix_length); bool ConvertToUnsignedInteger(const std::string& value_string, unsigned long long int* value); template void AppendData(T data) { size_t pos = data_->size(); data_->resize(pos + sizeof(T)); memcpy(&(*data_)[pos], &data, sizeof(T)); } template bool ConvertAndAppendData(InputType value) { if (value > std::numeric_limits::max() || value < std::numeric_limits::min()) { return false; } AppendData(static_cast(value)); return true; } template bool ConvertAndFillData(size_t pos, InputType value) { if (value > std::numeric_limits::max() || value < std::numeric_limits::min()) { return false; } TargetType target_value = static_cast(value); assert(pos + sizeof(TargetType) <= data_->size()); memcpy(&(*data_)[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* data_; size_t* num_handles_; std::string* error_message_; std::map pending_distance_items_; std::set anchors_; }; #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), DATA_TYPE("[handles]", 0, &ValidationTestInputParser::ParseHandles)}; const size_t ValidationTestInputParser::kDataTypeCount = sizeof(ValidationTestInputParser::kDataTypes) / sizeof(ValidationTestInputParser::kDataTypes[0]); ValidationTestInputParser::ValidationTestInputParser(const std::string& input, std::vector* data, size_t* num_handles, std::string* error_message) : input_(input), input_cursor_(0), data_(data), num_handles_(num_handles), error_message_(error_message) { assert(data_); assert(num_handles_); assert(error_message_); data_->clear(); *num_handles_ = 0; 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) { data_->clear(); *num_handles_ = 0; } 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; if (!ConvertToUnsignedInteger(value_string, &value)) return false; switch (type.data_size) { case 1: return ConvertAndAppendData(value); case 2: return ConvertAndAppendData(value); case 4: return ConvertAndAppendData(value); case 8: return ConvertAndAppendData(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(value); case 2: return ConvertAndAppendData(value); case 4: return ConvertAndAppendData(value); case 8: return ConvertAndAppendData(value); default: assert(false); return false; } } bool ValidationTestInputParser::ParseFloat(const DataType& type, const std::string& value_string) { static_assert(sizeof(float) == 4, "sizeof(float) 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) { static_assert(sizeof(double) == 8, "sizeof(double) 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 = {data_->size(), type.data_size}; data_->resize(data_->size() + type.data_size); pending_distance_items_[value_string] = item; return true; } bool ValidationTestInputParser::ParseAnchor(const DataType& type, const std::string& value_string) { if (anchors_.find(value_string) != anchors_.end()) return false; anchors_.insert(value_string); std::map::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 = data_->size() - dist_item.pos; switch (dist_item.data_size) { case 4: return ConvertAndFillData(dist_item.pos, distance); case 8: return ConvertAndFillData(dist_item.pos, distance); default: assert(false); return false; } } bool ValidationTestInputParser::ParseHandles(const DataType& type, const std::string& value_string) { // It should be the first item. if (!data_->empty()) return false; unsigned long long int value; if (!ConvertToUnsignedInteger(value_string, &value)) return false; if (value > std::numeric_limits::max()) return false; *num_handles_ = static_cast(value); return true; } bool ValidationTestInputParser::StartsWith(const Range& range, const char* prefix, size_t prefix_length) { if (static_cast(range.second - range.first) < prefix_length) return false; return memcmp(range.first, prefix, prefix_length) == 0; } bool ValidationTestInputParser::ConvertToUnsignedInteger( const std::string& value_string, unsigned long long int* value) { const char* format = nullptr; if (value_string.find_first_of("xX") != std::string::npos) format = "%llx"; else format = "%llu"; return sscanf(value_string.c_str(), format, value) == 1; } } // namespace bool ParseValidationTestInput(const std::string& input, std::vector* data, size_t* num_handles, std::string* error_message) { ValidationTestInputParser parser(input, data, num_handles, error_message); return parser.Run(); } } // namespace test } // namespace mojo