Commit 83e9248d authored by Dominik Charousset's avatar Dominik Charousset

Remove obsolete code

parent b1a512e2
...@@ -77,7 +77,6 @@ set(LIBCAF_CORE_SRCS ...@@ -77,7 +77,6 @@ set(LIBCAF_CORE_SRCS
src/monitorable_actor.cpp src/monitorable_actor.cpp
src/node_id.cpp src/node_id.cpp
src/outbound_path.cpp src/outbound_path.cpp
src/parse_ini.cpp
src/pec.cpp src/pec.cpp
src/pretty_type_name.cpp src/pretty_type_name.cpp
src/private_thread.cpp src/private_thread.cpp
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <string>
#include <istream>
#include <functional>
#include "caf/atom.hpp"
#include "caf/variant.hpp"
#include "caf/optional.hpp"
#include "caf/config_value.hpp"
namespace caf {
namespace detail {
struct parse_ini_t {
/// Denotes an optional error output stream
using opt_err = optional<std::ostream&>;
/// Denotes a callback for consuming configuration values.
using config_consumer = std::function<bool (size_t, std::string,
config_value&, opt_err)>;
/// Parse the given input stream as INI formatted data and
/// calls the consumer with every key-value pair.
/// @param input Input stream of INI formatted text.
/// @param consumer_fun Callback consuming generated key-value pairs.
/// @param errors Output stream for parser errors.
void operator()(std::istream& input,
const config_consumer& consumer_fun,
opt_err errors = none) const;
};
constexpr parse_ini_t parse_ini = parse_ini_t{};
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/detail/parse_ini.hpp"
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
namespace caf {
namespace detail {
namespace {
// TODO: replace with generic lambda when switching to C++14
// Wraps a temporary into an (lvalue) config_value and calls `consumer_fun`.
struct consumer_t {
const parse_ini_t::config_consumer& consumer_fun;
parse_ini_t::opt_err& errors;
template <class T>
void operator()(size_t ln, std::string name, T&& x) {
config_value tmp{std::forward<T>(x)};
consumer_fun(ln, std::move(name), tmp, errors);
}
};
} // namespace <anonymous>
void parse_ini_t::operator()(std::istream& input,
const config_consumer& consumer_fun,
opt_err errors) const {
consumer_t consumer{consumer_fun, errors};
std::string group;
std::string line;
size_t ln = 0; // line number
auto print = [&](const char* category, const char* str) {
if (errors)
*errors << category << " INI file line "
<< ln << ": " << str << std::endl;
};
auto print_error = [&](const char* str) {
print("[ERROR]", str);
};
auto print_warning = [&](const char* str) {
print("[WARNING]", str);
};
while (std::getline(input, line)) {
++ln;
// get begin-of-line (bol) and end-of-line (eol), ignoring whitespaces
auto eol = find_if_not(line.rbegin(), line.rend(), ::isspace).base();
auto bol = find_if_not(line.begin(), eol, ::isspace);
// ignore empty lines and lines starting with ';' (comment)
if (bol == eol || *bol == ';')
continue;
// do we read a group name?
if (*bol == '[') {
if (*(eol - 1) != ']')
print_error("missing ] at end of line");
else
group.assign(bol + 1, eol - 1);
// skip further processing of this line
continue;
}
// do we have a group name yet? (prohibit ungrouped values)
if (group.empty()) {
print_error("value outside of a group");
continue;
}
// position of the equal sign
auto eqs = find(bol, eol, '=');
if (eqs == eol) {
print_error("no '=' found");
continue;
}
if (bol == eqs) {
print_error("line starting with '='");
continue;
}
if ((eqs + 1) == eol) {
print_error("line ends with '='");
continue;
}
// our keys have the format "<group>.<config-name>"
auto key = group;
key += '.';
// ignore any whitespace between config-name and equal sign
key.insert(key.end(), bol, find_if(bol, eqs, ::isspace));
// begin-of-value, ignoring whitespaces after '='
auto bov = find_if_not(eqs + 1, eol, ::isspace);
// auto-detect what we are dealing with
auto icase_eq = [](char x, char y) {
return tolower(x) == tolower(y);
};
if (std::equal(bov, eol, "true", icase_eq)) {
consumer(ln, std::move(key), true);
} else if (std::equal(bov, eol, "false", icase_eq)) {
consumer(ln, std::move(key), false);
} else if (*bov == '\'') {
// end-of-atom iterator
auto eoa = eol - 1;
if (bov == eoa) {
print_error("stray '");
continue;
}
if (*eoa != '\'') {
print_error("atom not terminated by '");
continue;
}
++bov; // skip leading '
if (std::distance(bov, eoa) > 10) {
print_error("atoms are not allowed to have more than 10 characters");
continue;
}
// found an atom value, copy it into a null-terminated buffer
char buf[11];
std::copy(bov, eoa, buf);
buf[std::distance(bov, eoa)] = '\0';
atom_value result = atom(buf);
consumer(ln, std::move(key), result);
} else if (*bov == '"') {
// end-of-string iterator
auto eos = eol - 1;
if (bov == eos) {
print_error(R"(stray '"')");
continue;
}
if (*eos != '"') {
print_error(R"(string not terminated by '"')");
continue;
}
// found a string, remove first and last char from string,
// start escaping string sequence
auto last_char_backslash = false;
std::string result;
result.reserve(static_cast<size_t>(std::distance(bov, eos)));
// skip leading " and iterate up to the trailing "
++bov;
for (; bov != eos; ++bov) {
if (last_char_backslash) {
switch (*bov) {
case 'n':
result += '\n';
break;
case 't':
result += '\t';
break;
default:
result += *bov;
}
last_char_backslash = false;
} else if (*bov == '\\') {
last_char_backslash = true;
} else {
result += *bov;
}
}
if (last_char_backslash)
print_warning("trailing quotation mark escaped");
consumer(ln, std::move(key), std::move(result));
} else {
bool is_neg = *bov == '-';
if (is_neg && ++bov == eol) {
print_error("'-' is not a number");
continue;
}
auto set_ival = [&](int base, int prefix_len, const char* err) {
auto advanced_bov = bov + prefix_len;
const char* str_begin = &*(advanced_bov);
const char* str_end = str_begin + std::distance(advanced_bov, eol);
char* e;
int64_t res = std::strtoll(str_begin, &e, base);
// check if we reached the end
if (e != str_end)
print_error(err);
else
consumer(ln, std::move(key), is_neg ? -res : res);
};
auto set_dval = [&] {
const char* str_begin = &*(bov);
const char* str_end = str_begin + std::distance(bov, eol);
char* e;
double res = std::strtod(str_begin, &e);
if (e != str_end)
print_error("invalid value");
else
consumer(ln, std::move(key), is_neg ? -res : res);
};
// check for number prefixes (0 for octal, 0x for hex, 0b for binary)
if (*bov == '0' && std::distance(bov, eol) > 1)
switch (*(bov + 1)) {
case 'x':
case 'X':
set_ival(16, 2, "invalid hex value");
break;
case 'b':
case 'B':
set_ival(2, 2, "invalid binary value");
break;
default:
if (all_of(bov, eol, ::isdigit))
set_ival(8, 1, "invalid oct value");
else
set_dval();
}
else {
// no suffix, try parsing the line as integer or double
if (all_of(bov, eol, ::isdigit))
set_ival(10, 0, "invalid decimal value");
else
set_dval();
}
}
}
}
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE parse_ini
#include "caf/test/unit_test.hpp"
#include <sstream>
#include <iostream>
#include "caf/string_algorithms.hpp"
#include "caf/all.hpp"
#include "caf/detail/parse_ini.hpp"
#include "caf/detail/safe_equal.hpp"
#define ERROR_HANDLER \
[&](error& err) { CAF_FAIL(system.render(err)); }
using namespace caf;
namespace {
constexpr const char* case1 = R"__(
[scheduler]
policy="work-sharing"
max-threads=2
; the middleman
[middleman]
automatic-connections=true
[nexus]
host="127.0.0.1"
port=4242
[cash]
greeting="Hi there, this is \"CASH!\"\n ~\\~ use at your own risk ~\\~"
)__";
constexpr const char* case2 = R"__(
[test]
foo=-0xff
bar=034
baz=-0.23
buzz=1E-34
bazz=0b10101010110011
bizz=-1
)__";
constexpr const char* case3 = R"__("
[whoops
foo="bar"
[test]
; provoke some more errors
foo bar
=42
baz=
foo="
bar="foo
some-int=42
some-string="hi there!\"
neg=-
wtf=0x3733T
not-a-bin=0b101002
hu=0779
hop=--"hiho"
)__";
class message_visitor : public static_visitor<message> {
public:
template <class T>
message operator()(T& value) const {
return make_message(std::move(value));
}
};
struct config : actor_system_config {
config() {
parse(test::engine::argc(), test::engine::argv());
}
};
struct fixture {
actor_system_config cfg;
actor_system system;
fixture() : system(cfg) {
// nop
}
template <class F>
void load_impl(F consumer, const char* str) {
std::stringstream ss;
std::stringstream err;
ss << str;
detail::parse_ini(ss, consumer, static_cast<std::ostream&>(err));
split(errors, err.str(), is_any_of("\n"), token_compress_on);
}
void load_to_config_server(const char* str) {
config_server = actor_cast<actor>(system.registry().get(atom("ConfigServ")));
// clear config
scoped_actor self{system};
self->request(config_server, infinite, get_atom::value, "*").receive(
[&](std::vector<std::pair<std::string, message>>& msgs) {
for (auto& kvp : msgs)
self->send(config_server, put_atom::value, kvp.first, message{});
},
ERROR_HANDLER
);
auto consume = [&](size_t, std::string key, config_value& value,
optional<std::ostream&>) {
message_visitor mv;
anon_send(config_server, put_atom::value,
std::move(key), visit(mv, value));
return true;
};
load_impl(consume, str);
}
void load(const char* str) {
auto consume = [&](size_t, std::string key, config_value& value,
optional<std::ostream&>) {
values.emplace(std::move(key), std::move(value));
return true;
};
load_impl(consume, str);
}
bool has_error(const char* err) {
return std::any_of(errors.begin(), errors.end(),
[=](const std::string& str) { return str == err; });
}
template <class T>
bool config_server_has(const char* key, const T& what) {
using type =
typename std::conditional<
std::is_convertible<T, std::string>::value,
std::string,
typename std::conditional<
std::is_integral<T>::value && !std::is_same<T, bool>::value,
int64_t,
T
>::type
>::type;
bool result = false;
scoped_actor self{system};
self->request(config_server, infinite, get_atom::value, key).receive(
[&](std::string&, message& msg) {
msg.apply(
[&](type& val) {
result = detail::safe_equal(what, val);
}
);
},
ERROR_HANDLER
);
return result;
}
template <class T>
bool value_is(const char* key, const T& what) {
if (config_server)
return config_server_has(key, what);
auto& cv = values[key];
using type =
typename std::conditional<
std::is_convertible<T, std::string>::value,
std::string,
typename std::conditional<
std::is_integral<T>::value && !std::is_same<T, bool>::value,
int64_t,
T
>::type
>::type;
auto ptr = get_if<type>(&cv);
return ptr != nullptr && detail::safe_equal(*ptr, what);
}
size_t num_values() {
if (config_server) {
size_t result = 0;
scoped_actor self{system};
self->request(config_server, infinite, get_atom::value, "*").receive(
[&](std::vector<std::pair<std::string, message>>& msgs) {
for (auto& kvp : msgs)
if (!kvp.second.empty())
++result;
},
ERROR_HANDLER
);
return result;
}
return values.size();
}
void check_case1() {
CAF_CHECK(errors.empty());
CAF_CHECK(num_values() == 6);
CAF_CHECK(value_is("nexus.port", 4242));
CAF_CHECK(value_is("nexus.host", "127.0.0.1"));
CAF_CHECK(value_is("scheduler.policy", "work-sharing"));
CAF_CHECK(value_is("scheduler.max-threads", 2));
CAF_CHECK(value_is("middleman.automatic-connections", true));
CAF_CHECK(value_is("cash.greeting",
"Hi there, this is \"CASH!\"\n ~\\~ use at your own risk ~\\~"));
}
void check_case2() {
CAF_CHECK(errors.empty());
CAF_CHECK(num_values() == 6);
CAF_CHECK(value_is("test.foo", -0xff));
CAF_CHECK(value_is("test.bar", 034));
CAF_CHECK(value_is("test.baz", -0.23));
CAF_CHECK(value_is("test.buzz", 1E-34));
CAF_CHECK(value_is("test.bazz", 10931));
CAF_CHECK(value_is("test.bizz", -1));
}
void check_case3() {
CAF_CHECK(has_error("[ERROR] INI file line 2: missing ] at end of line"));
CAF_CHECK(has_error("[ERROR] INI file line 3: value outside of a group"));
CAF_CHECK(has_error("[ERROR] INI file line 6: no '=' found"));
CAF_CHECK(has_error("[ERROR] INI file line 7: line starting with '='"));
CAF_CHECK(has_error("[ERROR] INI file line 8: line ends with '='"));
CAF_CHECK(has_error("[ERROR] INI file line 9: stray '\"'"));
CAF_CHECK(has_error("[ERROR] INI file line 10: string not terminated by '\"'"));
CAF_CHECK(has_error("[WARNING] INI file line 12: trailing quotation mark escaped"));
CAF_CHECK(has_error("[ERROR] INI file line 13: '-' is not a number"));
CAF_CHECK(has_error("[ERROR] INI file line 14: invalid hex value"));
CAF_CHECK(has_error("[ERROR] INI file line 15: invalid binary value"));
CAF_CHECK(has_error("[ERROR] INI file line 16: invalid oct value"));
CAF_CHECK(has_error("[ERROR] INI file line 17: invalid value"));
CAF_CHECK(num_values() == 2);
CAF_CHECK(value_is("test.some-int", 42));
CAF_CHECK(value_is("test.some-string", "hi there!"));
}
std::map<std::string, config_value> values;
actor config_server;
std::vector<std::string> errors;
};
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(parse_ini_tests, fixture)
CAF_TEST(simple_ini) {
load(case1);
check_case1();
}
CAF_TEST(numbers) {
load(case2);
check_case2();
}
CAF_TEST(errors) {
load(case3);
check_case3();
}
CAF_TEST(simple_ini_via_config_server) {
load_to_config_server(case1);
CAF_CHECK(values.empty());
check_case1();
}
CAF_TEST(numbers_via_config_server) {
load_to_config_server(case2);
CAF_CHECK(values.empty());
check_case2();
}
CAF_TEST(errors_via_config_server) {
load_to_config_server(case3);
CAF_CHECK(values.empty());
check_case3();
}
CAF_TEST_FIXTURE_SCOPE_END()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment