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 |