diff --git a/CMakeLists.txt b/CMakeLists.txt index 099d2e8..7efbc08 100644 --- a/CMakeLists.txt +++ b/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 diff --git a/lokimq/address.cpp b/lokimq/address.cpp index fd6bde0..f402e0a 100644 --- a/lokimq/address.cpp +++ b/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 parse_tcp(string_view& addr, bool qr, bool expect_pubkey) { +std::tuple parse_tcp(std::string_view& addr, bool qr, bool expect_pubkey) { std::tuple 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 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 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 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 parse_unix(string_view& addr, bool expect_pubkey) { +std::pair parse_unix(std::string_view& addr, bool expect_pubkey) { std::pair result; if (expect_pubkey) { size_t b64_len = addr.size() > 0 && addr.back() == '=' ? 44 : 43; @@ -134,9 +134,9 @@ std::pair 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); diff --git a/lokimq/address.h b/lokimq/address.h index 6014126..eb097ad 100644 --- a/lokimq/address.h +++ b/lokimq/address.h @@ -28,7 +28,7 @@ #pragma once #include -#include "string_view.h" +#include 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 diff --git a/lokimq/auth.cpp b/lokimq/auth.cpp index 1dba1c8..cd5fd98 100644 --- a/lokimq/auth.cpp +++ b/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(bt_deserialize(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]); diff --git a/lokimq/base32z.h b/lokimq/base32z.h index ca14e1e..cdc08f2 100644 --- a/lokimq/base32z.h +++ b/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 #include #include #include @@ -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 +std::string to_base32z(std::basic_string_view 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 @@ -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 +constexpr bool is_base32z(std::basic_string_view 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 +std::string from_base32z(std::basic_string_view 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); } } diff --git a/lokimq/base64.h b/lokimq/base64.h index 31aa805..51612ce 100644 --- a/lokimq/base64.h +++ b/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 #include #include #include @@ -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 +std::string to_base64(std::basic_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(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 +constexpr bool is_base64(std::basic_string_view 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 +std::string from_base64(std::basic_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(std::string_view s) { return from_base64<>(s); } } diff --git a/lokimq/bt_serialize.cpp b/lokimq/bt_serialize.cpp index b6f0922..cf210ca 100644 --- a/lokimq/bt_serialize.cpp +++ b/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::operator()(string_view& s, string_view& val) { +void bt_deserialize::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::min() + std::numeric_limits static_cast(std::numeric_limits::max()) + uint64_t{1} == (uint64_t{1} << 63), "Non 2s-complement architecture not supported!"); -std::pair bt_deserialize_integer(string_view& s) { +std::pair 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 bt_deserialize_integer(string_view& s) { template struct bt_deserialize; template struct bt_deserialize; -void bt_deserialize::operator()(string_view& s, bt_value& val) { +void bt_deserialize::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::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{}(next, result); + std::string_view next{data}, result; + detail::bt_deserialize{}(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(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(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 bt_dict_consumer::next_string() { +std::pair bt_dict_consumer::next_string() { if (!is_string()) throw bt_deserialize_invalid_type{"expected a string, but found "s + data.front()}; - std::pair ret; + std::pair ret; ret.second = bt_list_consumer::consume_string_view(); ret.first = flush_key(); return ret; diff --git a/lokimq/bt_serialize.h b/lokimq/bt_serialize.h index 52e8441..a65a4de 100644 --- a/lokimq/bt_serialize.h +++ b/lokimq/bt_serialize.h @@ -38,7 +38,7 @@ #include #include #include -#include "string_view.h" +#include #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, @@ -121,8 +121,10 @@ template using void_t = typename void_t_impl::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 @@ -132,7 +134,7 @@ template struct bt_deserialize { static_assert(!std::is_same::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 bt_deserialize_integer(string_view& s); +std::pair bt_deserialize_integer(std::string_view& s); /// Integer specializations template @@ -158,7 +160,7 @@ struct bt_serialize::value>> { template struct bt_deserialize::value>> { - void operator()(string_view& s, T &val) { + void operator()(std::string_view& s, T &val) { constexpr uint64_t umax = static_cast(std::numeric_limits::max()); constexpr int64_t smin = static_cast(std::numeric_limits::min()), smax = static_cast(std::numeric_limits::max()); @@ -191,35 +193,35 @@ extern template struct bt_deserialize; template<> struct bt_serialize { void operator()(std::ostream& os, bt_u64 val) { bt_serialize{}(os, val.val); } }; template<> -struct bt_deserialize { void operator()(string_view& s, bt_u64& val) { bt_deserialize{}(s, val.val); } }; +struct bt_deserialize { void operator()(std::string_view& s, bt_u64& val) { bt_deserialize{}(s, val.val); } }; template <> -struct bt_serialize { - void operator()(std::ostream &os, const string_view &val) { os << val.size(); os.put(':'); os.write(val.data(), val.size()); } +struct bt_serialize { + 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 { - void operator()(string_view& s, string_view& val); +struct bt_deserialize { + void operator()(std::string_view& s, std::string_view& val); }; /// String specialization template <> struct bt_serialize { - void operator()(std::ostream &os, const std::string &val) { bt_serialize{}(os, val); } + void operator()(std::ostream &os, const std::string &val) { bt_serialize{}(os, val); } }; template <> struct bt_deserialize { - void operator()(string_view& s, std::string& val) { string_view view; bt_deserialize{}(s, view); val = {view.data(), view.size()}; } + void operator()(std::string_view& s, std::string& val) { std::string_view view; bt_deserialize{}(s, view); val = {view.data(), view.size()}; } }; /// char * and string literals -- we allow serialization for convenience, but not deserialization template <> struct bt_serialize { - void operator()(std::ostream &os, const char *str) { bt_serialize{}(os, {str, std::strlen(str)}); } + void operator()(std::ostream &os, const char *str) { bt_serialize{}(os, {str, std::strlen(str)}); } }; template struct bt_serialize { - void operator()(std::ostream &os, const char *str) { bt_serialize{}(os, {str, N-1}); } + void operator()(std::ostream &os, const char *str) { bt_serialize{}(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::value>> { template struct bt_deserialize::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::value>> { template struct bt_deserialize::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 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 -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{}(s, variant); } template struct bt_deserialize_try_variant_impl::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::value ? s[0] == 'l' : is_bt_output_dict_container::value ? s[0] == 'd' : std::is_integral::value ? s[0] == 'i' : @@ -398,7 +400,7 @@ struct bt_deserialize_try_variant_impl: template struct bt_deserialize_try_variant_impl::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(s, variant); } @@ -406,7 +408,7 @@ struct bt_deserialize_try_variant_impl template <> struct bt_deserialize { - void operator()(string_view& s, bt_value& val); + void operator()(std::string_view& s, bt_value& val); }; template @@ -418,7 +420,7 @@ struct bt_serialize> { template struct bt_deserialize> { - void operator()(string_view& s, mapbox::util::variant& val) { + void operator()(std::string_view& s, mapbox::util::variant& val) { bt_deserialize_try_variant(s, val); } }; @@ -435,7 +437,7 @@ struct bt_serialize> { template struct bt_deserialize> { - void operator()(string_view& s, std::variant& val) { + void operator()(std::string_view& s, std::variant& val) { bt_deserialize_try_variant(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 ::value, int> = 0> -void bt_deserialize(string_view s, T& val) { +void bt_deserialize(std::string_view s, T& val) { return detail::bt_deserialize{}(s, val); } @@ -512,7 +514,7 @@ void bt_deserialize(string_view s, T& val) { /// auto mylist = bt_deserialize>(encoded); /// template -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(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(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 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{}(next, ret); data = next; @@ -616,7 +618,7 @@ public: template 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{}(n, list); data = n; } @@ -635,7 +637,7 @@ public: template 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{}(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 next_string(); + std::pair 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 - std::pair next_integer() { + std::pair next_integer() { if (!is_integer()) throw bt_deserialize_invalid_type{"next bt dict value is not an integer"}; - std::pair ret; + std::pair ret; ret.second = bt_list_consumer::consume_integer(); 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 - std::pair next_list() { - std::pair pair; + std::pair next_list() { + std::pair 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 - 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 - std::pair next_dict() { - std::pair pair; + std::pair next_dict() { + std::pair 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 - 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 next_list_data() { + std::pair 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 next_list_consumer() { return next_list_data(); } + std::pair 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 next_dict_data() { + std::pair 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 next_dict_consumer() { return next_dict_data(); } + std::pair 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 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(); } diff --git a/lokimq/connections.cpp b/lokimq/connections.cpp index 5674efa..e186813 100644 --- a/lokimq/connections.cpp +++ b/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 -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 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; diff --git a/lokimq/connections.h b/lokimq/connections.h index 34e79cf..dabc973 100644 --- a/lokimq/connections.h +++ b/lokimq/connections.h @@ -1,6 +1,6 @@ #pragma once #include "auth.h" -#include "string_view.h" +#include namespace lokimq { @@ -9,7 +9,7 @@ struct ConnectionID; namespace detail { template -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; template - 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); }; diff --git a/lokimq/hex.h b/lokimq/hex.h index 74e61e4..1f065b7 100644 --- a/lokimq/hex.h +++ b/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 #include #include #include @@ -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 +std::string to_hex(std::basic_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(std::string_view s) { return to_hex<>(s); } /// Returns true if all elements in the range are hex characters template @@ -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 +constexpr bool is_hex(std::basic_string_view 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 +std::string from_hex(std::basic_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(std::string_view s) { return from_hex<>(s); } } diff --git a/lokimq/lokimq-internal.h b/lokimq/lokimq-internal.h index a054ea7..4dfe7b6 100644 --- a/lokimq/lokimq-internal.h +++ b/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(), 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 build_send_parts(bt_list_consumer send, string_view route) { +inline std::list build_send_parts(bt_list_consumer send, std::string_view route) { std::list parts; if (!route.empty()) parts.push_back(create_message(route)); @@ -128,7 +128,7 @@ inline std::list 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); } diff --git a/lokimq/lokimq.cpp b/lokimq/lokimq.cpp index eebb61d..0870b71 100644 --- a/lokimq/lokimq.cpp +++ b/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 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())); diff --git a/lokimq/lokimq.h b/lokimq/lokimq.h index 0a2007f..4f2451d 100644 --- a/lokimq/lokimq.h +++ b/lokimq/lokimq.h @@ -29,6 +29,7 @@ #pragma once #include +#include #include #include #include @@ -43,7 +44,6 @@ #include #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; + using AllowFunc = std::function; /// 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; + using SNRemoteAddress = std::function; /// The callback type for registered commands. using CommandCallback = std::function; @@ -169,7 +169,7 @@ public: /// Callback for the success case of connect_remote() using ConnectSuccess = std::function; /// Callback for the failure case of connect_remote() - using ConnectFailure = std::function; + using ConnectFailure = std::function; /// 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 proxy_connect_sn(string_view pubkey, string_view connect_hint, + std::pair 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 - 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 - 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 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 extract_metadata(zmq::message_t& msg); template -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 -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 -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 -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)...); } @@ -1368,14 +1368,14 @@ void Message::send_reply(Args&&... args) { } template -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), send_option::optional{!conn.sn()}, std::forward(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); diff --git a/lokimq/message.h b/lokimq/message.h index 272e78b..c7abff3 100644 --- a/lokimq/message.h +++ b/lokimq/message.h @@ -12,7 +12,7 @@ class LokiMQ; class Message { public: LokiMQ& lokimq; ///< The owning LokiMQ object - std::vector data; ///< The provided command data parts, if any. + std::vector 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 - 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 - void send_request(string_view cmd, ReplyCallback&& callback, Args&&... args); + void send_request(std::string_view cmd, ReplyCallback&& callback, Args&&... args); }; } diff --git a/lokimq/proxy.cpp b/lokimq/proxy.cpp index 8719734..3e68284 100644 --- a/lokimq/proxy.cpp +++ b/lokimq/proxy.cpp @@ -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 // Doubling as a bool and an offset: size_t incoming = connections[conn_index].getsockopt(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 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; diff --git a/lokimq/string_view.h b/lokimq/string_view.h index b06fb53..a64e015 100644 --- a/lokimq/string_view.h +++ b/lokimq/string_view.h @@ -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 - -#ifdef __cpp_lib_string_view - #include + namespace lokimq { + +// Deprecated type alias for std::string_view using string_view = std::string_view; -using ustring_view = std::basic_string_view; -} -#else - -#include -#include - -namespace lokimq { - -/// Basic implementation of std::string_view (except for std::hash support). -template -class simple_string_view { - const CharT *data_; - size_t size_; -public: - using traits_type = std::char_traits; - 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; - 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& 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::max(); } - constexpr bool empty() const noexcept { return size_ == 0; } - explicit operator std::basic_string() 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 -inline bool operator==(simple_string_view lhs, simple_string_view rhs) { - return lhs.size() == rhs.size() && 0 == std::char_traits::compare(lhs.data(), rhs.data(), lhs.size()); -}; -template -inline bool operator==(simple_string_view lhs, std::common_type_t> rhs) { - return lhs.size() == rhs.size() && 0 == std::char_traits::compare(lhs.data(), rhs.data(), lhs.size()); -}; -template -inline bool operator==(std::common_type_t> lhs, simple_string_view rhs) { - return lhs.size() == rhs.size() && 0 == std::char_traits::compare(lhs.data(), rhs.data(), lhs.size()); -}; -template -inline bool operator!=(simple_string_view lhs, simple_string_view rhs) { - return !(lhs == rhs); -} -template -inline bool operator!=(simple_string_view lhs, std::common_type_t> rhs) { - return !(lhs == rhs); -} -template -inline bool operator!=(std::common_type_t> lhs, simple_string_view rhs) { - return !(lhs == rhs); -} -template -inline int simple_string_view::compare(simple_string_view s) const { - int cmp = std::char_traits::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 -inline bool operator<(simple_string_view lhs, simple_string_view rhs) { - return lhs.compare(rhs) < 0; -}; -template -inline bool operator<(simple_string_view lhs, std::common_type_t> rhs) { - return lhs.compare(rhs) < 0; -}; -template -inline bool operator<(std::common_type_t> lhs, simple_string_view rhs) { - return lhs.compare(rhs) < 0; -}; -template -inline bool operator<=(simple_string_view lhs, simple_string_view rhs) { - return lhs.compare(rhs) <= 0; -}; -template -inline bool operator<=(simple_string_view lhs, std::common_type_t> rhs) { - return lhs.compare(rhs) <= 0; -}; -template -inline bool operator<=(std::common_type_t> lhs, simple_string_view rhs) { - return lhs.compare(rhs) <= 0; -}; -template -inline bool operator>(simple_string_view lhs, simple_string_view rhs) { - return lhs.compare(rhs) > 0; -}; -template -inline bool operator>(simple_string_view lhs, std::common_type_t> rhs) { - return lhs.compare(rhs) > 0; -}; -template -inline bool operator>(std::common_type_t> lhs, simple_string_view rhs) { - return lhs.compare(rhs) > 0; -}; -template -inline bool operator>=(simple_string_view lhs, simple_string_view rhs) { - return lhs.compare(rhs) >= 0; -}; -template -inline bool operator>=(simple_string_view lhs, std::common_type_t> rhs) { - return lhs.compare(rhs) >= 0; -}; -template -inline bool operator>=(std::common_type_t> lhs, simple_string_view rhs) { - return lhs.compare(rhs) >= 0; -}; -template -inline std::basic_ostream& operator<<(std::basic_ostream& os, const simple_string_view& s) { - os.write(s.data(), s.size()); - return os; -} - -using string_view = simple_string_view; -using ustring_view = simple_string_view; - -} - -#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 ) 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}; } } + } diff --git a/lokimq/worker.cpp b/lokimq/worker.cpp index e2df15c..306ea4f 100644 --- a/lokimq/worker.cpp +++ b/lokimq/worker.cpp @@ -126,7 +126,7 @@ void LokiMQ::proxy_worker_message(std::vector& 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"); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 948ae3a..11cfba7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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 ) diff --git a/tests/test_commands.cpp b/tests/test_commands.cpp index d6f63d3..643d736 100644 --- a/tests/test_commands.cpp +++ b/tests/test_commands.cpp @@ -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(), hello.size()}; + std::string_view hello_sv{hello.data(), hello.size()}; { auto lock = catch_lock(); REQUIRE( hello_sv == "HELLO" ); diff --git a/tests/test_connect.cpp b/tests/test_connect.cpp index 12edde9..0ea0084 100644 --- a/tests/test_connect.cpp +++ b/tests/test_connect.cpp @@ -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 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); diff --git a/tests/test_encoding.cpp b/tests/test_encoding.cpp index c1238b4..cd763ff 100644 --- a/tests/test_encoding.cpp +++ b/tests/test_encoding.cpp @@ -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, diff --git a/tests/test_string_view.cpp b/tests/test_string_view.cpp deleted file mode 100644 index dd87f35..0000000 --- a/tests/test_string_view.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include -#include "lokimq/string_view.h" -#include - -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"} ); -}