// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #pragma once #include "enumstore.h" #include "enumcomparator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace search { using vespalib::datastore::CompactionStrategy; using vespalib::datastore::EntryComparator; template Comparator make_enum_store_comparator(const DataStore& data_store, const DictionaryConfig& dict_cfg) { if constexpr (std::is_same_v) { return Comparator(data_store, dict_cfg.getMatch() == DictionaryConfig::Match::CASED); } else { return Comparator(data_store); } } std::unique_ptr make_enum_store_dictionary(IEnumStore &store, bool has_postings, const search::DictionaryConfig & dict_cfg, std::unique_ptr compare, std::unique_ptr folded_compare); template void EnumStoreT::free_value_if_unused(Index idx, IndexList& unused) { const auto& entry = get_entry_base(idx); if (entry.get_ref_count() == 0) { unused.push_back(idx); _store.get_allocator().hold(idx); } } template ssize_t EnumStoreT::load_unique_values_internal(const void* src, size_t available, IndexVector& idx) { size_t left = available; const char* p = static_cast(src); Index idx1; while (left > 0) { ssize_t sz = load_unique_value(p, left, idx1); if (sz < 0) { return sz; } p += sz; left -= sz; idx.push_back(idx1); } return available - left; } template ssize_t EnumStoreT::load_unique_value(const void* src, size_t available, Index& idx) { if (available < sizeof(EntryType)) { return -1; } const auto* value = static_cast(src); idx = _store.get_allocator().allocate(*value); return sizeof(EntryType); } template EnumStoreT::EnumStoreT(bool has_postings, const DictionaryConfig& dict_cfg, std::shared_ptr memory_allocator, EntryType default_value) : _store(std::move(memory_allocator), [&dict_cfg](const auto& data_store) { return make_enum_store_comparator(data_store, dict_cfg); }), _dict(), _is_folded(dict_cfg.getMatch() == DictionaryConfig::Match::UNCASED), _foldedComparator(make_optionally_folded_comparator(is_folded())), _compaction_spec(), _default_value(default_value), _default_value_ref() { _store.set_dictionary(make_enum_store_dictionary(*this, has_postings, dict_cfg, allocate_comparator(), allocate_optionally_folded_comparator(is_folded()))); _dict = static_cast(&_store.get_dictionary()); setup_default_value_ref(); } template EnumStoreT::EnumStoreT(bool has_postings, const DictionaryConfig& dict_cfg) : EnumStoreT(has_postings, dict_cfg, {}, attribute::getUndefined()) { } template EnumStoreT::~EnumStoreT() = default; template vespalib::AddressSpace EnumStoreT::get_values_address_space_usage() const { return _store.get_values_address_space_usage(); } template void EnumStoreT::assign_generation(generation_t current_gen) { _store.assign_generation(current_gen); } template void EnumStoreT::reclaim_memory(generation_t oldest_used_gen) { // remove generations in the range [0, firstUsed> _store.reclaim_memory(oldest_used_gen); } template ssize_t EnumStoreT::load_unique_values(const void* src, size_t available, IndexVector& idx) { ssize_t sz = load_unique_values_internal(src, available, idx); return sz; } template bool EnumStoreT::get_value(Index idx, EntryT& value) const { if (!idx.valid()) { return false; } value = _store.get(idx); return true; } template EnumStoreT::NonEnumeratedLoader::~NonEnumeratedLoader() = default; template IEnumStore::Index EnumStoreT::BatchUpdater::insert(EntryType value) { auto cmp = _store.make_comparator(value); auto result = _store._dict->add(cmp, [this, &value]() -> EntryRef { return _store._store.get_allocator().allocate(value); }); if (result.inserted()) { _possibly_unused.push_back(result.ref()); } return result.ref(); } template void EnumStoreT::write_value(BufferWriter& writer, Index idx) const { writer.write(&_store.get(idx), sizeof(EntryType)); } template bool EnumStoreT::is_folded_change(Index idx1, Index idx2) const { const auto & cmp = get_folded_comparator(); assert(!cmp.less(idx2, idx1)); return cmp.less(idx1, idx2); } template bool EnumStoreT::find_enum(EntryType value, IEnumStore::EnumHandle& e) const { auto cmp = make_comparator(value); Index idx; if (_dict->find_frozen_index(cmp, idx)) { e = idx.ref(); return true; } return false; } template bool EnumStoreT::find_index(EntryType value, Index& idx) const { auto cmp = make_comparator(value); return _dict->find_index(cmp, idx); } template void EnumStoreT::free_unused_values() { _dict->free_unused_values(get_comparator()); } template void EnumStoreT::free_unused_values(IndexList to_remove) { struct CompareEnumIndex { bool operator()(const Index &lhs, const Index &rhs) const { return lhs.ref() < rhs.ref(); } }; std::sort(to_remove.begin(), to_remove.end(), CompareEnumIndex()); _dict->free_unused_values(to_remove, get_comparator()); } template IEnumStore::Index EnumStoreT::insert(EntryType value) { return _store.add(value).ref(); } template void EnumStoreT::clear_default_value_ref() { auto ref = _default_value_ref.load_relaxed(); if (ref.valid()) { auto updater = make_batch_updater(); updater.dec_ref_count(ref); _default_value_ref.store_relaxed(Index()); updater.commit(); } } template void EnumStoreT::setup_default_value_ref() { if (!_default_value_ref.load_relaxed().valid()) { auto updater = make_batch_updater(); auto ref = updater.insert(_default_value); updater.inc_ref_count(ref); _default_value_ref.store_relaxed(ref); updater.commit(); } } template vespalib::MemoryUsage EnumStoreT::update_stat(const CompactionStrategy& compaction_strategy) { return _compaction_spec.update_stat(*this, compaction_strategy); } template std::unique_ptr EnumStoreT::consider_compact_values(const CompactionStrategy& compaction_strategy) { if (!_store.get_data_store().has_held_buffers() && _compaction_spec.get_values().compact()) { return compact_worst_values(_compaction_spec.get_values(), compaction_strategy); } return {}; } template std::unique_ptr EnumStoreT::compact_worst_values(CompactionSpec compaction_spec, const CompactionStrategy& compaction_strategy) { auto remapper = _store.compact_worst(compaction_spec, compaction_strategy); if (remapper) { auto ref = _default_value_ref.load_relaxed(); if (ref.valid() && remapper->get_entry_ref_filter().has(ref)) { _default_value_ref.store_release(remapper->remap(ref)); } } return remapper; } template bool EnumStoreT::consider_compact_dictionary(const CompactionStrategy& compaction_strategy) { if (_dict->has_held_buffers()) { return false; } if (_compaction_spec.btree_dictionary()) { _dict->compact_worst(true, false, compaction_strategy); return true; } if (_compaction_spec.hash_dictionary()) { _dict->compact_worst(false, true, compaction_strategy); return true; } return false; } template std::unique_ptr EnumStoreT::make_enumerator() { return std::make_unique(*_dict, _store.get_data_store(), false); } template std::unique_ptr EnumStoreT::allocate_comparator() const { return std::make_unique(_store.get_comparator()); } template std::unique_ptr EnumStoreT::allocate_optionally_folded_comparator(bool folded) const { return (has_string_type && folded) ? std::make_unique(_store.get_comparator().make_folded()) : std::unique_ptr(); } template typename EnumStoreT::ComparatorType EnumStoreT::make_optionally_folded_comparator(bool folded) const { return (has_string_type && folded) ? _store.get_comparator().make_folded() : _store.get_comparator(); } }