blob: e67568629d9ae9ebb980393b0d4992063c58405c [file] [log] [blame]
// Copyright 2021 The Chromium OS 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 "runtime_probe/field_converter.h"
#include <inttypes.h>
#include <memory>
#include <base/check.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <base/strings/string_piece.h>
#include <base/strings/string_number_conversions.h>
#include <base/values.h>
namespace runtime_probe {
namespace {
using ReturnCode = FieldConverter::ReturnCode;
constexpr const char* GetPrefix(ValidatorOperator op) {
switch (op) {
case ValidatorOperator::NOP:
return "!nop ";
case ValidatorOperator::RE:
return "!re ";
case ValidatorOperator::EQ:
return "!eq ";
case ValidatorOperator::NE:
return "!ne ";
case ValidatorOperator::GT:
return "!gt ";
case ValidatorOperator::GE:
return "!ge ";
case ValidatorOperator::LT:
return "!lt ";
case ValidatorOperator::LE:
return "!le ";
default:
DCHECK(false) << "should never reach here";
}
return nullptr;
}
constexpr const char* ToString(ValidatorOperator op) {
switch (op) {
case ValidatorOperator::NOP:
return "NOP";
case ValidatorOperator::RE:
return "RE";
case ValidatorOperator::EQ:
return "EQ";
case ValidatorOperator::NE:
return "NE";
case ValidatorOperator::GT:
return "GT";
case ValidatorOperator::GE:
return "GE";
case ValidatorOperator::LT:
return "LT";
case ValidatorOperator::LE:
return "LE";
default:
DCHECK(false) << "should never reach here";
}
return nullptr;
}
bool SplitValidateRuleString(const base::StringPiece& validate_rule,
ValidatorOperator* operator_,
base::StringPiece* operand) {
if (validate_rule.empty()) {
*operator_ = ValidatorOperator::NOP;
*operand = "";
return true;
}
auto first_space_idx = validate_rule.find_first_of(' ');
auto prefix = validate_rule.substr(0, first_space_idx + 1);
auto rest = validate_rule.substr(first_space_idx + 1);
for (int i = 0; i < static_cast<int>(ValidatorOperator::NUM_OP); i++) {
auto op = static_cast<ValidatorOperator>(i);
if (prefix == GetPrefix(op)) {
*operator_ = op;
if (op != ValidatorOperator::NOP) // NOP shouldn't have operand.
*operand = rest;
return true;
}
}
return false;
}
template <typename ConverterType>
std::unique_ptr<ConverterType> BuildNumericConverter(
const base::StringPiece& validate_rule) {
ValidatorOperator op;
base::StringPiece rest;
if (SplitValidateRuleString(validate_rule, &op, &rest)) {
if (op == ValidatorOperator::NOP)
return std::make_unique<ConverterType>(op, 0);
if (op == ValidatorOperator::EQ || op == ValidatorOperator::NE ||
op == ValidatorOperator::GT || op == ValidatorOperator::GE ||
op == ValidatorOperator::LT || op == ValidatorOperator::LE) {
typename ConverterType::OperandType operand;
if (ConverterType::StringToOperand(rest, &operand)) {
return std::make_unique<ConverterType>(op, operand);
} else {
LOG(ERROR) << "Can't convert to operand: " << rest;
}
}
}
LOG(ERROR) << "Invalid validate rule: " << validate_rule;
return nullptr;
}
template <typename ValueType>
ReturnCode CheckNumber(ValidatorOperator op,
const ValueType lhs,
const ValueType rhs) {
bool is_valid = true;
switch (op) {
case ValidatorOperator::NOP:
break;
case ValidatorOperator::EQ:
is_valid = lhs == rhs;
break;
case ValidatorOperator::GE:
is_valid = lhs >= rhs;
break;
case ValidatorOperator::GT:
is_valid = lhs > rhs;
break;
case ValidatorOperator::LE:
is_valid = lhs <= rhs;
break;
case ValidatorOperator::LT:
is_valid = lhs < rhs;
break;
case ValidatorOperator::NE:
is_valid = lhs != rhs;
break;
default:
return ReturnCode::UNSUPPORTED_OPERATOR;
}
return is_valid ? ReturnCode::OK : ReturnCode::INVALID_VALUE;
}
} // namespace
std::string StringFieldConverter::ToString() const {
return base::StringPrintf("StringFieldConverter(%s, %s)",
::runtime_probe::ToString(operator_),
operand_.c_str());
}
std::string IntegerFieldConverter::ToString() const {
return base::StringPrintf("IntegerFieldConverter(%s, %d)",
::runtime_probe::ToString(operator_), operand_);
}
std::string HexFieldConverter::ToString() const {
return base::StringPrintf("HexFieldConverter(%s, 0x%" PRIx64 ")",
::runtime_probe::ToString(operator_), operand_);
}
std::string DoubleFieldConverter::ToString() const {
return base::StringPrintf("DoubleFieldConverter(%s, %f)",
::runtime_probe::ToString(operator_), operand_);
}
std::unique_ptr<StringFieldConverter> StringFieldConverter::Build(
const base::StringPiece& validate_rule) {
ValidatorOperator op;
base::StringPiece pattern;
if (SplitValidateRuleString(validate_rule, &op, &pattern)) {
if (op == ValidatorOperator::NOP)
return std::make_unique<StringFieldConverter>(op, "");
if (op == ValidatorOperator::EQ || op == ValidatorOperator::NE) {
return std::make_unique<StringFieldConverter>(op, pattern);
}
if (op == ValidatorOperator::RE) {
auto instance = std::make_unique<StringFieldConverter>(op, pattern);
if (instance->regex_->error().empty()) {
// No error, the pattern is valid.
return instance;
}
// Error string is set to non-empty if there are errors.
LOG(ERROR) << "Invalid pattern: " << pattern;
LOG(ERROR) << instance->regex_->error();
}
}
LOG(ERROR) << "Invalid validate rule: " << validate_rule;
return nullptr;
}
std::unique_ptr<IntegerFieldConverter> IntegerFieldConverter::Build(
const base::StringPiece& validate_rule) {
return BuildNumericConverter<IntegerFieldConverter>(validate_rule);
}
std::unique_ptr<HexFieldConverter> HexFieldConverter::Build(
const base::StringPiece& validate_rule) {
return BuildNumericConverter<HexFieldConverter>(validate_rule);
}
std::unique_ptr<DoubleFieldConverter> DoubleFieldConverter::Build(
const base::StringPiece& validate_rule) {
return BuildNumericConverter<DoubleFieldConverter>(validate_rule);
}
ReturnCode StringFieldConverter::Convert(const std::string& field_name,
base::Value* dict_value) const {
CHECK(dict_value);
auto* value = dict_value->FindKey(field_name);
if (!value)
return ReturnCode::FIELD_NOT_FOUND;
switch (value->type()) {
case base::Value::Type::DOUBLE:
dict_value->SetStringKey(field_name, std::to_string(value->GetDouble()));
return ReturnCode::OK;
case base::Value::Type::INTEGER:
dict_value->SetStringKey(field_name, std::to_string(value->GetInt()));
return ReturnCode::OK;
case base::Value::Type::NONE:
dict_value->SetStringKey(field_name, "null");
return ReturnCode::OK;
case base::Value::Type::STRING:
return ReturnCode::OK;
default:
return ReturnCode::INCOMPATIBLE_VALUE;
}
}
ReturnCode IntegerFieldConverter::Convert(const std::string& field_name,
base::Value* dict_value) const {
CHECK(dict_value);
auto* value = dict_value->FindKey(field_name);
if (!value)
return ReturnCode::FIELD_NOT_FOUND;
switch (value->type()) {
case base::Value::Type::DOUBLE:
dict_value->SetIntKey(field_name, static_cast<int>(value->GetDouble()));
return ReturnCode::OK;
case base::Value::Type::INTEGER:
return ReturnCode::OK;
case base::Value::Type::STRING: {
const auto& string_value = value->GetString();
OperandType int_value;
if (StringToOperand(string_value, &int_value)) {
dict_value->SetIntKey(field_name, int_value);
return ReturnCode::OK;
} else {
LOG(ERROR) << "Failed to convert '" << string_value << "' to integer.";
return ReturnCode::INCOMPATIBLE_VALUE;
}
}
default:
return ReturnCode::INCOMPATIBLE_VALUE;
}
}
ReturnCode HexFieldConverter::Convert(const std::string& field_name,
base::Value* dict_value) const {
CHECK(dict_value);
auto* value = dict_value->FindKey(field_name);
if (!value)
return ReturnCode::FIELD_NOT_FOUND;
OperandType int_value;
switch (value->type()) {
case base::Value::Type::DOUBLE:
dict_value->SetIntKey(field_name, static_cast<int>(value->GetDouble()));
return ReturnCode::OK;
case base::Value::Type::INTEGER: {
int_value = value->GetInt();
break;
}
case base::Value::Type::STRING: {
const auto& string_value_ = value->GetString();
if (!StringToOperand(string_value_, &int_value)) {
LOG(ERROR) << "Failed to convert '" << string_value_ << "' to integer.";
return ReturnCode::INCOMPATIBLE_VALUE;
}
break;
}
default:
return ReturnCode::INCOMPATIBLE_VALUE;
}
// Since base::Value only supports 32-bit integer, we use string field to
// store large integers. We convert values to decimal strings to make
// google::protobuf::util::JsonStringToMessage parse strings to integers
// correctly.
const auto string_value = base::NumberToString(int_value);
dict_value->SetStringKey(field_name, string_value);
return ReturnCode::OK;
}
ReturnCode DoubleFieldConverter::Convert(const std::string& field_name,
base::Value* dict_value) const {
CHECK(dict_value);
auto* value = dict_value->FindKey(field_name);
if (!value)
return ReturnCode::FIELD_NOT_FOUND;
switch (value->type()) {
case base::Value::Type::DOUBLE:
return ReturnCode::OK;
case base::Value::Type::INTEGER:
dict_value->SetDoubleKey(field_name, value->GetDouble());
return ReturnCode::OK;
case base::Value::Type::STRING: {
const auto& string_value = value->GetString();
double double_value;
if (StringToDouble(string_value, &double_value)) {
dict_value->SetDoubleKey(field_name, double_value);
return ReturnCode::OK;
} else {
LOG(ERROR) << "Failed to convert '" << string_value << "' to double.";
return ReturnCode::INCOMPATIBLE_VALUE;
}
}
default:
return ReturnCode::INCOMPATIBLE_VALUE;
}
}
ReturnCode StringFieldConverter::Validate(const std::string& field_name,
base::Value* dict_value) const {
CHECK(dict_value);
auto* value_ = dict_value->FindKey(field_name);
if (!value_)
return ReturnCode::FIELD_NOT_FOUND;
if (!value_->is_string())
return ReturnCode::INCOMPATIBLE_VALUE;
const auto& value = value_->GetString();
bool is_valid = true;
switch (operator_) {
case ValidatorOperator::NOP:
break;
case ValidatorOperator::EQ:
is_valid = value == operand_;
break;
case ValidatorOperator::RE:
is_valid = regex_->FullMatch(value);
break;
case ValidatorOperator::NE:
is_valid = value != operand_;
break;
default:
return ReturnCode::UNSUPPORTED_OPERATOR;
}
return is_valid ? ReturnCode::OK : ReturnCode::INVALID_VALUE;
}
ReturnCode IntegerFieldConverter::Validate(const std::string& field_name,
base::Value* dict_value) const {
CHECK(dict_value);
auto* value_ = dict_value->FindKey(field_name);
if (!value_)
return ReturnCode::FIELD_NOT_FOUND;
if (!value_->is_int())
return ReturnCode::INCOMPATIBLE_VALUE;
const auto value = value_->GetInt();
return CheckNumber(operator_, value, operand_);
}
ReturnCode HexFieldConverter::Validate(const std::string& field_name,
base::Value* dict_value) const {
CHECK(dict_value);
auto* value_ = dict_value->FindKey(field_name);
if (!value_)
return ReturnCode::FIELD_NOT_FOUND;
OperandType value;
switch (value_->type()) {
case base::Value::Type::INTEGER: {
value = value_->GetInt();
break;
}
case base::Value::Type::STRING: {
const auto& string_value = value_->GetString();
if (!StringToInt64(string_value, &value))
return ReturnCode::INCOMPATIBLE_VALUE;
break;
}
default:
return ReturnCode::INCOMPATIBLE_VALUE;
}
return CheckNumber(operator_, value, operand_);
}
ReturnCode DoubleFieldConverter::Validate(const std::string& field_name,
base::Value* dict_value) const {
CHECK(dict_value);
auto* value_ = dict_value->FindKey(field_name);
if (!value_)
return ReturnCode::FIELD_NOT_FOUND;
if (!value_->is_double() && !value_->is_int())
return ReturnCode::INCOMPATIBLE_VALUE;
const auto value = value_->GetDouble();
return CheckNumber(operator_, value, operand_);
}
} // namespace runtime_probe