1
0
Fork 0
arangodb/lib/ProgramOptions/Parameters.h

531 lines
16 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Jan Steemann
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGODB_PROGRAM_OPTIONS_PARAMETERS_H
#define ARANGODB_PROGRAM_OPTIONS_PARAMETERS_H 1
#include "Basics/Common.h"
#include "Basics/Exceptions.h"
#include "Basics/NumberUtils.h"
#include "Basics/fpconv.h"
#include <velocypack/Builder.h>
#include <velocypack/velocypack-aliases.h>
#include <atomic>
#include <limits>
#include <numeric>
#include <type_traits>
#include <unordered_set>
namespace arangodb {
namespace options {
// helper functions to strip-non-numeric data from a string
std::string removeCommentsFromNumber(std::string const& value);
// convert a string into a number, base version for signed or unsigned integer types
template <typename T>
inline T toNumber(std::string value, T base) {
// replace leading spaces, replace trailing spaces & comments
value = removeCommentsFromNumber(value);
auto n = value.size();
int64_t m = 1;
int64_t d = 1;
bool seen = false;
if (n > 3) {
std::string suffix = value.substr(n - 3);
if (suffix == "kib" || suffix == "KiB") {
m = 1024;
value = value.substr(0, n - 3);
seen = true;
} else if (suffix == "mib" || suffix == "MiB") {
m = 1024 * 1024;
value = value.substr(0, n - 3);
seen = true;
} else if (suffix == "gib" || suffix == "GiB") {
m = 1024 * 1024 * 1024;
value = value.substr(0, n - 3);
seen = true;
}
}
if (!seen && n > 2) {
std::string suffix = value.substr(n - 2);
if (suffix == "kb" || suffix == "KB") {
m = 1000;
value = value.substr(0, n - 2);
seen = true;
} else if (suffix == "mb" || suffix == "MB") {
m = 1000 * 1000;
value = value.substr(0, n - 2);
seen = true;
} else if (suffix == "gb" || suffix == "GB") {
m = 1000 * 1000 * 1000;
value = value.substr(0, n - 2);
seen = true;
}
}
if (!seen && n > 1) {
std::string suffix = value.substr(n - 1);
if (suffix == "k" || suffix == "K") {
m = 1000;
value = value.substr(0, n - 1);
} else if (suffix == "m" || suffix == "M") {
m = 1000 * 1000;
value = value.substr(0, n - 1);
} else if (suffix == "g" || suffix == "G") {
m = 1000 * 1000 * 1000;
value = value.substr(0, n - 1);
} else if (suffix == "%") {
m = static_cast<T>(base);
d = 100;
value = value.substr(0, n - 1);
}
}
char const* p = value.data();
char const* e = p + value.size();
// skip leading whitespace
while (p < e && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) {
++p;
}
bool valid = true;
auto v = arangodb::NumberUtils::atoi<T>(p, e, valid);
if (!valid) {
throw std::out_of_range(value);
}
return static_cast<T>(v * m / d);
}
// convert a string into a number, version for double values
template <>
inline double toNumber<double>(std::string value, double /*base*/) {
// replace leading spaces, replace trailing spaces & comments
return std::stod(removeCommentsFromNumber(value));
}
// convert a string into another type, specialized version for numbers
template <typename T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type fromString(std::string value) {
return toNumber<T>(value, static_cast<T>(1));
}
// convert a string into another type, specialized version for string -> string
template <typename T>
typename std::enable_if<std::is_same<T, std::string>::value, T>::type fromString(std::string const& value) {
return value;
}
// stringify a value, base version for any type
template <typename T>
inline std::string stringifyValue(T const& value) {
return std::to_string(value);
}
// stringify a double value, specialized version
template <>
inline std::string stringifyValue<double>(double const& value) {
char buf[32];
int length = fpconv_dtoa(value, &buf[0]);
return std::string(&buf[0], static_cast<size_t>(length));
}
// stringify a boolean value, specialized version
template <>
inline std::string stringifyValue<bool>(bool const& value) {
return value ? "true" : "false";
}
// stringify a string value, specialized version
template <>
inline std::string stringifyValue<std::string>(std::string const& value) {
return "\"" + value + "\"";
}
// abstract base parameter type struct
struct Parameter {
Parameter() = default;
virtual ~Parameter() {}
virtual bool requiresValue() const { return true; }
virtual std::string name() const = 0;
virtual std::string valueString() const = 0;
virtual std::string set(std::string const&) = 0;
virtual std::string description() const { return std::string(); }
virtual std::string typeDescription() const {
return std::string("<") + name() + std::string(">");
}
virtual void toVPack(VPackBuilder&) const = 0;
};
// specialized type for boolean values
struct BooleanParameter : public Parameter {
typedef bool ValueType;
explicit BooleanParameter(ValueType* ptr, bool required = false)
: ptr(ptr), required(required) {}
bool requiresValue() const override { return required; }
std::string name() const override { return "boolean"; }
std::string valueString() const override { return stringifyValue(*ptr); }
std::string set(std::string const& value) override {
if (!required && value.empty()) {
// the empty value "" is considered "true", e.g. "--force" will mean
// "--force true"
*ptr = true;
return "";
}
if (value == "true" || value == "false" || value == "on" || value == "off" ||
value == "1" || value == "0" || value == "yes" || value == "no") {
*ptr =
(value == "true" || value == "on" || value == "1" || value == "yes");
return "";
}
return "invalid value for type " + this->name() + ". expecting 'true' or 'false'";
}
std::string typeDescription() const override {
return Parameter::typeDescription();
}
void toVPack(VPackBuilder& builder) const override {
builder.add(VPackValue(*ptr));
}
ValueType* ptr;
bool required;
};
// specialized type for atomic boolean values
struct AtomicBooleanParameter : public Parameter {
typedef std::atomic<bool> ValueType;
explicit AtomicBooleanParameter(ValueType* ptr, bool required = false)
: ptr(ptr), required(required) {}
bool requiresValue() const override { return required; }
std::string name() const override { return "boolean"; }
std::string valueString() const override {
return stringifyValue(ptr->load());
}
std::string set(std::string const& value) override {
if (!required && value.empty()) {
// the empty value "" is considered "true", e.g. "--force" will mean
// "--force true"
*ptr = true;
return "";
}
if (value == "true" || value == "false" || value == "on" ||
value == "off" || value == "1" || value == "0") {
ptr->store(value == "true" || value == "on" || value == "1");
return "";
}
return "invalid value for type " + this->name() + ". expecting 'true' or 'false'";
}
std::string typeDescription() const override {
return Parameter::typeDescription();
}
void toVPack(VPackBuilder& builder) const override {
builder.add(VPackValue(ptr->load()));
}
ValueType* ptr;
bool required;
};
// specialized type for numeric values
// this templated type needs a concrete number type
template <typename T>
struct NumericParameter : public Parameter {
typedef T ValueType;
explicit NumericParameter(ValueType* ptr) : ptr(ptr), base(1) {}
NumericParameter(ValueType* ptr, ValueType base) : ptr(ptr), base(base) {}
std::string valueString() const override { return stringifyValue(*ptr); }
std::string set(std::string const& value) override {
try {
ValueType v = toNumber<ValueType>(value, base);
*ptr = v;
return "";
} catch (...) {
return "invalid numeric value '" + value + "' for type " + this->name();
}
}
void toVPack(VPackBuilder& builder) const override {
builder.add(VPackValue(*ptr));
}
ValueType* ptr;
ValueType base;
};
// concrete int16 number value type
struct Int16Parameter : public NumericParameter<int16_t> {
typedef int16_t ValueType;
explicit Int16Parameter(ValueType* ptr) : NumericParameter<ValueType>(ptr) {}
Int16Parameter(ValueType* ptr, ValueType base)
: NumericParameter<ValueType>(ptr, base) {}
std::string name() const override { return "int16"; }
};
// concrete uint16 number value type
struct UInt16Parameter : public NumericParameter<uint16_t> {
typedef uint16_t ValueType;
explicit UInt16Parameter(ValueType* ptr) : NumericParameter<ValueType>(ptr) {}
UInt16Parameter(ValueType* ptr, ValueType base)
: NumericParameter<ValueType>(ptr, base) {}
std::string name() const override { return "uint16"; }
};
// concrete int32 number value type
struct Int32Parameter : public NumericParameter<int32_t> {
typedef int32_t ValueType;
explicit Int32Parameter(ValueType* ptr) : NumericParameter<ValueType>(ptr) {}
Int32Parameter(ValueType* ptr, ValueType base)
: NumericParameter<ValueType>(ptr, base) {}
std::string name() const override { return "int32"; }
};
// concrete uint32 number value type
struct UInt32Parameter : public NumericParameter<uint32_t> {
typedef uint32_t ValueType;
explicit UInt32Parameter(ValueType* ptr) : NumericParameter<ValueType>(ptr) {}
UInt32Parameter(ValueType* ptr, ValueType base)
: NumericParameter<ValueType>(ptr, base) {}
std::string name() const override { return "uint32"; }
};
// concrete int64 number value type
struct Int64Parameter : public NumericParameter<int64_t> {
typedef int64_t ValueType;
explicit Int64Parameter(ValueType* ptr) : NumericParameter<ValueType>(ptr) {}
Int64Parameter(ValueType* ptr, ValueType base)
: NumericParameter<ValueType>(ptr, base) {}
std::string name() const override { return "int64"; }
};
// concrete uint64 number value type
struct UInt64Parameter : public NumericParameter<uint64_t> {
typedef uint64_t ValueType;
explicit UInt64Parameter(ValueType* ptr) : NumericParameter<ValueType>(ptr) {}
UInt64Parameter(ValueType* ptr, ValueType base)
: NumericParameter<ValueType>(ptr, base) {}
std::string name() const override { return "uint64"; }
};
template <typename T>
struct BoundedParameter : public T {
BoundedParameter(typename T::ValueType* ptr, typename T::ValueType minValue,
typename T::ValueType maxValue)
: T(ptr), minValue(minValue), maxValue(maxValue) {}
std::string set(std::string const& value) override {
try {
typename T::ValueType v =
toNumber<typename T::ValueType>(value, static_cast<typename T::ValueType>(1));
if (v >= minValue && v <= maxValue) {
*this->ptr = v;
return "";
}
} catch (...) {
return "invalid numeric value '" + value + "' for type " + this->name();
}
return "number '" + value + "' out of allowed range (" +
std::to_string(minValue) + " - " + std::to_string(maxValue) + ")";
}
typename T::ValueType minValue;
typename T::ValueType maxValue;
};
// concrete double number value type
struct DoubleParameter : public NumericParameter<double> {
typedef double ValueType;
explicit DoubleParameter(ValueType* ptr) : NumericParameter<double>(ptr) {}
std::string name() const override { return "double"; }
};
// string value type
struct StringParameter : public Parameter {
typedef std::string ValueType;
explicit StringParameter(ValueType* ptr) : ptr(ptr) {}
std::string name() const override { return "string"; }
std::string valueString() const override { return stringifyValue(*ptr); }
std::string set(std::string const& value) override {
*ptr = value;
return "";
}
void toVPack(VPackBuilder& builder) const override {
builder.add(VPackValue(*ptr));
}
ValueType* ptr;
};
// specialized type for discrete values (defined in the unordered_set)
// this templated type needs a concrete value type
template <typename T>
struct DiscreteValuesParameter : public T {
DiscreteValuesParameter(typename T::ValueType* ptr,
std::unordered_set<typename T::ValueType> const& allowed)
: T(ptr), allowed(allowed) {
if (allowed.find(*ptr) == allowed.end()) {
// default value is not in list of allowed values
std::string msg("invalid default value for DiscreteValues parameter: '");
msg.append(stringifyValue(*ptr));
msg.append("'. ");
msg.append(description());
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, msg);
}
}
std::string set(std::string const& value) override {
auto it = allowed.find(fromString<typename T::ValueType>(value));
if (it == allowed.end()) {
std::string msg("invalid value '");
msg.append(value);
msg.append("'. ");
msg.append(description());
return msg;
}
return T::set(value);
}
std::string description() const override {
std::string msg("Possible values: ");
std::vector<std::string> values;
for (auto const& it : allowed) {
values.emplace_back(stringifyValue(it));
}
std::sort(values.begin(), values.end());
size_t i = 0;
for (auto const& it : values) {
if (i > 0) {
msg.append(", ");
}
msg.append(it);
++i;
}
return msg;
}
std::unordered_set<typename T::ValueType> allowed;
};
// specialized type for vectors of values
// this templated type needs a concrete value type
template <typename T>
struct VectorParameter : public Parameter {
explicit VectorParameter(std::vector<typename T::ValueType>* ptr)
: ptr(ptr) {}
std::string name() const override {
typename T::ValueType dummy;
T param(&dummy);
return std::string(param.name()) + "...";
}
std::string valueString() const override {
std::string value;
for (size_t i = 0; i < ptr->size(); ++i) {
if (i > 0) {
value.append(", ");
}
value.append(stringifyValue(ptr->at(i)));
}
return value;
}
std::string set(std::string const& value) override {
typename T::ValueType dummy;
T param(&dummy);
std::string result = param.set(value);
if (result.empty()) {
ptr->push_back(*(param.ptr));
}
return result;
}
void toVPack(VPackBuilder& builder) const override {
builder.openArray();
for (size_t i = 0; i < ptr->size(); ++i) {
builder.add(VPackValue(ptr->at(i)));
}
builder.close();
}
std::vector<typename T::ValueType>* ptr;
};
// a type that's useful for obsolete parameters that do nothing
struct ObsoleteParameter : public Parameter {
explicit ObsoleteParameter(bool requiresValue) : required(requiresValue) {}
bool requiresValue() const override { return required; }
std::string name() const override { return "obsolete"; }
std::string valueString() const override { return "-"; }
std::string set(std::string const&) override { return ""; }
void toVPack(VPackBuilder& builder) const override {
builder.add(VPackValue(VPackValueType::Null));
}
bool required;
};
} // namespace options
} // namespace arangodb
#endif