to disambiguate `fmt::format_to()` from `std::format_to()`. turns out,
we have `using namespace std` somewhere in the source tree, and with
libstdc++ shipped by GCC-13, we have `std::format_to()`, so without
exactly which one to use, compiler complains like
```
/optimized_clang/stage-1-X86/build/bin/clang++ -MD -MT build/dev/mutation/mutation.o -MF build/dev/mutation/mutation.o.d -I/optimized_clang/scylla-X86/seastar/include -I/optimized_clang/scylla-X86/build/dev/seastar/gen/include -U_FORTIFY_SOURCE -DSEASTAR_SSTRING -Werror=unused-result -fstack-clash-protection -DSEASTAR_API_LEVEL=6 -DSEASTAR_BUILD_SHARED_LIBS -DSEASTAR_ENABLE_ALLOC_FAILURE_INJECTION -DSEASTAR_SCHEDULING_GROUPS_COUNT=16 -DSEASTAR_TYPE_ERASE_MORE -DFMT_SHARED -I/usr/include/p11-kit-1 -ffile-prefix-map=/optimized_clang/scylla-X86=. -march=westmere -DDEVEL -DSEASTAR_ENABLE_ALLOC_FAILURE_INJECTION -DSCYLLA_ENABLE_ERROR_INJECTION -O2 -DSCYLLA_BUILD_MODE=dev -iquote. -iquote build/dev/gen --std=gnu++20 -ffile-prefix-map=/optimized_clang/scylla-X86=. -march=westmere -DBOOST_TEST_DYN_LINK -DNOMINMAX -DNOMINMAX -fvisibility=hidden -Wall -Werror -Wno-mismatched-tags -Wno-tautological-compare -Wno-parentheses-equality -Wno-c++11-narrowing -Wno-missing-braces -Wno-ignored-attributes -Wno-overloaded-virtual -Wno-unused-command-line-argument -Wno-unsupported-friend -Wno-delete-non-abstract-non-virtual-dtor -Wno-braced-scalar-init -Wno-implicit-int-float-conversion -Wno-delete-abstract-non-virtual-dtor -Wno-psabi -Wno-narrowing -Wno-nonnull -Wno-uninitialized -Wno-error=deprecated-declarations -DXXH_PRIVATE_API -DSEASTAR_TESTING_MAIN -DFMT_DEPRECATED_OSTREAM -c -o build/dev/mutation/mutation.o mutation/mutation.cc
In file included from mutation/mutation.cc:9:
In file included from mutation/mutation.hh:13:
In file included from mutation/mutation_partition.hh:21:
In file included from ./schema/schema_fwd.hh:13:
In file included from ./utils/UUID.hh:22:
./bytes.hh:116:21: error: call to 'format_to' is ambiguous
format_to(out, "{}{:02x}", _delimiter, std::byte(v[i]));
^~~~~~~~~
./bytes.hh:134:43: note: in instantiation of function template specialization 'fmt::formatter<fmt_hex>::format<fmt::basic_format_context<fmt::appender, char>>' requested here
return fmt::formatter<::fmt_hex>::format(::fmt_hex(bytes_view(s)), ctx);
^
/usr/include/fmt/core.h:813:64: note: in instantiation of function template specialization 'fmt::formatter<seastar::basic_sstring<signed char, unsigned int, 31, false>>::format<fmt::basic_format_context<fmt::appender, char>>' requested here
-> decltype(typename Context::template formatter_type<T>().format(
^
/usr/include/fmt/core.h:824:10: note: while substituting deduced template arguments into function template 'has_const_formatter_impl' [with Context = fmt::basic_format_context<fmt::appender, char>, T = seastar::basic_sstring<signed char, unsigned int, 31, false>]
return has_const_formatter_impl<Context>(static_cast<T*>(nullptr));
```
to address this FTBFS, let's be more explicit by adding "fmt::" to
specify which `format_to()` to use.
Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>
Closes #13361
197 lines
5.7 KiB
C++
197 lines
5.7 KiB
C++
/*
|
|
* Copyright (C) 2015-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "seastarx.hh"
|
|
#include <fmt/format.h>
|
|
#include <seastar/core/sstring.hh>
|
|
#include "utils/hashing.hh"
|
|
#include <optional>
|
|
#include <iosfwd>
|
|
#include <functional>
|
|
#include <compare>
|
|
#include "utils/mutable_view.hh"
|
|
#include <xxhash.h>
|
|
|
|
using bytes = basic_sstring<int8_t, uint32_t, 31, false>;
|
|
using bytes_view = std::basic_string_view<int8_t>;
|
|
using bytes_mutable_view = basic_mutable_view<bytes_view::value_type>;
|
|
using bytes_opt = std::optional<bytes>;
|
|
using sstring_view = std::string_view;
|
|
|
|
inline bytes to_bytes(bytes&& b) {
|
|
return std::move(b);
|
|
}
|
|
|
|
inline sstring_view to_sstring_view(bytes_view view) {
|
|
return {reinterpret_cast<const char*>(view.data()), view.size()};
|
|
}
|
|
|
|
inline bytes_view to_bytes_view(sstring_view view) {
|
|
return {reinterpret_cast<const int8_t*>(view.data()), view.size()};
|
|
}
|
|
|
|
struct fmt_hex {
|
|
const bytes_view& v;
|
|
fmt_hex(const bytes_view& v) noexcept : v(v) {}
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const fmt_hex& hex);
|
|
|
|
bytes from_hex(sstring_view s);
|
|
sstring to_hex(bytes_view b);
|
|
sstring to_hex(const bytes& b);
|
|
sstring to_hex(const bytes_opt& b);
|
|
|
|
std::ostream& operator<<(std::ostream& os, const bytes& b);
|
|
std::ostream& operator<<(std::ostream& os, const bytes_opt& b);
|
|
|
|
template <>
|
|
struct fmt::formatter<fmt_hex> {
|
|
size_t _group_size_in_bytes = 0;
|
|
char _delimiter = ' ';
|
|
public:
|
|
// format_spec := [group_size[delimeter]]
|
|
// group_size := a char from '0' to '9'
|
|
// delimeter := a char other than '{' or '}'
|
|
//
|
|
// by default, the given bytes are printed without delimeter, just
|
|
// like a string. so a string view of {0x20, 0x01, 0x0d, 0xb8} is
|
|
// printed like:
|
|
// "20010db8".
|
|
//
|
|
// but the format specifier can be used to customize how the bytes
|
|
// are printed. for instance, to print an bytes_view like IPv6. so
|
|
// the format specfier would be "{:2:}", where
|
|
// - "2": bytes are printed in groups of 2 bytes
|
|
// - ":": each group is delimeted by ":"
|
|
// and the formatted output will look like:
|
|
// "2001:0db8:0000"
|
|
//
|
|
// or we can mimic how the default format of used by hexdump using
|
|
// "{:2 }", where
|
|
// - "2": bytes are printed in group of 2 bytes
|
|
// - " ": each group is delimeted by " "
|
|
// and the formatted output will look like:
|
|
// "2001 0db8 0000"
|
|
//
|
|
// or we can just print each bytes and separate them by a dash using
|
|
// "{:1-}"
|
|
// and the formatted output will look like:
|
|
// "20-01-0b-b8-00-00"
|
|
constexpr auto parse(fmt::format_parse_context& ctx) {
|
|
// get the delimeter if any
|
|
auto it = ctx.begin();
|
|
auto end = ctx.end();
|
|
if (it != end) {
|
|
int group_size = *it++ - '0';
|
|
if (group_size < 0 ||
|
|
static_cast<size_t>(group_size) > sizeof(uint64_t)) {
|
|
throw format_error("invalid group_size");
|
|
}
|
|
_group_size_in_bytes = group_size;
|
|
if (it != end) {
|
|
// optional delimiter
|
|
_delimiter = *it++;
|
|
}
|
|
}
|
|
if (it != end && *it != '}') {
|
|
throw format_error("invalid format");
|
|
}
|
|
return it;
|
|
}
|
|
template <typename FormatContext>
|
|
auto format(const ::fmt_hex& s, FormatContext& ctx) const {
|
|
auto out = ctx.out();
|
|
const auto& v = s.v;
|
|
if (_group_size_in_bytes > 0) {
|
|
for (size_t i = 0, size = v.size(); i < size; i++) {
|
|
if (i != 0 && i % _group_size_in_bytes == 0) {
|
|
fmt::format_to(out, "{}{:02x}", _delimiter, std::byte(v[i]));
|
|
} else {
|
|
fmt::format_to(out, "{:02x}", std::byte(v[i]));
|
|
}
|
|
}
|
|
} else {
|
|
for (auto b : v) {
|
|
fmt::format_to(out, "{:02x}", std::byte(b));
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct fmt::formatter<bytes> : fmt::formatter<fmt_hex> {
|
|
template <typename FormatContext>
|
|
auto format(const ::bytes& s, FormatContext& ctx) const {
|
|
return fmt::formatter<::fmt_hex>::format(::fmt_hex(bytes_view(s)), ctx);
|
|
}
|
|
};
|
|
|
|
namespace std {
|
|
|
|
// Must be in std:: namespace, or ADL fails
|
|
std::ostream& operator<<(std::ostream& os, const bytes_view& b);
|
|
|
|
}
|
|
|
|
template<>
|
|
struct appending_hash<bytes> {
|
|
template<typename Hasher>
|
|
void operator()(Hasher& h, const bytes& v) const {
|
|
feed_hash(h, v.size());
|
|
h.update(reinterpret_cast<const char*>(v.cbegin()), v.size() * sizeof(bytes::value_type));
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct appending_hash<bytes_view> {
|
|
template<typename Hasher>
|
|
void operator()(Hasher& h, bytes_view v) const {
|
|
feed_hash(h, v.size());
|
|
h.update(reinterpret_cast<const char*>(v.begin()), v.size() * sizeof(bytes_view::value_type));
|
|
}
|
|
};
|
|
|
|
struct bytes_view_hasher : public hasher {
|
|
XXH64_state_t _state;
|
|
bytes_view_hasher(uint64_t seed = 0) noexcept {
|
|
XXH64_reset(&_state, seed);
|
|
}
|
|
void update(const char* ptr, size_t length) noexcept {
|
|
XXH64_update(&_state, ptr, length);
|
|
}
|
|
size_t finalize() {
|
|
return static_cast<size_t>(XXH64_digest(&_state));
|
|
}
|
|
};
|
|
|
|
namespace std {
|
|
template <>
|
|
struct hash<bytes_view> {
|
|
size_t operator()(bytes_view v) const {
|
|
bytes_view_hasher h;
|
|
appending_hash<bytes_view>{}(h, v);
|
|
return h.finalize();
|
|
}
|
|
};
|
|
} // namespace std
|
|
|
|
inline std::strong_ordering compare_unsigned(bytes_view v1, bytes_view v2) {
|
|
auto size = std::min(v1.size(), v2.size());
|
|
if (size) {
|
|
auto n = memcmp(v1.begin(), v2.begin(), size);
|
|
if (n) {
|
|
return n <=> 0;
|
|
}
|
|
}
|
|
return v1.size() <=> v2.size();
|
|
}
|