blob: ff04306be43d09f2bd9ee0cbde3477ef6e4fa800 [file] [log] [blame]
/**
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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/licenseas/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.
*/
#pragma once
#include <exception>
#include <string>
#include <cstring>
#include <vector>
#include <cstdlib>
#include <type_traits>
#include <limits>
#include <algorithm>
#include "PropertyErrors.h"
namespace org::apache::nifi::minifi::utils {
namespace internal {
class ValueParser {
public:
explicit ValueParser(const std::string& str, std::size_t offset = 0) : str(str), offset(offset) {}
ValueParser& parse(int& out) { // NOLINT
long result; // NOLINT
auto len = safeCallConverter(std::strtol, result);
if (len == 0) {
throw ParseException("Couldn't parse int");
}
if (result < std::numeric_limits<int>::min() || result > std::numeric_limits<int>::max()) {
throw ParseException("Cannot convert long to int");
}
offset += len;
out = result;
return *this;
}
ValueParser& parse(int64_t& out) {
long long result; // NOLINT
auto len = safeCallConverter(std::strtoll, result);
if (len == 0) {
throw ParseException("Couldn't parse long long");
}
if (result < std::numeric_limits<int64_t>::min() || result > std::numeric_limits<int64_t>::max()) {
throw ParseException("Cannot convert long long to int64_t");
}
offset += len;
out = result;
return *this;
}
ValueParser& parse(uint32_t & out) {
skipWhitespace();
if (offset < str.length() && str[offset] == '-') {
throw ParseException("Not an unsigned long");
}
unsigned long result; // NOLINT
auto len = safeCallConverter(std::strtoul, result);
if (len == 0) {
throw ParseException("Couldn't parse uint32_t");
}
if (result > std::numeric_limits<uint32_t>::max()) {
throw ParseException("Cannot convert unsigned long to uint32_t");
}
offset += len;
out = result;
return *this;
}
ValueParser& parse(uint64_t& out) {
skipWhitespace();
if (offset < str.length() && str[offset] == '-') {
throw ParseException("Not an unsigned long");
}
unsigned long long result; // NOLINT
auto len = safeCallConverter(std::strtoull, result);
if (len == 0) {
throw ParseException("Couldn't parse unsigned long long");
}
if (result > std::numeric_limits<uint64_t>::max()) {
throw ParseException("Cannot convert unsigned long long to uint64_t");
}
offset += len;
out = result;
return *this;
}
ValueParser& parse(bool& out) {
skipWhitespace();
if (std::strncmp(str.c_str() + offset, "false", std::strlen("false")) == 0) {
offset += std::strlen("false");
out = false;
} else if (std::strncmp(str.c_str() + offset, "true", std::strlen("true")) == 0) {
offset += std::strlen("true");
out = true;
} else {
throw ParseException("Couldn't parse bool");
}
return *this;
}
ValueParser& parse(double& out) {
double result(0);
auto len = safeCallConverter(std::strtod, result);
if (len == 0) {
throw ParseException("Couldn't parse double");
}
offset += len;
out = result;
return *this;
}
void parseEnd() {
skipWhitespace();
if (offset < str.length()) {
throw ParseException("Expected to parse till the end");
}
}
std::string rest() const noexcept {
return str.substr(offset);
}
private:
/**
*
* @tparam T
* @param converter
* @param out
* @return the number of characters used during conversion, 0 for error
*/
template<typename T>
std::size_t safeCallConverter(T (*converter)(const char* begin, char** end, int base), T& out) {
const char* const begin = str.c_str() + offset;
char* end;
errno = 0;
T result = converter(begin, &end, 10);
if (end == begin || errno == ERANGE) {
return 0;
}
out = result;
return end - begin;
}
/**
*
* @tparam T
* @param converter
* @param out
* @return the number of characters used during conversion, 0 for error
*/
template<typename T>
std::size_t safeCallConverter(T (*converter)(const char* begin, char** end), T& out) {
const char* const begin = str.c_str() + offset;
char* end;
errno = 0;
T result = converter(begin, &end);
if (end == begin || errno == ERANGE) {
return 0;
}
out = result;
return end - begin;
}
void skipWhitespace() {
while (offset < str.length() && std::isspace(static_cast<unsigned char>(str[offset]))) {
++offset;
}
}
const std::string& str;
std::size_t offset;
};
} // namespace internal
template<typename T>
std::optional<T> toNumber(const std::string& input) {
try {
T output;
internal::ValueParser(input).parse(output).parseEnd();
return output;
} catch(const internal::ParseException&) {
return std::nullopt;
}
}
} // namespace org::apache::nifi::minifi::utils