Require C++17

Removes lokimq::string_view (the type alias is still provided for
backwards compat, but now is always std::string_view).

Bump version (on dev branch) to 1.2.0
This commit is contained in:
Jason Rhinelander 2020-05-12 15:33:59 -03:00
parent 8984dfc4ea
commit 7b42537801
23 changed files with 184 additions and 692 deletions

View File

@ -5,8 +5,8 @@ project(liblokimq CXX)
include(GNUInstallDirs)
set(LOKIMQ_VERSION_MAJOR 1)
set(LOKIMQ_VERSION_MINOR 1)
set(LOKIMQ_VERSION_PATCH 5)
set(LOKIMQ_VERSION_MINOR 2)
set(LOKIMQ_VERSION_PATCH 0)
set(LOKIMQ_VERSION "${LOKIMQ_VERSION_MAJOR}.${LOKIMQ_VERSION_MINOR}.${LOKIMQ_VERSION_PATCH}")
message(STATUS "lokimq v${LOKIMQ_VERSION}")
@ -83,7 +83,7 @@ target_include_directories(lokimq
target_compile_options(lokimq PRIVATE -Wall -Wextra -Werror)
set_target_properties(lokimq PROPERTIES
CXX_STANDARD 14
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
POSITION_INDEPENDENT_CODE ON

View File

@ -16,7 +16,7 @@ constexpr size_t enc_length(address::encoding enc) {
// consumed pubkey data, and returns the pubkey (as a 32-byte string). Throws if no valid pubkey
// was found at the beginning of addr. We look for hex, base32z, or base64 pubkeys *unless* qr is
// given: for QR-friendly we only accept hex or base32z (since QR cannot handle base64's alphabet).
std::string decode_pubkey(string_view& in, bool qr) {
std::string decode_pubkey(std::string_view& in, bool qr) {
std::string pubkey;
if (in.size() >= 64 && lokimq::is_hex(in.substr(0, 64))) {
pubkey = from_hex(in.substr(0, 64));
@ -38,12 +38,12 @@ std::string decode_pubkey(string_view& in, bool qr) {
// Parse the host, port, and optionally pubkey from a string view, mutating it to remove the parsed
// sections. qr should be true if we should accept $IPv6$ as a QR-encoding-friendly alternative to
// [IPv6] (the returned host will have the $ replaced, i.e. [IPv6]).
std::tuple<std::string, uint16_t, std::string> parse_tcp(string_view& addr, bool qr, bool expect_pubkey) {
std::tuple<std::string, uint16_t, std::string> parse_tcp(std::string_view& addr, bool qr, bool expect_pubkey) {
std::tuple<std::string, uint16_t, std::string> result;
auto& host = std::get<0>(result);
if (addr.front() == '[' || (qr && addr.front() == '$')) { // IPv6 addr (though this is far from complete validation)
auto pos = addr.find_first_not_of(":.1234567890abcdefABCDEF", 1);
if (pos == string_view::npos)
if (pos == std::string_view::npos)
throw std::invalid_argument("Could not find terminating ] while parsing an IPv6 address");
if (!(addr[pos] == ']' || (qr && addr[pos] == '$')))
throw std::invalid_argument{"Expected " + (qr ? "$"s : "]"s) + " to close IPv6 address but found " + std::string(1, addr[pos])};
@ -57,7 +57,7 @@ std::tuple<std::string, uint16_t, std::string> parse_tcp(string_view& addr, bool
addr.remove_prefix(pos+1);
} else {
auto port_pos = addr.find(':');
if (port_pos == string_view::npos)
if (port_pos == std::string_view::npos)
throw std::invalid_argument{"Could not determine host (no following ':port' found)"};
if (port_pos == 0)
throw std::invalid_argument{"Host cannot be empty"};
@ -77,7 +77,7 @@ std::tuple<std::string, uint16_t, std::string> parse_tcp(string_view& addr, bool
auto pos = addr.find_first_not_of("1234567890");
if (pos == 0)
throw std::invalid_argument{"Could not find numeric port in address string"};
if (pos == string_view::npos)
if (pos == std::string_view::npos)
pos = addr.size();
size_t processed;
int port_int = std::stoi(std::string{addr.substr(0, pos)}, &processed);
@ -107,7 +107,7 @@ std::tuple<std::string, uint16_t, std::string> parse_tcp(string_view& addr, bool
// Parse the socket path and (possibly) pubkey, mutating it to remove the parsed sections.
// Currently the /pubkey *must* be at the end of the string, but this might not always be the case
// (e.g. we could in the future support query string-like arguments).
std::pair<std::string, std::string> parse_unix(string_view& addr, bool expect_pubkey) {
std::pair<std::string, std::string> parse_unix(std::string_view& addr, bool expect_pubkey) {
std::pair<std::string, std::string> result;
if (expect_pubkey) {
size_t b64_len = addr.size() > 0 && addr.back() == '=' ? 44 : 43;
@ -134,9 +134,9 @@ std::pair<std::string, std::string> parse_unix(string_view& addr, bool expect_pu
return result;
}
address::address(string_view addr) {
auto protoend = addr.find("://"_sv);
if (protoend == string_view::npos || protoend == 0)
address::address(std::string_view addr) {
auto protoend = addr.find("://"sv);
if (protoend == std::string_view::npos || protoend == 0)
throw std::invalid_argument("Invalid address: no protocol found");
auto pro = addr.substr(0, protoend);
addr.remove_prefix(protoend + 3);

View File

@ -28,7 +28,7 @@
#pragma once
#include <string>
#include "string_view.h"
#include <string_view>
namespace lokimq {
@ -116,7 +116,7 @@ struct address {
*
* Throw std::invalid_argument if the given address is not parseable.
*/
address(string_view addr);
address(std::string_view addr);
/// Constructs and builds the ZMQ connection address from the stored connection details. This
/// does not contain any of the curve-related details; those must be specified separately when

View File

@ -12,7 +12,7 @@ namespace {
// Builds a ZMTP metadata key-value pair. These will be available on every message from that peer.
// Keys must start with X- and be <= 255 characters.
std::string zmtp_metadata(string_view key, string_view value) {
std::string zmtp_metadata(std::string_view key, std::string_view value) {
assert(key.size() > 2 && key.size() <= 255 && key[0] == 'X' && key[1] == '-');
std::string result;
@ -63,7 +63,7 @@ bool LokiMQ::proxy_check_auth(size_t conn_index, bool outgoing, const peer_info&
msgs.push_back(create_message(peer.route));
msgs.push_back(create_message(reply));
if (cat_call.second && cat_call.second->second /*request command*/ && !data.empty()) {
msgs.push_back(create_message("REPLY"_sv));
msgs.push_back(create_message("REPLY"sv));
msgs.push_back(create_message(view(data.front()))); // reply tag
} else {
msgs.push_back(create_message(view(cmd)));
@ -87,7 +87,7 @@ void LokiMQ::set_active_sns(pubkey_set pubkeys) {
proxy_set_active_sns(std::move(pubkeys));
}
}
void LokiMQ::proxy_set_active_sns(string_view data) {
void LokiMQ::proxy_set_active_sns(std::string_view data) {
proxy_set_active_sns(detail::deserialize_object<pubkey_set>(bt_deserialize<uintptr_t>(data)));
}
void LokiMQ::proxy_set_active_sns(pubkey_set pubkeys) {
@ -267,7 +267,7 @@ void LokiMQ::process_zap_requests() {
status_text = "Invalid public key size for CURVE authentication";
} else {
auto ip = view(frames[3]);
string_view pubkey;
std::string_view pubkey;
bool sn = false;
if (bind[bind_id].second.curve) {
pubkey = view(frames[6]);

View File

@ -27,7 +27,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include "string_view.h"
#include <string_view>
#include <array>
#include <iterator>
#include <cassert>
@ -101,19 +101,14 @@ void to_base32z(InputIt begin, InputIt end, OutputIt out) {
}
/// Creates a base32z string from an iterable, std::string-like object
inline std::string to_base32z(string_view s) {
template <typename CharT>
std::string to_base32z(std::basic_string_view<CharT> s) {
std::string base32z;
base32z.reserve((s.size()*8 + 4) / 5); // == bytes*8/5, rounded up.
to_base32z(s.begin(), s.end(), std::back_inserter(base32z));
return base32z;
}
inline std::string to_base32z(ustring_view s) {
std::string base32z;
base32z.reserve((s.size()*8 + 4) / 5);
to_base32z(s.begin(), s.end(), std::back_inserter(base32z));
return base32z;
}
inline std::string to_base32z(std::string_view s) { return to_base32z<>(s); }
/// Returns true if all elements in the range are base32z characters
template <typename It>
@ -128,8 +123,9 @@ constexpr bool is_base32z(It begin, It end) {
}
/// Returns true if all elements in the string-like value are base32z characters
constexpr bool is_base32z(string_view s) { return is_base32z(s.begin(), s.end()); }
constexpr bool is_base32z(ustring_view s) { return is_base32z(s.begin(), s.end()); }
template <typename CharT>
constexpr bool is_base32z(std::basic_string_view<CharT> s) { return is_base32z(s.begin(), s.end()); }
constexpr bool is_base32z(std::string_view s) { return is_base32z<>(s); }
/// Converts a sequence of base32z digits to bytes. Undefined behaviour if any characters are not
/// valid base32z alphabet characters. It is permitted for the input and output ranges to overlap
@ -184,18 +180,13 @@ void from_base32z(InputIt begin, InputIt end, OutputIt out) {
/// Converts base32z digits from a std::string-like object into a std::string of bytes. Undefined
/// behaviour if any characters are not valid (case-insensitive) base32z characters.
inline std::string from_base32z(string_view s) {
template <typename CharT>
std::string from_base32z(std::basic_string_view<CharT> s) {
std::string bytes;
bytes.reserve((s.size()*5 + 7) / 8); // == chars*5/8, rounded up.
from_base32z(s.begin(), s.end(), std::back_inserter(bytes));
return bytes;
}
inline std::string from_base32z(ustring_view s) {
std::string bytes;
bytes.reserve((s.size()*5 + 7) / 8);
from_base32z(s.begin(), s.end(), std::back_inserter(bytes));
return bytes;
}
inline std::string from_base32z(std::string_view s) { return from_base32z<>(s); }
}

View File

@ -27,7 +27,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include "string_view.h"
#include <string_view>
#include <array>
#include <iterator>
#include <cassert>
@ -117,19 +117,14 @@ void to_base64(InputIt begin, InputIt end, OutputIt out) {
}
/// Creates a base64 string from an iterable, std::string-like object
inline std::string to_base64(string_view s) {
std::string base64;
base64.reserve((s.size() + 2) / 3 * 4);
to_base64(s.begin(), s.end(), std::back_inserter(base64));
return base64;
}
inline std::string to_base64(ustring_view s) {
template <typename CharT>
std::string to_base64(std::basic_string_view<CharT> s) {
std::string base64;
base64.reserve((s.size() + 2) / 3 * 4);
to_base64(s.begin(), s.end(), std::back_inserter(base64));
return base64;
}
inline std::string to_base64(std::string_view s) { return to_base64<>(s); }
/// Returns true if the range is a base64 encoded value; we allow (but do not require) '=' padding,
/// but only at the end, only 1 or 2, and only if it pads out the total to a multiple of 4.
@ -157,8 +152,9 @@ constexpr bool is_base64(It begin, It end) {
}
/// Returns true if the string-like value is a base64 encoded value
constexpr bool is_base64(string_view s) { return is_base64(s.begin(), s.end()); }
constexpr bool is_base64(ustring_view s) { return is_base64(s.begin(), s.end()); }
template <typename CharT>
constexpr bool is_base64(std::basic_string_view<CharT> s) { return is_base64(s.begin(), s.end()); }
constexpr bool is_base64(std::string_view s) { return is_base64(s.begin(), s.end()); }
/// Converts a sequence of base64 digits to bytes. Undefined behaviour if any characters are not
/// valid base64 alphabet characters. It is permitted for the input and output ranges to overlap as
@ -200,18 +196,13 @@ void from_base64(InputIt begin, InputIt end, OutputIt out) {
/// Converts base64 digits from a std::string-like object into a std::string of bytes. Undefined
/// behaviour if any characters are not valid base64 characters.
inline std::string from_base64(string_view s) {
std::string bytes;
bytes.reserve(s.size()*6 / 8);
from_base64(s.begin(), s.end(), std::back_inserter(bytes));
return bytes;
}
inline std::string from_base64(ustring_view s) {
template <typename CharT>
std::string from_base64(std::basic_string_view<CharT> s) {
std::string bytes;
bytes.reserve(s.size()*6 / 8);
from_base64(s.begin(), s.end(), std::back_inserter(bytes));
return bytes;
}
inline std::string from_base64(std::string_view s) { return from_base64<>(s); }
}

View File

@ -32,7 +32,7 @@ namespace lokimq {
namespace detail {
/// Reads digits into an unsigned 64-bit int.
uint64_t extract_unsigned(string_view& s) {
uint64_t extract_unsigned(std::string_view& s) {
if (s.empty())
throw bt_deserialize_invalid{"Expected 0-9 but found end of string"};
if (s[0] < '0' || s[0] > '9')
@ -48,7 +48,7 @@ uint64_t extract_unsigned(string_view& s) {
return uval;
}
void bt_deserialize<string_view>::operator()(string_view& s, string_view& val) {
void bt_deserialize<std::string_view>::operator()(std::string_view& s, std::string_view& val) {
if (s.size() < 2) throw bt_deserialize_invalid{"Deserialize failed: given data is not an bt-encoded string"};
if (s[0] < '0' || s[0] > '9')
throw bt_deserialize_invalid_type{"Expected 0-9 but found '"s + s[0] + "'"};
@ -72,7 +72,7 @@ static_assert(std::numeric_limits<int64_t>::min() + std::numeric_limits<int64_t>
static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + uint64_t{1} == (uint64_t{1} << 63),
"Non 2s-complement architecture not supported!");
std::pair<maybe_signed_int64_t, bool> bt_deserialize_integer(string_view& s) {
std::pair<maybe_signed_int64_t, bool> bt_deserialize_integer(std::string_view& s) {
// Smallest possible encoded integer is 3 chars: "i0e"
if (s.size() < 3) throw bt_deserialize_invalid("Deserialization failed: end of string found where integer expected");
if (s[0] != 'i') throw bt_deserialize_invalid_type("Deserialization failed: expected 'i', found '"s + s[0] + '\'');
@ -102,7 +102,7 @@ std::pair<maybe_signed_int64_t, bool> bt_deserialize_integer(string_view& s) {
template struct bt_deserialize<int64_t>;
template struct bt_deserialize<uint64_t>;
void bt_deserialize<bt_value, void>::operator()(string_view& s, bt_value& val) {
void bt_deserialize<bt_value, void>::operator()(std::string_view& s, bt_value& val) {
if (s.size() < 2) throw bt_deserialize_invalid("Deserialization failed: end of string found where bt-encoded value expected");
switch (s[0]) {
@ -137,7 +137,7 @@ void bt_deserialize<bt_value, void>::operator()(string_view& s, bt_value& val) {
} // namespace detail
bt_list_consumer::bt_list_consumer(string_view data_) : data{std::move(data_)} {
bt_list_consumer::bt_list_consumer(std::string_view data_) : data{std::move(data_)} {
if (data.empty()) throw std::runtime_error{"Cannot create a bt_list_consumer with an empty string_view"};
if (data[0] != 'l') throw std::runtime_error{"Cannot create a bt_list_consumer with non-list data"};
data.remove_prefix(1);
@ -145,13 +145,13 @@ bt_list_consumer::bt_list_consumer(string_view data_) : data{std::move(data_)} {
/// Attempt to parse the next value as a string (and advance just past it). Throws if the next
/// value is not a string.
string_view bt_list_consumer::consume_string_view() {
std::string_view bt_list_consumer::consume_string_view() {
if (data.empty())
throw bt_deserialize_invalid{"expected a string, but reached end of data"};
else if (!is_string())
throw bt_deserialize_invalid_type{"expected a string, but found "s + data.front()};
string_view next{data}, result;
detail::bt_deserialize<string_view>{}(next, result);
std::string_view next{data}, result;
detail::bt_deserialize<std::string_view>{}(next, result);
data = next;
return result;
}
@ -174,7 +174,7 @@ void bt_list_consumer::skip_value() {
throw bt_deserialize_invalid_type{"next bt value has unknown type"};
}
string_view bt_list_consumer::consume_list_data() {
std::string_view bt_list_consumer::consume_list_data() {
auto start = data.begin();
if (data.size() < 2 || !is_list()) throw bt_deserialize_invalid_type{"next bt value is not a list"};
data.remove_prefix(1); // Descend into the sublist, consume the "l"
@ -187,7 +187,7 @@ string_view bt_list_consumer::consume_list_data() {
return {start, static_cast<size_t>(std::distance(start, data.begin()))};
}
string_view bt_list_consumer::consume_dict_data() {
std::string_view bt_list_consumer::consume_dict_data() {
auto start = data.begin();
if (data.size() < 2 || !is_dict()) throw bt_deserialize_invalid_type{"next bt value is not a dict"};
data.remove_prefix(1); // Descent into the dict, consumer the "d"
@ -202,7 +202,7 @@ string_view bt_list_consumer::consume_dict_data() {
return {start, static_cast<size_t>(std::distance(start, data.begin()))};
}
bt_dict_consumer::bt_dict_consumer(string_view data_) {
bt_dict_consumer::bt_dict_consumer(std::string_view data_) {
data = std::move(data_);
if (data.empty()) throw std::runtime_error{"Cannot create a bt_dict_consumer with an empty string_view"};
if (data.size() < 2 || data[0] != 'd') throw std::runtime_error{"Cannot create a bt_dict_consumer with non-dict data"};
@ -220,10 +220,10 @@ bool bt_dict_consumer::consume_key() {
return true;
}
std::pair<string_view, string_view> bt_dict_consumer::next_string() {
std::pair<std::string_view, std::string_view> bt_dict_consumer::next_string() {
if (!is_string())
throw bt_deserialize_invalid_type{"expected a string, but found "s + data.front()};
std::pair<string_view, string_view> ret;
std::pair<std::string_view, std::string_view> ret;
ret.second = bt_list_consumer::consume_string_view();
ret.first = flush_key();
return ret;

View File

@ -38,7 +38,7 @@
#include <cstring>
#include <ostream>
#include <sstream>
#include "string_view.h"
#include <string_view>
#include "mapbox/variant.hpp"
@ -92,7 +92,7 @@ struct bt_u64 { uint64_t val; explicit bt_u64(uint64_t val) : val{val} {} };
/// Recursive generic type that can fully represent everything valid for a BT serialization.
using bt_value = mapbox::util::variant<
std::string,
string_view,
std::string_view,
int64_t,
bt_u64,
mapbox::util::recursive_wrapper<bt_list>,
@ -121,8 +121,10 @@ template <typename... Ts> using void_t = typename void_t_impl<Ts...>::type;
namespace detail {
/// Reads digits into an unsigned 64-bit int.
uint64_t extract_unsigned(string_view& s);
inline uint64_t extract_unsigned(string_view&& s) { return extract_unsigned(s); }
uint64_t extract_unsigned(std::string_view& s);
// (Provide non-constant lvalue and rvalue ref functions so that we only accept explicit
// string_views but not implicitly converted ones)
inline uint64_t extract_unsigned(std::string_view&& s) { return extract_unsigned(s); }
// Fallback base case; we only get here if none of the partial specializations below work
template <typename T, typename SFINAE = void>
@ -132,7 +134,7 @@ template <typename T, typename SFINAE = void>
struct bt_deserialize { static_assert(!std::is_same<T, T>::value, "Cannot deserialize T: unsupported type for bt deserialization"); };
/// Checks that we aren't at the end of a string view and throws if we are.
inline void bt_need_more(const string_view &s) {
inline void bt_need_more(const std::string_view &s) {
if (s.empty())
throw bt_deserialize_invalid{"Unexpected end of string while deserializing"};
}
@ -143,7 +145,7 @@ union maybe_signed_int64_t { int64_t i64; uint64_t u64; };
/// iff the value is int64_t because a negative value was read. Throws an exception if the read
/// value doesn't fit in a int64_t (if negative) or a uint64_t (if positive). Removes consumed
/// characters from the string_view.
std::pair<maybe_signed_int64_t, bool> bt_deserialize_integer(string_view& s);
std::pair<maybe_signed_int64_t, bool> bt_deserialize_integer(std::string_view& s);
/// Integer specializations
template <typename T>
@ -158,7 +160,7 @@ struct bt_serialize<T, std::enable_if_t<std::is_integral<T>::value>> {
template <typename T>
struct bt_deserialize<T, std::enable_if_t<std::is_integral<T>::value>> {
void operator()(string_view& s, T &val) {
void operator()(std::string_view& s, T &val) {
constexpr uint64_t umax = static_cast<uint64_t>(std::numeric_limits<T>::max());
constexpr int64_t smin = static_cast<int64_t>(std::numeric_limits<T>::min()),
smax = static_cast<int64_t>(std::numeric_limits<T>::max());
@ -191,35 +193,35 @@ extern template struct bt_deserialize<uint64_t>;
template<>
struct bt_serialize<bt_u64> { void operator()(std::ostream& os, bt_u64 val) { bt_serialize<uint64_t>{}(os, val.val); } };
template<>
struct bt_deserialize<bt_u64> { void operator()(string_view& s, bt_u64& val) { bt_deserialize<uint64_t>{}(s, val.val); } };
struct bt_deserialize<bt_u64> { void operator()(std::string_view& s, bt_u64& val) { bt_deserialize<uint64_t>{}(s, val.val); } };
template <>
struct bt_serialize<string_view> {
void operator()(std::ostream &os, const string_view &val) { os << val.size(); os.put(':'); os.write(val.data(), val.size()); }
struct bt_serialize<std::string_view> {
void operator()(std::ostream &os, const std::string_view &val) { os << val.size(); os.put(':'); os.write(val.data(), val.size()); }
};
template <>
struct bt_deserialize<string_view> {
void operator()(string_view& s, string_view& val);
struct bt_deserialize<std::string_view> {
void operator()(std::string_view& s, std::string_view& val);
};
/// String specialization
template <>
struct bt_serialize<std::string> {
void operator()(std::ostream &os, const std::string &val) { bt_serialize<string_view>{}(os, val); }
void operator()(std::ostream &os, const std::string &val) { bt_serialize<std::string_view>{}(os, val); }
};
template <>
struct bt_deserialize<std::string> {
void operator()(string_view& s, std::string& val) { string_view view; bt_deserialize<string_view>{}(s, view); val = {view.data(), view.size()}; }
void operator()(std::string_view& s, std::string& val) { std::string_view view; bt_deserialize<std::string_view>{}(s, view); val = {view.data(), view.size()}; }
};
/// char * and string literals -- we allow serialization for convenience, but not deserialization
template <>
struct bt_serialize<char *> {
void operator()(std::ostream &os, const char *str) { bt_serialize<string_view>{}(os, {str, std::strlen(str)}); }
void operator()(std::ostream &os, const char *str) { bt_serialize<std::string_view>{}(os, {str, std::strlen(str)}); }
};
template <size_t N>
struct bt_serialize<char[N]> {
void operator()(std::ostream &os, const char *str) { bt_serialize<string_view>{}(os, {str, N-1}); }
void operator()(std::ostream &os, const char *str) { bt_serialize<std::string_view>{}(os, {str, N-1}); }
};
/// Partial dict validity; we don't check the second type for serializability, that will be handled
@ -275,7 +277,7 @@ struct bt_serialize<T, std::enable_if_t<is_bt_input_dict_container<T>::value>> {
template <typename T>
struct bt_deserialize<T, std::enable_if_t<is_bt_output_dict_container<T>::value>> {
using second_type = typename T::value_type::second_type;
void operator()(string_view& s, T& dict) {
void operator()(std::string_view& s, T& dict) {
// Smallest dict is 2 bytes "de", for an empty dict.
if (s.size() < 2) throw bt_deserialize_invalid("Deserialization failed: end of string found where dict expected");
if (s[0] != 'd') throw bt_deserialize_invalid_type("Deserialization failed: expected 'd', found '"s + s[0] + "'"s);
@ -330,7 +332,7 @@ struct bt_serialize<T, std::enable_if_t<is_bt_input_list_container<T>::value>> {
template <typename T>
struct bt_deserialize<T, std::enable_if_t<is_bt_output_list_container<T>::value>> {
using value_type = typename T::value_type;
void operator()(string_view& s, T& list) {
void operator()(std::string_view& s, T& list) {
// Smallest list is 2 bytes "le", for an empty list.
if (s.size() < 2) throw bt_deserialize_invalid("Deserialization failed: end of string found where list expected");
if (s[0] != 'l') throw bt_deserialize_invalid_type("Deserialization failed: expected 'l', found '"s + s[0] + "'"s);
@ -368,20 +370,20 @@ using is_bt_deserializable = std::integral_constant<bool,
// which means we reached the end without finding any variant type capable of holding the value.
template <typename SFINAE, typename Variant, typename... Ts>
struct bt_deserialize_try_variant_impl {
void operator()(string_view&, Variant&) {
void operator()(std::string_view&, Variant&) {
throw bt_deserialize_invalid("Deserialization failed: could not deserialize value into any variant type");
}
};
template <typename... Ts, typename Variant>
void bt_deserialize_try_variant(string_view& s, Variant& variant) {
void bt_deserialize_try_variant(std::string_view& s, Variant& variant) {
bt_deserialize_try_variant_impl<void, Variant, Ts...>{}(s, variant);
}
template <typename Variant, typename T, typename... Ts>
struct bt_deserialize_try_variant_impl<std::enable_if_t<is_bt_deserializable<T>::value>, Variant, T, Ts...> {
void operator()(string_view& s, Variant& variant) {
void operator()(std::string_view& s, Variant& variant) {
if ( is_bt_output_list_container<T>::value ? s[0] == 'l' :
is_bt_output_dict_container<T>::value ? s[0] == 'd' :
std::is_integral<T>::value ? s[0] == 'i' :
@ -398,7 +400,7 @@ struct bt_deserialize_try_variant_impl<std::enable_if_t<is_bt_deserializable<T>:
template <typename Variant, typename T, typename... Ts>
struct bt_deserialize_try_variant_impl<std::enable_if_t<!is_bt_deserializable<T>::value>, Variant, T, Ts...> {
void operator()(string_view& s, Variant& variant) {
void operator()(std::string_view& s, Variant& variant) {
// Unsupported deserialization type, skip it
bt_deserialize_try_variant<Ts...>(s, variant);
}
@ -406,7 +408,7 @@ struct bt_deserialize_try_variant_impl<std::enable_if_t<!is_bt_deserializable<T>
template <>
struct bt_deserialize<bt_value, void> {
void operator()(string_view& s, bt_value& val);
void operator()(std::string_view& s, bt_value& val);
};
template <typename... Ts>
@ -418,7 +420,7 @@ struct bt_serialize<mapbox::util::variant<Ts...>> {
template <typename... Ts>
struct bt_deserialize<mapbox::util::variant<Ts...>> {
void operator()(string_view& s, mapbox::util::variant<Ts...>& val) {
void operator()(std::string_view& s, mapbox::util::variant<Ts...>& val) {
bt_deserialize_try_variant<Ts...>(s, val);
}
};
@ -435,7 +437,7 @@ struct bt_serialize<std::variant<Ts...>> {
template <typename... Ts>
struct bt_deserialize<std::variant<Ts...>> {
void operator()(string_view& s, std::variant<Ts...>& val) {
void operator()(std::string_view& s, std::variant<Ts...>& val) {
bt_deserialize_try_variant<Ts...>(s, val);
}
};
@ -501,7 +503,7 @@ std::string bt_serialize(const T &val) { return bt_serializer(val); }
/// bt_deserialize(encoded, value); // Sets value to 42
///
template <typename T, std::enable_if_t<!std::is_const<T>::value, int> = 0>
void bt_deserialize(string_view s, T& val) {
void bt_deserialize(std::string_view s, T& val) {
return detail::bt_deserialize<T>{}(s, val);
}
@ -512,7 +514,7 @@ void bt_deserialize(string_view s, T& val) {
/// auto mylist = bt_deserialize<std::list<int>>(encoded);
///
template <typename T>
T bt_deserialize(string_view s) {
T bt_deserialize(std::string_view s) {
T val;
bt_deserialize(s, val);
return val;
@ -528,7 +530,7 @@ T bt_deserialize(string_view s) {
/// int v = get_int<int>(val); // fails unless the encoded value was actually an integer that
/// // fits into an `int`
///
inline bt_value bt_get(string_view s) {
inline bt_value bt_get(std::string_view s) {
return bt_deserialize<bt_value>(s);
}
@ -562,10 +564,10 @@ IntType get_int(const bt_value &v) {
/// memory stays valid for the lifetime of the bt_list_consumer object.
class bt_list_consumer {
protected:
string_view data;
std::string_view data;
bt_list_consumer() = default;
public:
bt_list_consumer(string_view data_);
bt_list_consumer(std::string_view data_);
/// Copy constructor. Making a copy copies the current position so can be used for multipass
/// iteration through a list.
@ -588,14 +590,14 @@ public:
/// Attempt to parse the next value as a string (and advance just past it). Throws if the next
/// value is not a string.
std::string consume_string();
string_view consume_string_view();
std::string_view consume_string_view();
/// Attempts to parse the next value as an integer (and advance just past it). Throws if the
/// next value is not an integer.
template <typename IntType>
IntType consume_integer() {
if (!is_integer()) throw bt_deserialize_invalid_type{"next value is not an integer"};
string_view next{data};
std::string_view next{data};
IntType ret;
detail::bt_deserialize<IntType>{}(next, ret);
data = next;
@ -616,7 +618,7 @@ public:
template <typename T>
void consume_list(T& list) {
if (!is_list()) throw bt_deserialize_invalid_type{"next bt value is not a list"};
string_view n{data};
std::string_view n{data};
detail::bt_deserialize<T>{}(n, list);
data = n;
}
@ -635,7 +637,7 @@ public:
template <typename T>
void consume_dict(T& dict) {
if (!is_dict()) throw bt_deserialize_invalid_type{"next bt value is not a dict"};
string_view n{data};
std::string_view n{data};
detail::bt_deserialize<T>{}(n, dict);
data = n;
}
@ -647,13 +649,13 @@ public:
/// entire thing. This is recursive into both lists and dicts and likely to be quite
/// inefficient for large, nested structures (unless the values only need to be skipped but
/// aren't separately needed). This, however, does not require dynamic memory allocation.
string_view consume_list_data();
std::string_view consume_list_data();
/// Attempts to parse the next value as a dict and returns the string_view that contains the
/// entire thing. This is recursive into both lists and dicts and likely to be quite
/// inefficient for large, nested structures (unless the values only need to be skipped but
/// aren't separately needed). This, however, does not require dynamic memory allocation.
string_view consume_dict_data();
std::string_view consume_dict_data();
};
@ -661,7 +663,7 @@ public:
/// copying or allocating memory. It accesses existing memory directly and so the caller must
/// ensure that the referenced memory stays valid for the lifetime of the bt_dict_consumer object.
class bt_dict_consumer : private bt_list_consumer {
string_view key_;
std::string_view key_;
/// Consume the key if not already consumed and there is a key present (rather than 'e').
/// Throws exception if what should be a key isn't a string, or if the key consumes the entire
@ -671,14 +673,14 @@ class bt_dict_consumer : private bt_list_consumer {
/// Clears the cached key and returns it. Must have already called consume_key directly or
/// indirectly via one of the `is_{...}` methods.
string_view flush_key() {
string_view k;
std::string_view flush_key() {
std::string_view k;
k.swap(key_);
return k;
}
public:
bt_dict_consumer(string_view data_);
bt_dict_consumer(std::string_view data_);
/// Copy constructor. Making a copy copies the current position so can be used for multipass
/// iteration through a list.
@ -703,7 +705,7 @@ public:
/// all of the other consume_* methods. The value is cached whether called here or by some
/// other method; accessing it multiple times simple accesses the cache until the next value is
/// consumed.
string_view key() {
std::string_view key() {
if (!consume_key())
throw bt_deserialize_invalid{"Cannot access next key: at the end of the dict"};
return key_;
@ -711,14 +713,14 @@ public:
/// Attempt to parse the next value as a string->string pair (and advance just past it). Throws
/// if the next value is not a string.
std::pair<string_view, string_view> next_string();
std::pair<std::string_view, std::string_view> next_string();
/// Attempts to parse the next value as an string->integer pair (and advance just past it).
/// Throws if the next value is not an integer.
template <typename IntType>
std::pair<string_view, IntType> next_integer() {
std::pair<std::string_view, IntType> next_integer() {
if (!is_integer()) throw bt_deserialize_invalid_type{"next bt dict value is not an integer"};
std::pair<string_view, IntType> ret;
std::pair<std::string_view, IntType> ret;
ret.second = bt_list_consumer::consume_integer<IntType>();
ret.first = flush_key();
return ret;
@ -729,15 +731,15 @@ public:
/// which allows alloc-free traversal, but requires parsing twice (if the contents are to be
/// used).
template <typename T = bt_list>
std::pair<string_view, T> next_list() {
std::pair<string_view, T> pair;
std::pair<std::string_view, T> next_list() {
std::pair<std::string_view, T> pair;
pair.first = consume_list(pair.second);
return pair;
}
/// Same as above, but takes a pre-existing list-like data type. Returns the key.
template <typename T>
string_view next_list(T& list) {
std::string_view next_list(T& list) {
if (!is_list()) throw bt_deserialize_invalid_type{"next bt value is not a list"};
bt_list_consumer::consume_list(list);
return flush_key();
@ -748,15 +750,15 @@ public:
/// which allows alloc-free traversal, but requires parsing twice (if the contents are to be
/// used).
template <typename T = bt_dict>
std::pair<string_view, T> next_dict() {
std::pair<string_view, T> pair;
std::pair<std::string_view, T> next_dict() {
std::pair<std::string_view, T> pair;
pair.first = consume_dict(pair.second);
return pair;
}
/// Same as above, but takes a pre-existing dict-like data type. Returns the key.
template <typename T>
string_view next_dict(T& dict) {
std::string_view next_dict(T& dict) {
if (!is_dict()) throw bt_deserialize_invalid_type{"next bt value is not a dict"};
bt_list_consumer::consume_dict(dict);
return flush_key();
@ -766,25 +768,25 @@ public:
/// contains the entire thing. This is recursive into both lists and dicts and likely to be
/// quite inefficient for large, nested structures (unless the values only need to be skipped
/// but aren't separately needed). This, however, does not require dynamic memory allocation.
std::pair<string_view, string_view> next_list_data() {
std::pair<std::string_view, std::string_view> next_list_data() {
if (data.size() < 2 || !is_list()) throw bt_deserialize_invalid_type{"next bt dict value is not a list"};
return {flush_key(), bt_list_consumer::consume_list_data()};
}
/// Same as next_list_data(), but wraps the value in a bt_list_consumer for convenience
std::pair<string_view, bt_list_consumer> next_list_consumer() { return next_list_data(); }
std::pair<std::string_view, bt_list_consumer> next_list_consumer() { return next_list_data(); }
/// Attempts to parse the next value as a string->dict pair and returns the string_view that
/// contains the entire thing. This is recursive into both lists and dicts and likely to be
/// quite inefficient for large, nested structures (unless the values only need to be skipped
/// but aren't separately needed). This, however, does not require dynamic memory allocation.
std::pair<string_view, string_view> next_dict_data() {
std::pair<std::string_view, std::string_view> next_dict_data() {
if (data.size() < 2 || !is_dict()) throw bt_deserialize_invalid_type{"next bt dict value is not a dict"};
return {flush_key(), bt_list_consumer::consume_dict_data()};
}
/// Same as next_dict_data(), but wraps the value in a bt_dict_consumer for convenience
std::pair<string_view, bt_dict_consumer> next_dict_consumer() { return next_dict_data(); }
std::pair<std::string_view, bt_dict_consumer> next_dict_consumer() { return next_dict_data(); }
/// Skips ahead until we find the first key >= the given key or reach the end of the dict.
/// Returns true if we found an exact match, false if we reached some greater value or the end.
@ -799,7 +801,7 @@ public:
/// - this is irreversible; you cannot returned to skipped values without reparsing. (You *can*
/// however, make a copy of the bt_dict_consumer before calling and use the copy to return to
/// the pre-skipped position).
bool skip_until(string_view find) {
bool skip_until(std::string_view find) {
while (consume_key() && key_ < find) {
flush_key();
skip_value();
@ -834,8 +836,8 @@ public:
template <typename T>
void consume_dict(T& dict) { next_dict(dict); }
string_view consume_list_data() { return next_list_data().second; }
string_view consume_dict_data() { return next_dict_data().second; }
std::string_view consume_list_data() { return next_list_data().second; }
std::string_view consume_dict_data() { return next_dict_data().second; }
bt_list_consumer consume_list_consumer() { return consume_list_data(); }
bt_dict_consumer consume_dict_consumer() { return consume_dict_data(); }

View File

@ -47,7 +47,7 @@ void LokiMQ::setup_external_socket(zmq::socket_t& socket) {
}
}
void LokiMQ::setup_outgoing_socket(zmq::socket_t& socket, string_view remote_pubkey) {
void LokiMQ::setup_outgoing_socket(zmq::socket_t& socket, std::string_view remote_pubkey) {
setup_external_socket(socket);
@ -67,7 +67,7 @@ void LokiMQ::setup_outgoing_socket(zmq::socket_t& socket, string_view remote_pub
// else let ZMQ pick a random one
}
ConnectionID LokiMQ::connect_sn(string_view pubkey, std::chrono::milliseconds keep_alive, string_view hint) {
ConnectionID LokiMQ::connect_sn(std::string_view pubkey, std::chrono::milliseconds keep_alive, std::string_view hint) {
if (!proxy_thread.joinable())
throw std::logic_error("Cannot call connect_sn() before calling `start()`");
@ -76,8 +76,8 @@ ConnectionID LokiMQ::connect_sn(string_view pubkey, std::chrono::milliseconds ke
return pubkey;
}
ConnectionID LokiMQ::connect_remote(string_view remote, ConnectSuccess on_connect, ConnectFailure on_failure,
string_view pubkey, AuthLevel auth_level, std::chrono::milliseconds timeout) {
ConnectionID LokiMQ::connect_remote(std::string_view remote, ConnectSuccess on_connect, ConnectFailure on_failure,
std::string_view pubkey, AuthLevel auth_level, std::chrono::milliseconds timeout) {
if (!proxy_thread.joinable())
throw std::logic_error("Cannot call connect_remote() before calling `start()`");
@ -109,7 +109,7 @@ void LokiMQ::disconnect(ConnectionID id, std::chrono::milliseconds linger) {
}
std::pair<zmq::socket_t *, std::string>
LokiMQ::proxy_connect_sn(string_view remote, string_view connect_hint, bool optional, bool incoming_only, bool outgoing_only, std::chrono::milliseconds keep_alive) {
LokiMQ::proxy_connect_sn(std::string_view remote, std::string_view connect_hint, bool optional, bool incoming_only, bool outgoing_only, std::chrono::milliseconds keep_alive) {
ConnectionID remote_cid{remote};
auto its = peers.equal_range(remote_cid);
peer_info* peer = nullptr;
@ -185,7 +185,7 @@ LokiMQ::proxy_connect_sn(string_view remote, string_view connect_hint, bool opti
}
std::pair<zmq::socket_t *, std::string> LokiMQ::proxy_connect_sn(bt_dict_consumer data) {
string_view hint, remote_pk;
std::string_view hint, remote_pk;
std::chrono::milliseconds keep_alive;
bool optional = false, incoming_only = false, outgoing_only = false;

View File

@ -1,6 +1,6 @@
#pragma once
#include "auth.h"
#include "string_view.h"
#include <string_view>
namespace lokimq {
@ -9,7 +9,7 @@ struct ConnectionID;
namespace detail {
template <typename... T>
bt_dict build_send(ConnectionID to, string_view cmd, T&&... opts);
bt_dict build_send(ConnectionID to, std::string_view cmd, T&&... opts);
}
/// Opaque data structure representing a connection which supports ==, !=, < and std::hash. For
@ -26,7 +26,7 @@ struct ConnectionID {
throw std::runtime_error{"Invalid pubkey: expected 32 bytes"};
}
// Construction from a service node pubkey
ConnectionID(string_view pubkey_) : ConnectionID(std::string{pubkey_}) {}
ConnectionID(std::string_view pubkey_) : ConnectionID(std::string{pubkey_}) {}
ConnectionID(const ConnectionID&) = default;
ConnectionID(ConnectionID&&) = default;
@ -75,7 +75,7 @@ private:
friend class LokiMQ;
friend struct std::hash<ConnectionID>;
template <typename... T>
friend bt_dict detail::build_send(ConnectionID to, string_view cmd, T&&... opts);
friend bt_dict detail::build_send(ConnectionID to, std::string_view cmd, T&&... opts);
friend std::ostream& operator<<(std::ostream& o, const ConnectionID& conn);
};

View File

@ -27,7 +27,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include "string_view.h"
#include <string_view>
#include <array>
#include <iterator>
#include <cassert>
@ -72,19 +72,14 @@ void to_hex(InputIt begin, InputIt end, OutputIt out) {
}
/// Creates a hex string from an iterable, std::string-like object
inline std::string to_hex(string_view s) {
std::string hex;
hex.reserve(s.size() * 2);
to_hex(s.begin(), s.end(), std::back_inserter(hex));
return hex;
}
inline std::string to_hex(ustring_view s) {
template <typename CharT>
std::string to_hex(std::basic_string_view<CharT> s) {
std::string hex;
hex.reserve(s.size() * 2);
to_hex(s.begin(), s.end(), std::back_inserter(hex));
return hex;
}
inline std::string to_hex(std::string_view s) { return to_hex<>(s); }
/// Returns true if all elements in the range are hex characters
template <typename It>
@ -98,8 +93,9 @@ constexpr bool is_hex(It begin, It end) {
}
/// Returns true if all elements in the string-like value are hex characters
constexpr bool is_hex(string_view s) { return is_hex(s.begin(), s.end()); }
constexpr bool is_hex(ustring_view s) { return is_hex(s.begin(), s.end()); }
template <typename CharT>
constexpr bool is_hex(std::basic_string_view<CharT> s) { return is_hex(s.begin(), s.end()); }
constexpr bool is_hex(std::string_view s) { return is_hex(s.begin(), s.end()); }
/// Convert a hex digit into its numeric (0-15) value
constexpr char from_hex_digit(unsigned char x) noexcept {
@ -125,18 +121,13 @@ void from_hex(InputIt begin, InputIt end, OutputIt out) {
/// Converts hex digits from a std::string-like object into a std::string of bytes. Undefined
/// behaviour if any characters are not in [0-9a-fA-F] or if the input sequence length is not even.
inline std::string from_hex(string_view s) {
std::string bytes;
bytes.reserve(s.size() / 2);
from_hex(s.begin(), s.end(), std::back_inserter(bytes));
return bytes;
}
inline std::string from_hex(ustring_view s) {
template <typename CharT>
std::string from_hex(std::basic_string_view<CharT> s) {
std::string bytes;
bytes.reserve(s.size() / 2);
from_hex(s.begin(), s.end(), std::back_inserter(bytes));
return bytes;
}
inline std::string from_hex(std::string_view s) { return from_hex<>(s); }
}

View File

@ -33,7 +33,7 @@ inline zmq::message_t create_message(std::string&& data) {
};
/// Create a message copying from a string_view
inline zmq::message_t create_message(string_view data) {
inline zmq::message_t create_message(std::string_view data) {
return zmq::message_t{data.begin(), data.end()};
}
@ -94,7 +94,7 @@ inline const char* peer_address(zmq::message_t& msg) {
// Returns a string view of the given message data. It's the caller's responsibility to keep the
// referenced message alive. If you want a std::string instead just call `m.to_string()`
inline string_view view(const zmq::message_t& m) {
inline std::string_view view(const zmq::message_t& m) {
return {m.data<char>(), m.size()};
}
@ -108,7 +108,7 @@ inline std::string to_string(AuthLevel a) {
}
}
inline AuthLevel auth_from_string(string_view a) {
inline AuthLevel auth_from_string(std::string_view a) {
if (a == "none") return AuthLevel::none;
if (a == "basic") return AuthLevel::basic;
if (a == "admin") return AuthLevel::admin;
@ -116,7 +116,7 @@ inline AuthLevel auth_from_string(string_view a) {
}
// Extracts and builds the "send" part of a message for proxy_send/proxy_reply
inline std::list<zmq::message_t> build_send_parts(bt_list_consumer send, string_view route) {
inline std::list<zmq::message_t> build_send_parts(bt_list_consumer send, std::string_view route) {
std::list<zmq::message_t> parts;
if (!route.empty())
parts.push_back(create_message(route));
@ -128,7 +128,7 @@ inline std::list<zmq::message_t> build_send_parts(bt_list_consumer send, string_
/// Sends a control message to a specific destination by prefixing the worker name (or identity)
/// then appending the command and optional data (if non-empty). (This is needed when sending the control message
/// to a router socket, i.e. inside the proxy thread).
inline void route_control(zmq::socket_t& sock, string_view identity, string_view cmd, const std::string& data = {}) {
inline void route_control(zmq::socket_t& sock, std::string_view identity, std::string_view cmd, const std::string& data = {}) {
sock.send(create_message(identity), zmq::send_flags::sndmore);
detail::send_control(sock, cmd, data);
}

View File

@ -38,7 +38,7 @@ namespace detail {
// Sends a control messages between proxy and threads or between proxy and workers consisting of a
// single command codes with an optional data part (the data frame is omitted if empty).
void send_control(zmq::socket_t& sock, string_view cmd, std::string data) {
void send_control(zmq::socket_t& sock, std::string_view cmd, std::string data) {
auto c = create_message(std::move(cmd));
if (data.empty()) {
sock.send(c, zmq::send_flags::none);
@ -53,7 +53,7 @@ void send_control(zmq::socket_t& sock, string_view cmd, std::string data) {
std::pair<std::string, AuthLevel> extract_metadata(zmq::message_t& msg) {
auto result = std::make_pair(""s, AuthLevel::none);
try {
string_view pubkey_hex{msg.gets("User-Id")};
std::string_view pubkey_hex{msg.gets("User-Id")};
if (pubkey_hex.size() != 64)
throw std::logic_error("bad user-id");
assert(is_hex(pubkey_hex.begin(), pubkey_hex.end()));

View File

@ -29,6 +29,7 @@
#pragma once
#include <string>
#include <string_view>
#include <list>
#include <queue>
#include <unordered_map>
@ -43,7 +44,6 @@
#include <cassert>
#include "zmq.hpp"
#include "bt_serialize.h"
#include "string_view.h"
#include "connections.h"
#include "message.h"
#include "auth.h"
@ -146,12 +146,12 @@ public:
///
/// @returns an `AuthLevel` enum value indicating the default auth level for the incoming
/// connection, or AuthLevel::denied if the connection should be refused.
using AllowFunc = std::function<AuthLevel(string_view address, string_view pubkey, bool service_node)>;
using AllowFunc = std::function<AuthLevel(std::string_view address, std::string_view pubkey, bool service_node)>;
/// Callback that is invoked when we need to send a "strong" message to a SN that we aren't
/// already connected to and need to establish a connection. This callback returns the ZMQ
/// connection string we should use which is typically a string such as `tcp://1.2.3.4:5678`.
using SNRemoteAddress = std::function<std::string(string_view pubkey)>;
using SNRemoteAddress = std::function<std::string(std::string_view pubkey)>;
/// The callback type for registered commands.
using CommandCallback = std::function<void(Message& message)>;
@ -169,7 +169,7 @@ public:
/// Callback for the success case of connect_remote()
using ConnectSuccess = std::function<void(ConnectionID)>;
/// Callback for the failure case of connect_remote()
using ConnectFailure = std::function<void(ConnectionID, string_view)>;
using ConnectFailure = std::function<void(ConnectionID, std::string_view)>;
/// Explicitly non-copyable, non-movable because most things here aren't copyable, and a few
/// things aren't movable, either. If you need to pass the LokiMQ instance around, wrap it
@ -465,7 +465,7 @@ private:
// provided then the connection will be curve25519 encrypted and authenticate; otherwise it will
// be unencrypted and unauthenticated. Note that the remote end must be in the same mode (i.e.
// either accepting curve connections, or not accepting curve).
void setup_outgoing_socket(zmq::socket_t& socket, string_view remote_pubkey = {});
void setup_outgoing_socket(zmq::socket_t& socket, std::string_view remote_pubkey = {});
/// Common connection implementation used by proxy_connect/proxy_send. Returns the socket and,
/// if a routing prefix is needed, the required prefix (or an empty string if not needed). For
@ -481,7 +481,7 @@ private:
/// @param keep_alive the keep alive for the connection, if we establish a new outgoing
/// connection. If we already have an outgoing connection then its keep-alive gets increased to
/// this if currently less than this.
std::pair<zmq::socket_t*, std::string> proxy_connect_sn(string_view pubkey, string_view connect_hint,
std::pair<zmq::socket_t*, std::string> proxy_connect_sn(std::string_view pubkey, std::string_view connect_hint,
bool optional, bool incoming_only, bool outgoing_only, std::chrono::milliseconds keep_alive);
/// CONNECT_SN command telling us to connect to a new pubkey. Returns the socket (which could
@ -582,7 +582,7 @@ private:
pubkey_set active_service_nodes;
/// Resets or updates the stored set of active SN pubkeys
void proxy_set_active_sns(string_view data);
void proxy_set_active_sns(std::string_view data);
void proxy_set_active_sns(pubkey_set pubkeys);
void proxy_update_active_sns(bt_list_consumer data);
void proxy_update_active_sns(pubkey_set added, pubkey_set removed);
@ -902,7 +902,7 @@ public:
* *don't* need to worry about this (and can just discard it): you can always simply pass the
* pubkey as a string wherever a ConnectionID is called.
*/
ConnectionID connect_sn(string_view pubkey, std::chrono::milliseconds keep_alive = 5min, string_view hint = {});
ConnectionID connect_sn(std::string_view pubkey, std::chrono::milliseconds keep_alive = 5min, std::string_view hint = {});
/**
* Establish a connection to the given remote with callbacks invoked on a successful or failed
@ -934,8 +934,8 @@ public:
* @param returns ConnectionID that uniquely identifies the connection to this remote node. In
* order to talk to it you will need the returned value (or a copy of it).
*/
ConnectionID connect_remote(string_view remote, ConnectSuccess on_connect, ConnectFailure on_failure,
string_view pubkey = {},
ConnectionID connect_remote(std::string_view remote, ConnectSuccess on_connect, ConnectFailure on_failure,
std::string_view pubkey = {},
AuthLevel auth_level = AuthLevel::none,
std::chrono::milliseconds timeout = REMOTE_CONNECT_TIMEOUT);
@ -994,7 +994,7 @@ public:
* connection hint may be used rather than performing a connection address lookup on the pubkey.
*/
template <typename... T>
void send(ConnectionID to, string_view cmd, const T&... opts);
void send(ConnectionID to, std::string_view cmd, const T&... opts);
/** Send a command configured as a "REQUEST" command to a service node: the data parts will be
* prefixed with a random identifier. The remote is expected to reply with a ["REPLY",
@ -1026,7 +1026,7 @@ public:
* not running as a service node.
*/
template <typename... T>
void request(ConnectionID to, string_view cmd, ReplyCallback callback, const T&... opts);
void request(ConnectionID to, std::string_view cmd, ReplyCallback callback, const T&... opts);
/// The key pair this LokiMQ was created with; if empty keys were given during construction then
/// this returns the generated keys.
@ -1257,10 +1257,10 @@ template <typename T> T deserialize_object(uintptr_t ptrval) {
// Sends a control message to the given socket consisting of the command plus optional dict
// data (only sent if the data is non-empty).
void send_control(zmq::socket_t& sock, string_view cmd, std::string data = {});
void send_control(zmq::socket_t& sock, std::string_view cmd, std::string data = {});
/// Base case: takes a string-like value and appends it to the message parts
inline void apply_send_option(bt_list& parts, bt_dict&, string_view arg) {
inline void apply_send_option(bt_list& parts, bt_dict&, std::string_view arg) {
parts.emplace_back(arg);
}
@ -1315,7 +1315,7 @@ inline void apply_send_option(bt_list&, bt_dict& control_data, send_option::queu
std::pair<std::string, AuthLevel> extract_metadata(zmq::message_t& msg);
template <typename... T>
bt_dict build_send(ConnectionID to, string_view cmd, T&&... opts) {
bt_dict build_send(ConnectionID to, std::string_view cmd, T&&... opts) {
bt_dict control_data;
bt_list parts{{cmd}};
#ifdef __cpp_fold_expressions
@ -1339,7 +1339,7 @@ bt_dict build_send(ConnectionID to, string_view cmd, T&&... opts) {
template <typename... T>
void LokiMQ::send(ConnectionID to, string_view cmd, const T&... opts) {
void LokiMQ::send(ConnectionID to, std::string_view cmd, const T&... opts) {
detail::send_control(get_control_socket(), "SEND",
bt_serialize(detail::build_send(std::move(to), cmd, opts...)));
}
@ -1347,17 +1347,17 @@ void LokiMQ::send(ConnectionID to, string_view cmd, const T&... opts) {
std::string make_random_string(size_t size);
template <typename... T>
void LokiMQ::request(ConnectionID to, string_view cmd, ReplyCallback callback, const T &...opts) {
void LokiMQ::request(ConnectionID to, std::string_view cmd, ReplyCallback callback, const T &...opts) {
const auto reply_tag = make_random_string(15); // 15 random bytes is lots and should keep us in most stl implementations' small string optimization
bt_dict control_data = detail::build_send(std::move(to), cmd, reply_tag, opts...);
control_data["request"] = true;
control_data["request_callback"] = detail::serialize_object(std::move(callback));
control_data["request_tag"] = string_view{reply_tag};
control_data["request_tag"] = std::string_view{reply_tag};
detail::send_control(get_control_socket(), "SEND", bt_serialize(std::move(control_data)));
}
template <typename... Args>
void Message::send_back(string_view command, Args&&... args) {
void Message::send_back(std::string_view command, Args&&... args) {
lokimq.send(conn, command, send_option::optional{!conn.sn()}, std::forward<Args>(args)...);
}
@ -1368,14 +1368,14 @@ void Message::send_reply(Args&&... args) {
}
template <typename Callback, typename... Args>
void Message::send_request(string_view cmd, Callback&& callback, Args&&... args) {
void Message::send_request(std::string_view cmd, Callback&& callback, Args&&... args) {
lokimq.request(conn, cmd, std::forward<Callback>(callback),
send_option::optional{!conn.sn()}, std::forward<Args>(args)...);
}
// When log messages are invoked we strip out anything before this in the filename:
constexpr string_view LOG_PREFIX{"lokimq/", 7};
inline string_view trim_log_filename(string_view local_file) {
constexpr std::string_view LOG_PREFIX{"lokimq/", 7};
inline std::string_view trim_log_filename(std::string_view local_file) {
auto chop = local_file.rfind(LOG_PREFIX);
if (chop != local_file.npos)
local_file.remove_prefix(chop);

View File

@ -12,7 +12,7 @@ class LokiMQ;
class Message {
public:
LokiMQ& lokimq; ///< The owning LokiMQ object
std::vector<string_view> data; ///< The provided command data parts, if any.
std::vector<std::string_view> data; ///< The provided command data parts, if any.
ConnectionID conn; ///< The connection info for routing a reply; also contains the pubkey/sn status.
std::string reply_tag; ///< If the invoked command is a request command this is the required reply tag that will be prepended by `send_reply()`.
Access access; ///< The access level of the invoker. This can be higher than the access level of the command, for example for an admin invoking a basic command.
@ -36,7 +36,7 @@ public:
/// If you want to send a non-strong reply even when the remote is a service node then add
/// an explicit `send_option::optional()` argument.
template <typename... Args>
void send_back(string_view, Args&&... args);
void send_back(std::string_view, Args&&... args);
/// Sends a reply to a request. This takes no command: the command is always the built-in
/// "REPLY" command, followed by the unique reply tag, then any reply data parts. All other
@ -51,7 +51,7 @@ public:
/// Sends a request back to whomever sent this message. This is effectively a wrapper around
/// lmq.request() that takes care of setting up the recipient arguments.
template <typename ReplyCallback, typename... Args>
void send_request(string_view cmd, ReplyCallback&& callback, Args&&... args);
void send_request(std::string_view cmd, ReplyCallback&& callback, Args&&... args);
};
}

View File

@ -35,7 +35,7 @@ void LokiMQ::proxy_quit() {
void LokiMQ::proxy_send(bt_dict_consumer data) {
// NB: bt_dict_consumer goes in alphabetical order
string_view hint;
std::string_view hint;
std::chrono::milliseconds keep_alive{DEFAULT_SEND_KEEP_ALIVE};
std::chrono::milliseconds request_timeout{DEFAULT_REQUEST_TIMEOUT};
bool optional = false;
@ -488,7 +488,7 @@ void LokiMQ::proxy_loop() {
}
}
static bool is_error_response(string_view cmd) {
static bool is_error_response(std::string_view cmd) {
return cmd == "FORBIDDEN" || cmd == "FORBIDDEN_SN" || cmd == "NOT_A_SERVICE_NODE" || cmd == "UNKNOWNCOMMAND" || cmd == "NO_REPLY_TAG";
}
@ -498,7 +498,7 @@ bool LokiMQ::proxy_handle_builtin(size_t conn_index, std::vector<zmq::message_t>
// Doubling as a bool and an offset:
size_t incoming = connections[conn_index].getsockopt<int>(ZMQ_TYPE) == ZMQ_ROUTER;
string_view route, cmd;
std::string_view route, cmd;
if (parts.size() < 1 + incoming) {
LMQ_LOG(warn, "Received empty message; ignoring");
return true;
@ -608,7 +608,7 @@ bool LokiMQ::proxy_handle_builtin(size_t conn_index, std::vector<zmq::message_t>
LMQ_LOG(warn, "Received REPLY with unknown or already handled reply tag (", to_hex(reply_tag), "); ignoring");
}
} else {
LMQ_LOG(warn, "Received ", cmd, ':', (parts.size() > 1 + incoming ? view(parts[1 + incoming]) : "(unknown command)"_sv),
LMQ_LOG(warn, "Received ", cmd, ':', (parts.size() > 1 + incoming ? view(parts[1 + incoming]) : "(unknown command)"sv),
" from ", peer_address(parts.back()));
}
return true;

View File

@ -1,310 +1,15 @@
// Copyright (c) 2019-2020, The Loki Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <string>
#ifdef __cpp_lib_string_view
#include <string_view>
namespace lokimq {
// Deprecated type alias for std::string_view
using string_view = std::string_view;
using ustring_view = std::basic_string_view<unsigned char>;
}
#else
#include <ostream>
#include <limits>
namespace lokimq {
/// Basic implementation of std::string_view (except for std::hash support).
template <typename CharT>
class simple_string_view {
const CharT *data_;
size_t size_;
public:
using traits_type = std::char_traits<CharT>;
using value_type = CharT;
using pointer = CharT*;
using const_pointer = const CharT*;
using reference = CharT&;
using const_reference = const CharT&;
using const_iterator = const_pointer;
using iterator = const_iterator;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using reverse_iterator = const_reverse_iterator;
using size_type = std::size_t;
using different_type = std::ptrdiff_t;
static constexpr auto& npos = std::string::npos;
constexpr simple_string_view() noexcept : data_{nullptr}, size_{0} {}
constexpr simple_string_view(const simple_string_view&) noexcept = default;
simple_string_view(const std::basic_string<CharT>& str) : data_{str.data()}, size_{str.size()} {}
constexpr simple_string_view(const CharT* data, size_t size) noexcept : data_{data}, size_{size} {}
simple_string_view(const CharT* data) : data_{data}, size_{traits_type::length(data)} {}
simple_string_view& operator=(const simple_string_view&) = default;
constexpr const CharT* data() const noexcept { return data_; }
constexpr size_t size() const noexcept { return size_; }
constexpr size_t length() const noexcept { return size_; }
constexpr size_t max_size() const noexcept { return std::numeric_limits<size_t>::max(); }
constexpr bool empty() const noexcept { return size_ == 0; }
explicit operator std::basic_string<CharT>() const { return {data_, size_}; }
constexpr const CharT* begin() const noexcept { return data_; }
constexpr const CharT* cbegin() const noexcept { return data_; }
constexpr const CharT* end() const noexcept { return data_ + size_; }
constexpr const CharT* cend() const noexcept { return data_ + size_; }
reverse_iterator rbegin() const { return reverse_iterator{end()}; }
reverse_iterator crbegin() const { return reverse_iterator{end()}; }
reverse_iterator rend() const { return reverse_iterator{begin()}; }
reverse_iterator crend() const { return reverse_iterator{begin()}; }
constexpr const CharT& operator[](size_t pos) const { return data_[pos]; }
constexpr const CharT& front() const { return *data_; }
constexpr const CharT& back() const { return data_[size_ - 1]; }
int compare(simple_string_view s) const;
constexpr void remove_prefix(size_t n) { data_ += n; size_ -= n; }
constexpr void remove_suffix(size_t n) { size_ -= n; }
void swap(simple_string_view &s) noexcept { std::swap(data_, s.data_); std::swap(size_, s.size_); }
#if defined(__clang__) || !defined(__GNUG__) || __GNUC__ >= 6
constexpr // GCC 5.x is buggy wrt constexpr throwing
#endif
const CharT& at(size_t pos) const {
if (pos >= size())
throw std::out_of_range{"invalid string_view index"};
return data_[pos];
};
size_t copy(CharT* dest, size_t count, size_t pos = 0) const {
if (pos > size()) throw std::out_of_range{"invalid copy pos"};
size_t rcount = std::min(count, size_ - pos);
traits_type::copy(dest, data_ + pos, rcount);
return rcount;
}
#if defined(__clang__) || !defined(__GNUG__) || __GNUC__ >= 6
constexpr // GCC 5.x is buggy wrt constexpr throwing
#endif
simple_string_view substr(size_t pos = 0, size_t count = npos) const {
if (pos > size()) throw std::out_of_range{"invalid substr range"};
simple_string_view result = *this;
if (pos > 0) result.remove_prefix(pos);
if (count < result.size()) result.remove_suffix(result.size() - count);
return result;
}
size_t find(simple_string_view v, size_t pos = 0) const {
if (pos > size_ || v.size_ > size_) return npos;
for (const size_t max_pos = size_ - v.size_; pos <= max_pos; ++pos) {
if (0 == traits_type::compare(v.data_, data_ + pos, v.size_))
return pos;
}
return npos;
}
size_t find(CharT c, size_t pos = 0) const { return find({&c, 1}, pos); }
size_t find(const CharT* c, size_t pos, size_t count) const { return find({c, count}, pos); }
size_t find(const CharT* c, size_t pos = 0) const { return find(simple_string_view(c), pos); }
size_t rfind(simple_string_view v, size_t pos = npos) const {
if (v.size_ > size_) return npos;
const size_t max_pos = size_ - v.size_;
for (pos = std::min(pos, max_pos); pos <= max_pos; --pos) {
if (0 == traits_type::compare(v.data_, data_ + pos, v.size_))
return pos;
}
return npos;
}
size_t rfind(CharT c, size_t pos = npos) const { return rfind({&c, 1}, pos); }
size_t rfind(const CharT* c, size_t pos, size_t count) const { return rfind({c, count}, pos); }
size_t rfind(const CharT* c, size_t pos = npos) const { return rfind(simple_string_view(c), pos); }
constexpr size_t find_first_of(simple_string_view v, size_t pos = 0) const noexcept {
for (; pos < size_; ++pos)
for (CharT c : v)
if (data_[pos] == c)
return pos;
return npos;
}
constexpr size_t find_first_of(CharT c, size_t pos = 0) const noexcept { return find_first_of({&c, 1}, pos); }
constexpr size_t find_first_of(const CharT* c, size_t pos, size_t count) const { return find_first_of({c, count}, pos); }
size_t find_first_of(const CharT* c, size_t pos = 0) const { return find_first_of(simple_string_view(c), pos); }
constexpr size_t find_last_of(simple_string_view v, const size_t pos = npos) const noexcept {
if (size_ == 0) return npos;
const size_t last_pos = std::min(pos, size_-1);
for (size_t i = last_pos; i <= last_pos; --i)
for (CharT c : v)
if (data_[i] == c)
return i;
return npos;
}
constexpr size_t find_last_of(CharT c, size_t pos = npos) const noexcept { return find_last_of({&c, 1}, pos); }
constexpr size_t find_last_of(const CharT* c, size_t pos, size_t count) const { return find_last_of({c, count}, pos); }
size_t find_last_of(const CharT* c, size_t pos = npos) const { return find_last_of(simple_string_view(c), pos); }
constexpr size_t find_first_not_of(simple_string_view v, size_t pos = 0) const noexcept {
for (; pos < size_; ++pos) {
bool none = true;
for (CharT c : v) {
if (data_[pos] == c) {
none = false;
break;
}
}
if (none) return pos;
}
return npos;
}
constexpr size_t find_first_not_of(CharT c, size_t pos = 0) const noexcept { return find_first_not_of({&c, 1}, pos); }
constexpr size_t find_first_not_of(const CharT* c, size_t pos, size_t count) const { return find_first_not_of({c, count}, pos); }
size_t find_first_not_of(const CharT* c, size_t pos = 0) const { return find_first_not_of(simple_string_view(c), pos); }
constexpr size_t find_last_not_of(simple_string_view v, const size_t pos = npos) const noexcept {
if (size_ == 0) return npos;
const size_t last_pos = std::min(pos, size_-1);
for (size_t i = last_pos; i <= last_pos; --i) {
bool none = true;
for (CharT c : v) {
if (data_[i] == c) {
none = false;
break;
}
}
if (none) return i;
}
return npos;
}
constexpr size_t find_last_not_of(CharT c, size_t pos = npos) const noexcept { return find_last_not_of({&c, 1}, pos); }
constexpr size_t find_last_not_of(const CharT* c, size_t pos, size_t count) const { return find_last_not_of({c, count}, pos); }
size_t find_last_not_of(const CharT* c, size_t pos = npos) const { return find_last_not_of(simple_string_view(c), pos); }
};
/// We have three of each of these: one with two string views, one with RHS argument deduction, and
/// one with LHS argument deduction, so that you can do (sv == sv), (sv == "foo"), and ("foo" == sv)
template <typename CharT>
inline bool operator==(simple_string_view<CharT> lhs, simple_string_view<CharT> rhs) {
return lhs.size() == rhs.size() && 0 == std::char_traits<CharT>::compare(lhs.data(), rhs.data(), lhs.size());
};
template <typename CharT>
inline bool operator==(simple_string_view<CharT> lhs, std::common_type_t<simple_string_view<CharT>> rhs) {
return lhs.size() == rhs.size() && 0 == std::char_traits<CharT>::compare(lhs.data(), rhs.data(), lhs.size());
};
template <typename CharT>
inline bool operator==(std::common_type_t<simple_string_view<CharT>> lhs, simple_string_view<CharT> rhs) {
return lhs.size() == rhs.size() && 0 == std::char_traits<CharT>::compare(lhs.data(), rhs.data(), lhs.size());
};
template <typename CharT>
inline bool operator!=(simple_string_view<CharT> lhs, simple_string_view<CharT> rhs) {
return !(lhs == rhs);
}
template <typename CharT>
inline bool operator!=(simple_string_view<CharT> lhs, std::common_type_t<simple_string_view<CharT>> rhs) {
return !(lhs == rhs);
}
template <typename CharT>
inline bool operator!=(std::common_type_t<simple_string_view<CharT>> lhs, simple_string_view<CharT> rhs) {
return !(lhs == rhs);
}
template <typename CharT>
inline int simple_string_view<CharT>::compare(simple_string_view s) const {
int cmp = std::char_traits<CharT>::compare(data_, s.data(), std::min(size_, s.size()));
if (cmp) return cmp;
if (size_ < s.size()) return -1;
else if (size_ > s.size()) return 1;
return 0;
}
template <typename CharT>
inline bool operator<(simple_string_view<CharT> lhs, simple_string_view<CharT> rhs) {
return lhs.compare(rhs) < 0;
};
template <typename CharT>
inline bool operator<(simple_string_view<CharT> lhs, std::common_type_t<simple_string_view<CharT>> rhs) {
return lhs.compare(rhs) < 0;
};
template <typename CharT>
inline bool operator<(std::common_type_t<simple_string_view<CharT>> lhs, simple_string_view<CharT> rhs) {
return lhs.compare(rhs) < 0;
};
template <typename CharT>
inline bool operator<=(simple_string_view<CharT> lhs, simple_string_view<CharT> rhs) {
return lhs.compare(rhs) <= 0;
};
template <typename CharT>
inline bool operator<=(simple_string_view<CharT> lhs, std::common_type_t<simple_string_view<CharT>> rhs) {
return lhs.compare(rhs) <= 0;
};
template <typename CharT>
inline bool operator<=(std::common_type_t<simple_string_view<CharT>> lhs, simple_string_view<CharT> rhs) {
return lhs.compare(rhs) <= 0;
};
template <typename CharT>
inline bool operator>(simple_string_view<CharT> lhs, simple_string_view<CharT> rhs) {
return lhs.compare(rhs) > 0;
};
template <typename CharT>
inline bool operator>(simple_string_view<CharT> lhs, std::common_type_t<simple_string_view<CharT>> rhs) {
return lhs.compare(rhs) > 0;
};
template <typename CharT>
inline bool operator>(std::common_type_t<simple_string_view<CharT>> lhs, simple_string_view<CharT> rhs) {
return lhs.compare(rhs) > 0;
};
template <typename CharT>
inline bool operator>=(simple_string_view<CharT> lhs, simple_string_view<CharT> rhs) {
return lhs.compare(rhs) >= 0;
};
template <typename CharT>
inline bool operator>=(simple_string_view<CharT> lhs, std::common_type_t<simple_string_view<CharT>> rhs) {
return lhs.compare(rhs) >= 0;
};
template <typename CharT>
inline bool operator>=(std::common_type_t<simple_string_view<CharT>> lhs, simple_string_view<CharT> rhs) {
return lhs.compare(rhs) >= 0;
};
template <typename CharT>
inline std::basic_ostream<CharT>& operator<<(std::basic_ostream<CharT>& os, const simple_string_view<CharT>& s) {
os.write(s.data(), s.size());
return os;
}
using string_view = simple_string_view<char>;
using ustring_view = simple_string_view<unsigned char>;
}
#endif
// Add a "foo"_sv literal that works exactly like the C++17 "foo"sv literal, but works with our
// implementation in pre-C++17.
namespace lokimq {
// Deprecated "foo"_sv literal; you should use "foo"sv (from <string_view>) instead.
inline namespace literals {
inline constexpr string_view operator""_sv(const char* str, size_t len) { return {str, len}; }
inline constexpr std::string_view operator""_sv(const char* str, size_t len) { return {str, len}; }
}
}

View File

@ -126,7 +126,7 @@ void LokiMQ::proxy_worker_message(std::vector<zmq::message_t>& parts) {
auto route = view(parts[0]), cmd = view(parts[1]);
LMQ_TRACE("worker message from ", route);
assert(route.size() >= 2 && route[0] == 'w' && route[1] >= '0' && route[1] <= '9');
string_view worker_id_str{&route[1], route.size()-1}; // Chop off the leading "w"
std::string_view worker_id_str{&route[1], route.size()-1}; // Chop off the leading "w"
unsigned int worker_id = detail::extract_unsigned(worker_id_str);
if (!worker_id_str.empty() /* didn't consume everything */ || worker_id >= workers.size()) {
LMQ_LOG(error, "Worker id '", route, "' is invalid, unable to process worker command");

View File

@ -10,7 +10,6 @@ set(LMQ_TEST_SRC
test_encoding.cpp
test_failures.cpp
test_requests.cpp
test_string_view.cpp
)
add_executable(tests ${LMQ_TEST_SRC})
@ -22,7 +21,7 @@ pkg_check_modules(SODIUM REQUIRED libsodium)
target_link_libraries(tests Catch2::Catch2 lokimq ${SODIUM_LIBRARIES} Threads::Threads)
set_target_properties(tests PROPERTIES
CXX_STANDARD 14
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)

View File

@ -43,7 +43,7 @@ TEST_CASE("basic commands", "[commands]") {
auto c = client.connect_remote(listen,
[&](auto conn) { pubkey = conn.pubkey(); success = true; got = true; },
[&](auto conn, string_view) { failed = true; got = true; },
[&](auto conn, std::string_view) { failed = true; got = true; },
server.get_pubkey());
wait_for_conn(got);
@ -304,7 +304,7 @@ TEST_CASE("send failure callbacks", "[commands][queue_full]") {
client.send(zmq::message_t{"HI", 2}, zmq::send_flags::none);
zmq::message_t hello;
client.recv(hello);
string_view hello_sv{hello.data<char>(), hello.size()};
std::string_view hello_sv{hello.data<char>(), hello.size()};
{
auto lock = catch_lock();
REQUIRE( hello_sv == "HELLO" );

View File

@ -29,7 +29,7 @@ TEST_CASE("connections with curve authentication", "[curve][connect]") {
bool success = false;
auto server_conn = client.connect_remote(listen,
[&](auto conn) { success = true; got = true; },
[&](auto conn, string_view reason) { auto lock = catch_lock(); INFO("connection failed: " << reason); got = true; },
[&](auto conn, std::string_view reason) { auto lock = catch_lock(); INFO("connection failed: " << reason); got = true; },
pubkey);
wait_for_conn(got);
@ -108,7 +108,7 @@ TEST_CASE("plain-text connections", "[plaintext][connect]") {
bool success = false;
auto c = client.connect_remote(listen,
[&](auto conn) { success = true; got = true; },
[&](auto conn, string_view reason) { auto lock = catch_lock(); INFO("connection failed: " << reason); got = true; }
[&](auto conn, std::string_view reason) { auto lock = catch_lock(); INFO("connection failed: " << reason); got = true; }
);
wait_for_conn(got);
@ -150,11 +150,11 @@ TEST_CASE("unique connection IDs", "[connect][id]") {
std::atomic<bool> good1{false}, good2{false};
auto r1 = client1.connect_remote(listen,
[&](auto conn) { good1 = true; },
[&](auto conn, string_view reason) { auto lock = catch_lock(); INFO("connection failed: " << reason); }
[&](auto conn, std::string_view reason) { auto lock = catch_lock(); INFO("connection failed: " << reason); }
);
auto r2 = client2.connect_remote(listen,
[&](auto conn) { good2 = true; },
[&](auto conn, string_view reason) { auto lock = catch_lock(); INFO("connection failed: " << reason); }
[&](auto conn, std::string_view reason) { auto lock = catch_lock(); INFO("connection failed: " << reason); }
);
wait_for_conn(good1);

View File

@ -31,14 +31,14 @@ TEST_CASE("hex encoding/decoding", "[encoding][decoding][hex]") {
TEST_CASE("base32z encoding/decoding", "[encoding][decoding][base32z]") {
REQUIRE( lokimq::to_base32z("\0\0\0\0\0"s) == "yyyyyyyy" );
REQUIRE( lokimq::to_base32z("\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef"_sv)
REQUIRE( lokimq::to_base32z("\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef"sv)
== "yrtwk3hjixg66yjdeiuauk6p7hy1gtm8tgih55abrpnsxnpm3zzo");
REQUIRE( lokimq::from_base32z("yrtwk3hjixg66yjdeiuauk6p7hy1gtm8tgih55abrpnsxnpm3zzo")
== "\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef"_sv);
== "\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef"sv);
REQUIRE( lokimq::from_base32z("YRTWK3HJIXG66YJDEIUAUK6P7HY1GTM8TGIH55ABRPNSXNPM3ZZO")
== "\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef"_sv);
== "\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef"sv);
auto five_nulls = lokimq::from_base32z("yyyyyyyy");
REQUIRE( five_nulls.size() == 5 );
@ -56,7 +56,7 @@ TEST_CASE("base32z encoding/decoding", "[encoding][decoding][base32z]") {
REQUIRE( lokimq::from_base32z("ybndrfa") == "\x00\x44\x32\x17"s );
// Round-trip it:
REQUIRE( lokimq::from_base32z(lokimq::to_base32z("\x00\x44\x32\x17"_sv)) == "\x00\x44\x32\x17"_sv );
REQUIRE( lokimq::from_base32z(lokimq::to_base32z("\x00\x44\x32\x17"sv)) == "\x00\x44\x32\x17"sv );
REQUIRE( lokimq::to_base32z(lokimq::from_base32z("ybndrfa")) == "ybndrfa" );
// Special case 2: 7 base32z digits with 3 trailing bits 010; we just ignore the trailing stuff,

View File

@ -1,187 +0,0 @@
#include <catch2/catch.hpp>
#include "lokimq/string_view.h"
#include <future>
using namespace lokimq;
using namespace std::literals;
TEST_CASE("string view", "[string_view]") {
std::string foo = "abc 123 xyz";
string_view f1{foo};
string_view f2{"def 789 uvw"};
string_view f3{"nu\0ll", 5};
REQUIRE( f1 == "abc 123 xyz" );
REQUIRE( f2 == "def 789 uvw" );
REQUIRE( f3.size() == 5 );
REQUIRE( f3 == std::string{"nu\0ll", 5} );
REQUIRE( f3 != "nu" );
REQUIRE( f3.data() == "nu"s );
REQUIRE( string_view(f3) == f3 );
auto f4 = f3;
REQUIRE( f4 == f3 );
f4 = f2;
REQUIRE( f4 == "def 789 uvw" );
REQUIRE( f1.size() == 11 );
REQUIRE( f3.length() == 5 );
string_view f5{""};
REQUIRE( !f3.empty() );
REQUIRE( f5.empty() );
REQUIRE( f1[5] == '2' );
size_t i = 0;
for (auto c : f3)
REQUIRE(c == f3[i++]);
std::string backwards;
for (auto it = std::rbegin(f2); it != f2.crend(); ++it)
backwards += *it;
REQUIRE( backwards == "wvu 987 fed" );
REQUIRE( f1.at(10) == 'z' );
REQUIRE_THROWS_AS( f1.at(15), std::out_of_range );
REQUIRE_THROWS_AS( f1.at(11), std::out_of_range );
f4 = f1;
f4.remove_prefix(2);
REQUIRE( f4 == "c 123 xyz" );
f4.remove_prefix(2);
f4.remove_suffix(4);
REQUIRE( f4 == "123" );
f4.remove_prefix(1);
REQUIRE( f4 == "23" );
REQUIRE( f1 == "abc 123 xyz" );
f4.swap(f1);
REQUIRE( f1 == "23" );
REQUIRE( f4 == "abc 123 xyz" );
f1.remove_suffix(2);
REQUIRE( f1.empty() );
REQUIRE( f4 == "abc 123 xyz" );
f1.swap(f4);
REQUIRE( f4.empty() );
REQUIRE( f1 == "abc 123 xyz" );
REQUIRE( f1.front() == 'a' );
REQUIRE( f1.back() == 'z' );
REQUIRE( f1.compare("abc") > 0 );
REQUIRE( f1.compare("abd") < 0 );
REQUIRE( f1.compare("abc 123 xyz") == 0 );
REQUIRE( f1.compare("abc 123 xyza") < 0 );
REQUIRE( f1.compare("abc 123 xy") > 0 );
std::string buf;
buf.resize(5);
f1.copy(&buf[0], 5, 2);
REQUIRE( buf == "c 123" );
buf.resize(100, 'X');
REQUIRE( f1.copy(&buf[0], 100) == 11 );
REQUIRE( buf.substr(0, 11) == f1 );
REQUIRE( buf.substr(11) == std::string(89, 'X') );
REQUIRE( f1.substr(4) == "123 xyz" );
REQUIRE( f1.substr(4, 3) == "123" );
REQUIRE_THROWS_AS( f1.substr(500, 3), std::out_of_range );
REQUIRE( f1.substr(11, 2) == "" );
REQUIRE( f1.substr(8, 500) == "xyz" );
REQUIRE( f1.find("123") == 4 );
REQUIRE( f1.find("abc") == 0 );
REQUIRE( f1.find("xyz") == 8 );
REQUIRE( f1.find("abc 123 xyz 7") == string_view::npos );
REQUIRE( f1.find("23") == 5 );
REQUIRE( f1.find("234") == string_view::npos );
string_view f6{"zz abc abcd abcde abcdef"};
REQUIRE( f6.find("abc") == 3 );
REQUIRE( f6.find("abc", 3) == 3 );
REQUIRE( f6.find("abc", 4) == 7 );
REQUIRE( f6.find("abc", 7) == 7 );
REQUIRE( f6.find("abc", 8) == 12 );
REQUIRE( f6.find("abc", 18) == 18 );
REQUIRE( f6.find("abc", 19) == string_view::npos );
REQUIRE( f6.find("abcd") == 7 );
REQUIRE( f6.rfind("abc") == 18 );
REQUIRE( f6.rfind("abcd") == 18 );
REQUIRE( f6.rfind("bcd") == 19 );
REQUIRE( f6.rfind("abc", 19) == 18 );
REQUIRE( f6.rfind("abc", 18) == 18 );
REQUIRE( f6.rfind("abc", 17) == 12 );
REQUIRE( f6.rfind("abc", 17) == 12 );
REQUIRE( f6.rfind("abc", 8) == 7 );
REQUIRE( f6.rfind("abc", 7) == 7 );
REQUIRE( f6.rfind("abc", 6) == 3 );
REQUIRE( f6.rfind("abc", 3) == 3 );
REQUIRE( f6.rfind("abc", 2) == string_view::npos );
REQUIRE( f6.find('a') == 3 );
REQUIRE( f6.find('a', 17) == 18 );
REQUIRE( f6.find('a', 20) == string_view::npos );
REQUIRE( f6.rfind('a') == 18 );
REQUIRE( f6.rfind('a', 17) == 12 );
REQUIRE( f6.rfind('a', 2) == string_view::npos );
string_view f7{"abc\0def", 7};
REQUIRE( f7.find("c\0d", 0, 3) == 2 );
REQUIRE( f7.find("c\0e", 0, 3) == string_view::npos );
REQUIRE( f7.rfind("c\0d", string_view::npos, 3) == 2 );
REQUIRE( f7.rfind("c\0e", 0, 3) == string_view::npos );
REQUIRE( f6.find_first_of("c789b") == 4 );
REQUIRE( f6.find_first_of("c789") == 5 );
REQUIRE( f2.find_first_of("c789b") == 4 );
REQUIRE( f6.find_first_of("c789b", 6) == 8 );
REQUIRE( f6.find_last_of("c789b") == 20 );
REQUIRE( f6.find_last_of("789b") == 19 );
REQUIRE( f2.find_last_of("c789b") == 6 );
REQUIRE( f6.find_last_of("c789b", 6) == 5 );
REQUIRE( f6.find_last_of("c789b", 5) == 5 );
REQUIRE( f6.find_last_of("c789b", 4) == 4 );
REQUIRE( f6.find_last_of("c789b", 3) == string_view::npos );
REQUIRE( f2.find_first_of(f7) == 0 );
REQUIRE( f3.find_first_of(f7) == 2 );
REQUIRE( f3.find_first_of('\0') == 2 );
REQUIRE( f3.find_first_of("jk\0", 0, 3) == 2 );
REQUIRE( f1.find_first_not_of("abc") == 3 );
REQUIRE( f1.find_first_not_of("abc ", 3) == 4 );
REQUIRE( f1.find_first_not_of(" 123", 3) == 8 );
REQUIRE( f1.find_last_not_of("abc") == 10 );
REQUIRE( f1.find_last_not_of("xyz") == 7 );
REQUIRE( f1.find_last_not_of("xyz 321") == 2 );
REQUIRE( f1.find_last_not_of("xay z1b2c3") == string_view::npos );
REQUIRE( f6.find_last_not_of("def") == 20 );
REQUIRE( f6.find_last_not_of("abcdef") == 17 );
REQUIRE( f6.find_last_not_of("abcdef ") == 1 );
REQUIRE( f6.find_first_not_of('z') == 2 );
REQUIRE( f6.find_first_not_of("z ") == 3 );
REQUIRE( f6.find_first_not_of("a ", 2) == 4 );
REQUIRE( f6.find_last_not_of("abc ", 9) == 1 );
REQUIRE( string_view{"abc"} == string_view{"abc"} );
REQUIRE_FALSE( string_view{"abc"} == string_view{"abd"} );
REQUIRE_FALSE( string_view{"abc"} == string_view{"abcd"} );
REQUIRE( string_view{"abc"} != string_view{"abd"} );
REQUIRE( string_view{"abc"} != string_view{"abcd"} );
REQUIRE( string_view{"abc"} < string_view{"abcd"} );
REQUIRE( string_view{"abc"} < string_view{"abd"} );
REQUIRE_FALSE( string_view{"abd"} < string_view{"abc"} );
REQUIRE_FALSE( string_view{"abcd"} < string_view{"abc"} );
REQUIRE_FALSE( string_view{"abc"} < string_view{"abc"} );
REQUIRE( string_view{"abd"} > string_view{"abc"} );
REQUIRE( string_view{"abcd"} > string_view{"abc"} );
REQUIRE_FALSE( string_view{"abc"} > string_view{"abd"} );
REQUIRE_FALSE( string_view{"abc"} > string_view{"abcd"} );
REQUIRE_FALSE( string_view{"abc"} > string_view{"abc"} );
REQUIRE( string_view{"abc"} <= string_view{"abcd"} );
REQUIRE( string_view{"abc"} <= string_view{"abc"} );
REQUIRE( string_view{"abc"} <= string_view{"abd"} );
REQUIRE( string_view{"abd"} >= string_view{"abc"} );
REQUIRE( string_view{"abc"} >= string_view{"abc"} );
REQUIRE( string_view{"abcd"} >= string_view{"abc"} );
}