Merge 'treewide: improve compatibility with gcc 13' from Avi Kivity

An assortment of patches that reduce our incompatibilities with the upcoming gcc 13.

Closes #13243

* github.com:scylladb/scylladb:
  transport: correctly format unknown opcode
  treewide: catch by reference
  test: raft: avoid confusing string compare
  utils, types, test: extract lexicographical compare utilities
  test: raft: fsm_test: disambiguate raft::configuration construction
  test: reader_concurrency_semaphore_test: handle all enum values
  repair: fix signed/unsigned compare
  repair: fix incorrect signed/unsigned compare
  treewide: avoid unused variables in if statements
  keys: disambiguate construction from initializer_list<bytes>
  cql3: expr: fix serialize_listlike() reference-to-temporary with gcc
  compaction: error on invalid scrub type
  treewide: prevent redefining names
  api: task_manager: fix signed/unsigned compare
  alternator: streams: fix signed/unsigned comparison
  test: fix some mismatched signed/unsigned comparisons
This commit is contained in:
Botond Dénes
2023-03-24 15:16:04 +02:00
29 changed files with 225 additions and 209 deletions

View File

@@ -167,7 +167,7 @@ future<alternator::executor::request_return_type> alternator::executor::list_str
// generate duplicates in a paged listing here. Can obviously miss things if they
// are added between paged calls and end up with a "smaller" UUID/ARN, but that
// is to be expected.
if (limit < cfs.size() || streams_start) {
if (std::cmp_less(limit, cfs.size()) || streams_start) {
std::sort(cfs.begin(), cfs.end(), [](const data_dictionary::table& t1, const data_dictionary::table& t2) {
return t1.schema()->id().uuid() < t2.schema()->id().uuid();
});

View File

@@ -205,7 +205,7 @@ void set_task_manager(http_context& ctx, routes& r, db::config& cfg) {
while (!q.empty()) {
auto& current = q.front();
res.push_back(co_await retrieve_status(current));
for (auto i = 0; i < current->get_children().size(); ++i) {
for (size_t i = 0; i < current->get_children().size(); ++i) {
q.push(co_await current->get_children()[i].copy());
}
q.pop();

View File

@@ -175,7 +175,7 @@ private:
}
class hasher {
const schema* _schema; // pointer instead of reference for default assignment
const ::schema* _schema; // pointer instead of reference for default assignment
public:
explicit hasher(const schema& s) : _schema(&s) { }

View File

@@ -1573,6 +1573,7 @@ future<compaction_manager::compaction_stats_opt> compaction_manager::perform_sst
case sstables::compaction_type_options::scrub::quarantine_mode::only:
return sst->is_quarantined();
}
on_internal_error(cmlog, "bad scrub quarantine mode");
}));
return make_ready_future<std::vector<sstables::shared_sstable>>(std::move(sstables));
}, can_purge_tombstones::no);

View File

@@ -1970,7 +1970,8 @@ static managed_bytes serialize_listlike(const Range& elements, const char* colle
collection_name, elements.size(), std::numeric_limits<int32_t>::max()));
}
for (const managed_bytes_opt& element_opt : elements) {
for (const auto& element : elements) {
const managed_bytes_opt& element_opt = element;
if (element_opt) {
auto& element = *element_opt;
if (element.size() > std::numeric_limits<int32_t>::max()) {

View File

@@ -51,10 +51,10 @@ private:
bool only_keyspace;
};
struct describe_listing {
element_type element_type;
describe_statement::element_type element_type;
};
struct describe_element {
element_type element_type;
describe_statement::element_type element_type;
std::optional<sstring> keyspace;
sstring name;
};

View File

@@ -111,7 +111,7 @@ public:
return _row.is_live(s, column_kind(), base_tombstone, now);
}
column_kind column_kind() const {
::column_kind column_kind() const {
return _key.has_value()
? column_kind::regular_column : column_kind::static_column;
}

View File

@@ -697,6 +697,7 @@ public:
partition_key(std::vector<bytes> v)
: compound_wrapper(managed_bytes(c_type::serialize_value(std::move(v))))
{ }
partition_key(std::initializer_list<bytes> v) : partition_key(std::vector(v)) {}
partition_key(partition_key&& v) = default;
partition_key(const partition_key& v) = default;
@@ -812,6 +813,7 @@ public:
clustering_key_prefix(std::vector<managed_bytes> v)
: prefix_compound_wrapper(compound::element_type::serialize_value(std::move(v)))
{ }
clustering_key_prefix(std::initializer_list<bytes> v) : clustering_key_prefix(std::vector(v)) {}
clustering_key_prefix(clustering_key_prefix&& v) = default;
clustering_key_prefix(const clustering_key_prefix& v) = default;

View File

@@ -697,10 +697,10 @@ future<add_entry_reply> server_impl::execute_modify_config(server_id from,
if (const auto* ex = dynamic_cast<const not_a_leader*>(&e)) {
co_return add_entry_reply{transient_error{std::current_exception(), ex->leader}};
}
if (const auto* ex = dynamic_cast<const dropped_entry*>(&e)) {
if (dynamic_cast<const dropped_entry*>(&e)) {
co_return add_entry_reply{transient_error{std::current_exception(), {}}};
}
if (const auto* ex = dynamic_cast<const conf_change_in_progress*>(&e)) {
if (dynamic_cast<const conf_change_in_progress*>(&e)) {
co_return add_entry_reply{transient_error{std::current_exception(), {}}};
}
throw;

View File

@@ -377,7 +377,7 @@ void repair_module::done(repair_uniq_id id, bool succeeded) {
}
repair_status repair_module::get(int id) const {
if (id > _sequence_number) {
if (std::cmp_greater(id, _sequence_number)) {
throw std::runtime_error(format("unknown repair id {}", id));
}
auto it = _status.find(id);
@@ -390,7 +390,7 @@ repair_status repair_module::get(int id) const {
future<repair_status> repair_module::repair_await_completion(int id, std::chrono::steady_clock::time_point timeout) {
return seastar::with_gate(async_gate(), [this, id, timeout] {
if (id > _sequence_number) {
if (std::cmp_greater(id, _sequence_number)) {
return make_exception_future<repair_status>(std::runtime_error(format("unknown repair id {}", id)));
}
return repeat_until_value([this, id, timeout] {
@@ -922,7 +922,7 @@ private:
future<> shard_repair_task_impl::do_repair_ranges() {
// Repair tables in the keyspace one after another
assert(table_names().size() == table_ids.size());
for (int idx = 0; idx < table_ids.size(); idx++) {
for (size_t idx = 0; idx < table_ids.size(); idx++) {
auto table_id = table_ids[idx];
auto table_name = table_names()[idx];
// repair all the ranges in limited parallelism

View File

@@ -614,12 +614,12 @@ private:
int32_t _min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
int32_t _max_index_interval = 2048;
int32_t _memtable_flush_period = 0;
speculative_retry _speculative_retry = ::speculative_retry(speculative_retry::type::PERCENTILE, 0.99);
::speculative_retry _speculative_retry = ::speculative_retry(speculative_retry::type::PERCENTILE, 0.99);
// This is the compaction strategy that will be used by default on tables which don't have one explicitly specified.
sstables::compaction_strategy_type _compaction_strategy = sstables::compaction_strategy_type::size_tiered;
std::map<sstring, sstring> _compaction_strategy_options;
bool _compaction_enabled = true;
caching_options _caching_options;
::caching_options _caching_options;
table_schema_version _version;
std::unordered_map<sstring, dropped_column> _dropped_columns;
std::map<bytes, data_type> _collections;

View File

@@ -3906,10 +3906,10 @@ public:
void error(gms::inet_address ep, std::exception_ptr eptr) {
sstring why;
error_kind kind = error_kind::FAILURE;
if (auto ex = try_catch<replica::rate_limit_exception>(eptr)) {
if (try_catch<replica::rate_limit_exception>(eptr)) {
// There might be a lot of those, so ignore
kind = error_kind::RATE_LIMIT;
} else if (auto ex = try_catch<rpc::closed_error>(eptr)) {
} else if (try_catch<rpc::closed_error>(eptr)) {
// do not report connection closed exception, gossiper does that
kind = error_kind::DISCONNECT;
} else if (try_catch<rpc::timeout_error>(eptr)) {

View File

@@ -48,7 +48,7 @@ private:
modules _modules;
config _cfg;
seastar::abort_source _as;
optimized_optional<abort_source::subscription> _abort_subscription;
optimized_optional<seastar::abort_source::subscription> _abort_subscription;
serialized_action _update_task_ttl_action;
utils::observer<uint32_t> _task_ttl_observer;
uint32_t _task_ttl;
@@ -99,8 +99,8 @@ public:
foreign_task_vector _children;
shared_promise<> _done;
module_ptr _module;
abort_source _as;
optimized_optional<abort_source::subscription> _shutdown_subscription;
seastar::abort_source _as;
optimized_optional<seastar::abort_source::subscription> _shutdown_subscription;
public:
impl(module_ptr module, task_id id, uint64_t sequence_number, std::string keyspace, std::string table, std::string entity, task_id parent_id) noexcept;
virtual ~impl() = default;
@@ -203,7 +203,7 @@ public:
}
};
public:
task_manager(config cfg, abort_source& as) noexcept;
task_manager(config cfg, seastar::abort_source& as) noexcept;
task_manager() noexcept;
modules& get_modules() noexcept;

View File

@@ -11,7 +11,7 @@
#include <boost/test/unit_test.hpp>
#include <utility>
#include "utils/UUID_gen.hh"
#include "types/types.hh"
#include "utils/lexicographical_compare.hh"
BOOST_AUTO_TEST_CASE(test_generation_of_name_based_UUID) {
auto uuid = utils::UUID_gen::get_name_UUID("systembatchlog");

View File

@@ -315,7 +315,7 @@ SEASTAR_THREAD_TEST_CASE(test_stress_eviction) {
return cf_lru.evict();
});
for (int i = 0; i < (cached_size / page_size); ++i) {
for (size_t i = 0; i < (cached_size / page_size); ++i) {
read_to_string(cf, page_size * i, page_size);
}
@@ -335,7 +335,7 @@ SEASTAR_THREAD_TEST_CASE(test_stress_eviction) {
testlog.debug("Memory: allocated={}, free={}", seastar::memory::stats().allocated_memory(), seastar::memory::stats().free_memory());
testlog.debug("Starting test...");
for (int j = 0; j < n_pages * 16; ++j) {
for (size_t j = 0; j < n_pages * 16; ++j) {
testlog.trace("Allocating");
auto stride = tests::random::get_int(1, 20);
auto page_idx = tests::random::get_int(n_pages - stride);

View File

@@ -231,17 +231,17 @@ SEASTAR_TEST_CASE(test_chunk_reserve) {
for (auto conf :
{ // std::make_pair(reserve size, push count)
std::make_pair(0, 4000),
std::make_pair(100, 4000),
std::make_pair(200, 4000),
std::make_pair(1000, 4000),
std::make_pair(2000, 4000),
std::make_pair(3000, 4000),
std::make_pair(5000, 4000),
std::make_pair(500, 8000),
std::make_pair(1000, 8000),
std::make_pair(2000, 8000),
std::make_pair(8000, 500),
std::make_pair(0u, 4000u),
std::make_pair(100u, 4000u),
std::make_pair(200u, 4000u),
std::make_pair(1000u, 4000u),
std::make_pair(2000u, 4000u),
std::make_pair(3000u, 4000u),
std::make_pair(5000u, 4000u),
std::make_pair(500u, 8000u),
std::make_pair(1000u, 8000u),
std::make_pair(2000u, 8000u),
std::make_pair(8000u, 500u),
})
{
with_allocator(region.allocator(), [&] {
@@ -275,7 +275,7 @@ SEASTAR_TEST_CASE(test_correctness_when_crossing_chunk_boundary) {
size_t max_chunk_size = lsa::chunked_managed_vector<int>::max_chunk_capacity();
lsa::chunked_managed_vector<int> v;
for (auto i = 0; i < (max_chunk_size + 1); i++) {
for (size_t i = 0; i < (max_chunk_size + 1); i++) {
v.push_back(i);
}
BOOST_REQUIRE(v.back() == max_chunk_size);

View File

@@ -168,7 +168,7 @@ SEASTAR_TEST_CASE(test_directory_lister_close) {
auto dl = directory_lister(tmp.path());
auto initial = tests::random::get_int(count);
BOOST_TEST_MESSAGE(fmt::format("Getting {} dir entries", initial));
for (auto i = 0; i < initial; i++) {
for (decltype(initial) i = 0; i < initial; i++) {
auto de = co_await dl.get();
BOOST_REQUIRE(de);
}

View File

@@ -1136,7 +1136,7 @@ SEASTAR_TEST_CASE(flushing_rate_is_reduced_if_compaction_doesnt_keep_up) {
::usleep(sleep_ms * 1000);
co_await db.apply(t.schema(), freeze(gen()), tracing::trace_state_ptr(), db::commitlog::force_sync::yes, db::no_timeout);
co_await t.flush();
BOOST_ASSERT(t.sstables_count() < t.schema()->max_compaction_threshold() * 2);
BOOST_ASSERT(t.sstables_count() < size_t(t.schema()->max_compaction_threshold() * 2));
}
co_await drop_table();
}

View File

@@ -561,7 +561,7 @@ SEASTAR_THREAD_TEST_CASE(test_dht_subtract_ranges) {
dht::partition_range_vector ranges;
ranges.reserve(count);
for (auto i = 0; i < count; i++) {
for (size_t i = 0; i < count; i++) {
dht::token t0 = get_random_token();
dht::token t1 = get_random_token();
if (t1 < t0) {

View File

@@ -1115,6 +1115,7 @@ public:
case state::release_memory: return "state::release_memory";
case state::done: return "state::done";
}
std::abort();
};
private:
reader_concurrency_semaphore& _sem;
@@ -1327,7 +1328,7 @@ memory_limit_table create_memory_limit_table(cql_test_env& env, uint64_t target_
const auto sstable_write_concurrency = 16;
auto num_sstables = 0;
uint64_t num_sstables = 0;
parallel_for_each(boost::irange(0, sstable_write_concurrency), [&] (int i) {
return seastar::async([&] {
while (num_sstables != target_num_sstables) {

View File

@@ -135,7 +135,7 @@ static std::unique_ptr<strategy_control> make_strategy_control_for_test(bool has
static void assert_table_sstable_count(table_for_tests& t, size_t expected_count) {
testlog.info("sstable_set_size={}, live_sstable_count={}, expected={}", t->get_sstables()->size(), t->get_stats().live_sstable_count, expected_count);
BOOST_REQUIRE(t->get_sstables()->size() == expected_count);
BOOST_REQUIRE(t->get_stats().live_sstable_count == expected_count);
BOOST_REQUIRE(uint64_t(t->get_stats().live_sstable_count) == expected_count);
}
SEASTAR_TEST_CASE(compaction_manager_basic_test) {
@@ -4233,13 +4233,13 @@ SEASTAR_TEST_CASE(max_ongoing_compaction_test) {
auto s = schemas[idx];
auto cf = tables[idx];
auto cft = column_family_test(cf);
for (auto i = 0; i < num_sstables; i++) {
for (size_t i = 0; i < num_sstables; i++) {
auto muts = { make_expiring_cell(s, std::chrono::hours(1)) };
cft.add_sstable(make_sstable_containing([&sst_gen, idx] { return sst_gen(idx); }, muts)).get();
}
};
for (auto i = 0; i < num_tables; i++) {
for (size_t i = 0; i < num_tables; i++) {
add_sstables_to_table(i, DEFAULT_MIN_COMPACTION_THRESHOLD);
}
@@ -4551,7 +4551,7 @@ SEASTAR_TEST_CASE(simple_backlog_controller_test) {
auto tiers = get_total_tiers(target_disk_usage);
auto t = create_table();
for (auto tier_idx = 0; tier_idx < tiers; tier_idx++) {
for (size_t tier_idx = 0; tier_idx < tiers; tier_idx++) {
auto tier_size = get_size_for_tier(tier_idx);
if (tier_size > available_space) {
break;
@@ -4628,7 +4628,7 @@ SEASTAR_TEST_CASE(test_compaction_strategy_cleanup_method) {
std::vector<sstables::shared_sstable> candidates;
candidates.reserve(all_files);
for (auto i = 0; i < all_files; i++) {
for (size_t i = 0; i < all_files; i++) {
auto current_step = duration_cast<microseconds>(step_base) * i;
auto sst = make_sstable_containing(sst_gen, {make_mutation(i, next_timestamp(current_step))});
sst->set_sstable_level(sstable_level);

View File

@@ -51,7 +51,7 @@ class test_env {
tmpdir dir;
std::unique_ptr<db::config> db_config;
directory_semaphore dir_sem;
cache_tracker cache_tracker;
::cache_tracker cache_tracker;
gms::feature_service feature_service;
db::nop_large_data_handler nop_ld_handler;
test_env_sstables_manager mgr;

View File

@@ -1089,7 +1089,7 @@ BOOST_AUTO_TEST_CASE(test_empty_configuration) {
server_id id1 = id();
raft::configuration cfg({});
raft::configuration cfg{config_member_set()};
raft::log log(raft::snapshot_descriptor{.idx = index_t{0}, .config = cfg});
auto follower = create_follower(id1, std::move(log));
// Initial state is follower
@@ -1107,7 +1107,7 @@ BOOST_AUTO_TEST_CASE(test_empty_configuration) {
election_timeout(leader);
BOOST_CHECK(leader.is_leader());
// Transitioning to an empty configuration is not supported.
BOOST_CHECK_THROW(leader.add_entry(raft::configuration({})), std::invalid_argument);
BOOST_CHECK_THROW(leader.add_entry(raft::configuration{config_member_set()}), std::invalid_argument);
leader.add_entry(config_from_ids({id1, id2}));
communicate(leader, follower);

View File

@@ -23,7 +23,7 @@ SEASTAR_THREAD_TEST_CASE(test_check_abort_on_client_api) {
cluster.stop_server(0, "test crash").get0();
auto check_error = [](const raft::stopped_error& e) {
return e.what() == sstring("Raft instance is stopped, reason: \"test crash\"");
return sstring(e.what()) == sstring("Raft instance is stopped, reason: \"test crash\"");
};
BOOST_CHECK_EXCEPTION(cluster.add_entries(1, 0).get0(), raft::stopped_error, check_error);
BOOST_CHECK_EXCEPTION(cluster.get_server(0).modify_config({}, {to_raft_id(0)}).get0(), raft::stopped_error, check_error);

View File

@@ -312,15 +312,15 @@ future<call_result_t<M>> call(
}).handle_exception([] (std::exception_ptr eptr) {
try {
std::rethrow_exception(eptr);
} catch (raft::not_a_leader e) {
} catch (raft::not_a_leader& e) {
return make_ready_future<call_result_t<M>>(e);
} catch (raft::not_a_member e) {
} catch (raft::not_a_member& e) {
return make_ready_future<call_result_t<M>>(e);
} catch (raft::dropped_entry e) {
} catch (raft::dropped_entry& e) {
return make_ready_future<call_result_t<M>>(e);
} catch (raft::commit_status_unknown e) {
} catch (raft::commit_status_unknown& e) {
return make_ready_future<call_result_t<M>>(e);
} catch (raft::stopped_error e) {
} catch (raft::stopped_error& e) {
return make_ready_future<call_result_t<M>>(e);
} catch (raft::request_aborted&) {
return make_ready_future<call_result_t<M>>(timed_out_error{});

View File

@@ -103,14 +103,14 @@ int main(int argc, char **argv) {
w.invoke_on_all(&worker::loop).get();
w.stop().get();
for (int i = 0; i < smp::count * phases_scale; i++) {
for (size_t i = 0; i < smp::count * phases_scale; i++) {
sharded<worker> w;
w.start(utils::cross_shard_barrier()).get();
try {
w.invoke_on_all(&worker::loop_with_error).get();
} catch (...) {
auto ph = w.invoke_on(0, [] (auto& w) { return w.get_phase(); }).get0();
for (int c = 1; c < smp::count; c++) {
for (size_t c = 1; c < smp::count; c++) {
auto ph_2 = w.invoke_on(c, [] (auto& w) { return w.get_phase(); }).get0();
if (ph_2 != ph) {
fmt::print("aborted barrier passed shard through\n");

View File

@@ -141,6 +141,7 @@ sstring to_string(cql_binary_opcode op) {
case cql_binary_opcode::AUTH_SUCCESS: return "AUTH_SUCCESS";
case cql_binary_opcode::OPCODES_COUNT: return "OPCODES_COUNT";
}
return format("Unknown CQL binary opcode {}", static_cast<unsigned>(op));
}
sstring to_string(const event::status_change::status_type t) {
@@ -712,7 +713,7 @@ future<> cql_server::connection::process_request() {
? get_units(_server._memory_available, mem_estimate, shedding_timeout).then_wrapped([this, length = f.length] (auto f) {
try {
return make_ready_future<semaphore_units<>>(f.get0());
} catch (semaphore_timed_out sto) {
} catch (semaphore_timed_out& sto) {
// Cancel shedding in case no more requests are going to do that on completion
if (_pending_requests_gate.get_count() == 0) {
_shed_incoming_requests = false;

View File

@@ -33,6 +33,7 @@
#include "utils/managed_bytes.hh"
#include "utils/bit_cast.hh"
#include "utils/chunked_vector.hh"
#include "utils/lexicographical_compare.hh"
#include "tasks/types.hh"
class tuple_type_impl;
@@ -50,159 +51,6 @@ class cql3_type;
}
// Specifies position in a lexicographically ordered sequence
// relative to some value.
//
// For example, if used with a value "bc" with lexicographical ordering on strings,
// each enum value represents the following positions in an example sequence:
//
// aa
// aaa
// b
// ba
// --> before_all_prefixed
// bc
// --> before_all_strictly_prefixed
// bca
// bcd
// --> after_all_prefixed
// bd
// bda
// c
// ca
//
enum class lexicographical_relation : int8_t {
before_all_prefixed,
before_all_strictly_prefixed,
after_all_prefixed
};
// Like std::lexicographical_compare but injects values from shared sequence (types) to the comparator
// Compare is an abstract_type-aware less comparator, which takes the type as first argument.
template <typename TypesIterator, typename InputIt1, typename InputIt2, typename Compare>
bool lexicographical_compare(TypesIterator types, InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2, Compare comp) {
while (first1 != last1 && first2 != last2) {
if (comp(*types, *first1, *first2)) {
return true;
}
if (comp(*types, *first2, *first1)) {
return false;
}
++first1;
++first2;
++types;
}
return (first1 == last1) && (first2 != last2);
}
// Like std::lexicographical_compare but injects values from shared sequence
// (types) to the comparator. Compare is an abstract_type-aware trichotomic
// comparator, which takes the type as first argument.
template <std::input_iterator TypesIterator, std::input_iterator InputIt1, std::input_iterator InputIt2, typename Compare>
requires requires (TypesIterator types, InputIt1 i1, InputIt2 i2, Compare cmp) {
{ cmp(*types, *i1, *i2) } -> std::same_as<std::strong_ordering>;
}
std::strong_ordering lexicographical_tri_compare(TypesIterator types_first, TypesIterator types_last,
InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2,
Compare comp,
lexicographical_relation relation1 = lexicographical_relation::before_all_strictly_prefixed,
lexicographical_relation relation2 = lexicographical_relation::before_all_strictly_prefixed) {
while (types_first != types_last && first1 != last1 && first2 != last2) {
auto c = comp(*types_first, *first1, *first2);
if (c != 0) {
return c;
}
++first1;
++first2;
++types_first;
}
bool e1 = first1 == last1;
bool e2 = first2 == last2;
if (e1 && e2) {
return static_cast<int>(relation1) <=> static_cast<int>(relation2);
}
if (e2) {
return relation2 == lexicographical_relation::after_all_prefixed ? std::strong_ordering::less : std::strong_ordering::greater;
} else if (e1) {
return relation1 == lexicographical_relation::after_all_prefixed ? std::strong_ordering::greater : std::strong_ordering::less;
} else {
return std::strong_ordering::equal;
}
}
// Trichotomic version of std::lexicographical_compare()
template <typename InputIt1, typename InputIt2, typename Compare>
requires requires (InputIt1 i1, InputIt2 i2, Compare c) {
{ c(*i1, *i2) } -> std::same_as<std::strong_ordering>;
}
std::strong_ordering lexicographical_tri_compare(InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2,
Compare comp,
lexicographical_relation relation1 = lexicographical_relation::before_all_strictly_prefixed,
lexicographical_relation relation2 = lexicographical_relation::before_all_strictly_prefixed) {
while (first1 != last1 && first2 != last2) {
auto c = comp(*first1, *first2);
if (c != 0) {
return c;
}
++first1;
++first2;
}
bool e1 = first1 == last1;
bool e2 = first2 == last2;
if (e1 == e2) {
return static_cast<int>(relation1) <=> static_cast<int>(relation2);
}
if (e2) {
return relation2 == lexicographical_relation::after_all_prefixed ? std::strong_ordering::less : std::strong_ordering::greater;
} else {
return relation1 == lexicographical_relation::after_all_prefixed ? std::strong_ordering::greater : std::strong_ordering::less;
}
}
// A trichotomic comparator for prefix equality total ordering.
// In this ordering, two sequences are equal iff any of them is a prefix
// of the another. Otherwise, lexicographical ordering determines the order.
//
// 'comp' is an abstract_type-aware trichotomic comparator, which takes the
// type as first argument.
//
template <typename TypesIterator, typename InputIt1, typename InputIt2, typename Compare>
requires requires (TypesIterator ti, InputIt1 i1, InputIt2 i2, Compare c) {
{ c(*ti, *i1, *i2) } -> std::same_as<std::strong_ordering>;
}
std::strong_ordering prefix_equality_tri_compare(TypesIterator types, InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2, Compare comp) {
while (first1 != last1 && first2 != last2) {
auto c = comp(*types, *first1, *first2);
if (c != 0) {
return c;
}
++first1;
++first2;
++types;
}
return std::strong_ordering::equal;
}
// Returns true iff the second sequence is a prefix of the first sequence
// Equality is an abstract_type-aware equality checker which takes the type as first argument.
template <typename TypesIterator, typename InputIt1, typename InputIt2, typename Equality>
bool is_prefixed_by(TypesIterator types, InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2, Equality equality) {
while (first1 != last1 && first2 != last2) {
if (!equality(*types, *first1, *first2)) {
return false;
}
++first1;
++first2;
++types;
}
return first2 == last2;
}
int64_t timestamp_from_string(sstring_view s);
struct runtime_exception : public std::exception {

View File

@@ -0,0 +1,162 @@
// Copyright 2023-present ScyllaDB
// SPDX-License-Identifier: AGPL-3.0-or-later
#pragma once
#include <compare>
#include <cstdint>
#include <iterator>
// Specifies position in a lexicographically ordered sequence
// relative to some value.
//
// For example, if used with a value "bc" with lexicographical ordering on strings,
// each enum value represents the following positions in an example sequence:
//
// aa
// aaa
// b
// ba
// --> before_all_prefixed
// bc
// --> before_all_strictly_prefixed
// bca
// bcd
// --> after_all_prefixed
// bd
// bda
// c
// ca
//
enum class lexicographical_relation : int8_t {
before_all_prefixed,
before_all_strictly_prefixed,
after_all_prefixed
};
// Like std::lexicographical_compare but injects values from shared sequence (types) to the comparator
// Compare is an abstract_type-aware less comparator, which takes the type as first argument.
template <typename TypesIterator, typename InputIt1, typename InputIt2, typename Compare>
bool lexicographical_compare(TypesIterator types, InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2, Compare comp) {
while (first1 != last1 && first2 != last2) {
if (comp(*types, *first1, *first2)) {
return true;
}
if (comp(*types, *first2, *first1)) {
return false;
}
++first1;
++first2;
++types;
}
return (first1 == last1) && (first2 != last2);
}
// Like std::lexicographical_compare but injects values from shared sequence
// (types) to the comparator. Compare is an abstract_type-aware trichotomic
// comparator, which takes the type as first argument.
template <std::input_iterator TypesIterator, std::input_iterator InputIt1, std::input_iterator InputIt2, typename Compare>
requires requires (TypesIterator types, InputIt1 i1, InputIt2 i2, Compare cmp) {
{ cmp(*types, *i1, *i2) } -> std::same_as<std::strong_ordering>;
}
std::strong_ordering lexicographical_tri_compare(TypesIterator types_first, TypesIterator types_last,
InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2,
Compare comp,
lexicographical_relation relation1 = lexicographical_relation::before_all_strictly_prefixed,
lexicographical_relation relation2 = lexicographical_relation::before_all_strictly_prefixed) {
while (types_first != types_last && first1 != last1 && first2 != last2) {
auto c = comp(*types_first, *first1, *first2);
if (c != 0) {
return c;
}
++first1;
++first2;
++types_first;
}
bool e1 = first1 == last1;
bool e2 = first2 == last2;
if (e1 && e2) {
return static_cast<int>(relation1) <=> static_cast<int>(relation2);
}
if (e2) {
return relation2 == lexicographical_relation::after_all_prefixed ? std::strong_ordering::less : std::strong_ordering::greater;
} else if (e1) {
return relation1 == lexicographical_relation::after_all_prefixed ? std::strong_ordering::greater : std::strong_ordering::less;
} else {
return std::strong_ordering::equal;
}
}
// Trichotomic version of std::lexicographical_compare()
template <typename InputIt1, typename InputIt2, typename Compare>
requires requires (InputIt1 i1, InputIt2 i2, Compare c) {
{ c(*i1, *i2) } -> std::same_as<std::strong_ordering>;
}
std::strong_ordering lexicographical_tri_compare(InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2,
Compare comp,
lexicographical_relation relation1 = lexicographical_relation::before_all_strictly_prefixed,
lexicographical_relation relation2 = lexicographical_relation::before_all_strictly_prefixed) {
while (first1 != last1 && first2 != last2) {
auto c = comp(*first1, *first2);
if (c != 0) {
return c;
}
++first1;
++first2;
}
bool e1 = first1 == last1;
bool e2 = first2 == last2;
if (e1 == e2) {
return static_cast<int>(relation1) <=> static_cast<int>(relation2);
}
if (e2) {
return relation2 == lexicographical_relation::after_all_prefixed ? std::strong_ordering::less : std::strong_ordering::greater;
} else {
return relation1 == lexicographical_relation::after_all_prefixed ? std::strong_ordering::greater : std::strong_ordering::less;
}
}
// A trichotomic comparator for prefix equality total ordering.
// In this ordering, two sequences are equal iff any of them is a prefix
// of the another. Otherwise, lexicographical ordering determines the order.
//
// 'comp' is an abstract_type-aware trichotomic comparator, which takes the
// type as first argument.
//
template <typename TypesIterator, typename InputIt1, typename InputIt2, typename Compare>
requires requires (TypesIterator ti, InputIt1 i1, InputIt2 i2, Compare c) {
{ c(*ti, *i1, *i2) } -> std::same_as<std::strong_ordering>;
}
std::strong_ordering prefix_equality_tri_compare(TypesIterator types, InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2, Compare comp) {
while (first1 != last1 && first2 != last2) {
auto c = comp(*types, *first1, *first2);
if (c != 0) {
return c;
}
++first1;
++first2;
++types;
}
return std::strong_ordering::equal;
}
// Returns true iff the second sequence is a prefix of the first sequence
// Equality is an abstract_type-aware equality checker which takes the type as first argument.
template <typename TypesIterator, typename InputIt1, typename InputIt2, typename Equality>
bool is_prefixed_by(TypesIterator types, InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2, Equality equality) {
while (first1 != last1 && first2 != last2) {
if (!equality(*types, *first1, *first2)) {
return false;
}
++first1;
++first2;
++types;
}
return first2 == last2;
}