Browse Source

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
pull/6/head
Jason Rhinelander 2 years ago
parent
commit
7b42537801
  1. 6
      CMakeLists.txt
  2. 18
      lokimq/address.cpp
  3. 4
      lokimq/address.h
  4. 8
      lokimq/auth.cpp
  5. 29
      lokimq/base32z.h
  6. 29
      lokimq/base64.h
  7. 26
      lokimq/bt_serialize.cpp
  8. 116
      lokimq/bt_serialize.h
  9. 12
      lokimq/connections.cpp
  10. 8
      lokimq/connections.h
  11. 29
      lokimq/hex.h
  12. 10
      lokimq/lokimq-internal.h
  13. 4
      lokimq/lokimq.cpp
  14. 44
      lokimq/lokimq.h
  15. 6
      lokimq/message.h
  16. 8
      lokimq/proxy.cpp
  17. 305
      lokimq/string_view.h
  18. 2
      lokimq/worker.cpp
  19. 3
      tests/CMakeLists.txt
  20. 4
      tests/test_commands.cpp
  21. 8
      tests/test_connect.cpp
  22. 8
      tests/test_encoding.cpp
  23. 187
      tests/test_string_view.cpp

6
CMakeLists.txt

@ -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

18
lokimq/address.cpp

@ -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);

4
lokimq/address.h

@ -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

8
lokimq/auth.cpp

@ -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]);

29
lokimq/base32z.h

@ -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); }
}

29
lokimq/base64.h

@ -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); }
}

26
lokimq/bt_serialize.cpp

@ -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;

116
lokimq/bt_serialize.h

@ -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(); }

12
lokimq/connections.cpp

@ -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;

8
lokimq/connections.h

@ -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);
};

29
lokimq/hex.h

@ -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); }
}

10
lokimq/lokimq-internal.h

@ -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);
}

4
lokimq/lokimq.cpp

@ -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()));

44
lokimq/lokimq.h

@ -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