//////////////////////////////////////////////////////////////////////////////// /// 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 #include #include #include #include #include #include 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 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(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(p, e, valid); if (!valid) { throw std::out_of_range(value); } return static_cast(v * m / d); } // convert a string into a number, version for double values template <> inline double toNumber(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 std::enable_if::value, T>::type fromString(std::string value) { return toNumber(value, static_cast(1)); } // convert a string into another type, specialized version for string -> string template typename std::enable_if::value, T>::type fromString(std::string const& value) { return value; } // stringify a value, base version for any type template inline std::string stringifyValue(T const& value) { return std::to_string(value); } // stringify a double value, specialized version template <> inline std::string stringifyValue(double const& value) { char buf[32]; int length = fpconv_dtoa(value, &buf[0]); return std::string(&buf[0], static_cast(length)); } // stringify a boolean value, specialized version template <> inline std::string stringifyValue(bool const& value) { return value ? "true" : "false"; } // stringify a string value, specialized version template <> inline std::string stringifyValue(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 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 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(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 { typedef int16_t ValueType; explicit Int16Parameter(ValueType* ptr) : NumericParameter(ptr) {} Int16Parameter(ValueType* ptr, ValueType base) : NumericParameter(ptr, base) {} std::string name() const override { return "int16"; } }; // concrete uint16 number value type struct UInt16Parameter : public NumericParameter { typedef uint16_t ValueType; explicit UInt16Parameter(ValueType* ptr) : NumericParameter(ptr) {} UInt16Parameter(ValueType* ptr, ValueType base) : NumericParameter(ptr, base) {} std::string name() const override { return "uint16"; } }; // concrete int32 number value type struct Int32Parameter : public NumericParameter { typedef int32_t ValueType; explicit Int32Parameter(ValueType* ptr) : NumericParameter(ptr) {} Int32Parameter(ValueType* ptr, ValueType base) : NumericParameter(ptr, base) {} std::string name() const override { return "int32"; } }; // concrete uint32 number value type struct UInt32Parameter : public NumericParameter { typedef uint32_t ValueType; explicit UInt32Parameter(ValueType* ptr) : NumericParameter(ptr) {} UInt32Parameter(ValueType* ptr, ValueType base) : NumericParameter(ptr, base) {} std::string name() const override { return "uint32"; } }; // concrete int64 number value type struct Int64Parameter : public NumericParameter { typedef int64_t ValueType; explicit Int64Parameter(ValueType* ptr) : NumericParameter(ptr) {} Int64Parameter(ValueType* ptr, ValueType base) : NumericParameter(ptr, base) {} std::string name() const override { return "int64"; } }; // concrete uint64 number value type struct UInt64Parameter : public NumericParameter { typedef uint64_t ValueType; explicit UInt64Parameter(ValueType* ptr) : NumericParameter(ptr) {} UInt64Parameter(ValueType* ptr, ValueType base) : NumericParameter(ptr, base) {} std::string name() const override { return "uint64"; } }; template 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(value, static_cast(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 { typedef double ValueType; explicit DoubleParameter(ValueType* ptr) : NumericParameter(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 struct DiscreteValuesParameter : public T { DiscreteValuesParameter(typename T::ValueType* ptr, std::unordered_set 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(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 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 allowed; }; // specialized type for vectors of values // this templated type needs a concrete value type template struct VectorParameter : public Parameter { explicit VectorParameter(std::vector* 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* 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