#ifndef NET_DISK_CACHE_SQL_INDEXED_PAIR_SET_H_
#define NET_DISK_CACHE_SQL_INDEXED_PAIR_SET_H_
#include <optional>
#include <vector>
#include "base/check.h"
#include "net/base/net_export.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_set.h"
namespace disk_cache {
template <class Key, class Value>
class NET_EXPORT_PRIVATE IndexedPairSet {
public:
IndexedPairSet() = default;
~IndexedPairSet() = default;
IndexedPairSet(const IndexedPairSet&) = delete;
IndexedPairSet& operator=(const IndexedPairSet&) = delete;
IndexedPairSet(IndexedPairSet&& other) noexcept
: primary_map_(std::move(other.primary_map_)),
secondary_map_(std::move(other.secondary_map_)),
size_(other.size_) {
other.size_ = 0;
}
IndexedPairSet& operator=(IndexedPairSet&& other) noexcept {
if (this == &other) {
return *this;
}
primary_map_ = std::move(other.primary_map_);
secondary_map_ = std::move(other.secondary_map_);
size_ = other.size_;
other.size_ = 0;
return *this;
}
bool Insert(Key key, Value value) {
auto primary_it = primary_map_.find(key);
if (primary_it == primary_map_.end()) {
primary_map_.insert({key, value});
size_++;
return true;
}
if (primary_it->second == value) {
return false;
}
auto insert_result = secondary_map_[key].insert(value);
if (insert_result.second) {
size_++;
return true;
}
return false;
}
std::vector<Value> Find(Key key) const {
std::vector<Value> results;
auto primary_it = primary_map_.find(key);
if (primary_it == primary_map_.end()) {
return results;
}
results.push_back(primary_it->second);
auto secondary_it = secondary_map_.find(key);
if (secondary_it != secondary_map_.end()) {
results.insert(results.end(), secondary_it->second.begin(),
secondary_it->second.end());
}
return results;
}
bool Remove(Key key, Value value) {
auto primary_it = primary_map_.find(key);
if (primary_it == primary_map_.end()) {
return false;
}
if (primary_it->second == value) {
auto secondary_it = secondary_map_.find(key);
if (secondary_it != secondary_map_.end()) {
CHECK(!secondary_it->second.empty());
auto& secondary_set = secondary_it->second;
Value new_base_value = *secondary_set.begin();
secondary_set.erase(secondary_set.begin());
primary_it->second = new_base_value;
if (secondary_set.empty()) {
secondary_map_.erase(secondary_it);
}
} else {
primary_map_.erase(primary_it);
}
size_--;
return true;
}
auto secondary_it = secondary_map_.find(key);
if (secondary_it != secondary_map_.end()) {
if (secondary_it->second.erase(value) > 0) {
if (secondary_it->second.empty()) {
secondary_map_.erase(secondary_it);
}
size_--;
return true;
}
}
return false;
}
bool Contains(Key key) const { return primary_map_.contains(key); }
size_t size() const { return size_; }
bool empty() const { return size_ == 0; }
void Clear() {
primary_map_.clear();
secondary_map_.clear();
size_ = 0;
}
bool HasMultipleValues(const Key& key) const {
return secondary_map_.contains(key);
}
std::optional<Value> TryGetSingleValue(const Key& key) const {
if (HasMultipleValues(key)) {
return std::nullopt;
}
if (const auto primary_it = primary_map_.find(key);
primary_it != primary_map_.end()) {
return primary_it->second;
}
return std::nullopt;
}
private:
absl::flat_hash_map<Key, Value> primary_map_;
absl::flat_hash_map<Key, absl::flat_hash_set<Value>> secondary_map_;
size_t size_ = 0;
};
}
#endif