blob: b2f4d50e4dc39e78c1bbc89033301e93a9cea724 [file] [log] [blame]
// Copyright 2017-2020 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef VERIBLE_COMMON_UTIL_BIJECTIVE_MAP_H_
#define VERIBLE_COMMON_UTIL_BIJECTIVE_MAP_H_
#include <functional>
#include <map>
#include <utility>
#include "common/util/forward.h"
namespace verible {
// 1-to-1 key-value map that maintains bijectiveness as an invariant.
// Both keys and values are stored once, and internal cross-reference
// maps point to each other's keys.
// Using std::map internally because it provides stable iterators
// across insertion/deletion operations.
template <class K, class V, class KComp = std::less<K>,
class VComp = std::less<V>>
class BijectiveMap {
// Map of keys to values (by pointer).
// Would really rather have value_type be a reverse_map_type::const_iterator
// but cannot due to mutually recursive template type.
typedef std::map<K, const V*, KComp> forward_map_type;
// Map of values to keys (by pointer).
// Would really rather have value_type be a forward_map_type::const_iterator
// but cannot due to mutually recursive template type.
typedef std::map<V, const K*, VComp> reverse_map_type;
public:
BijectiveMap() = default;
// Returns number of keys, which is same as number of values.
size_t size() const { return forward_map.size(); }
bool empty() const { return forward_map.empty(); }
// read-only direct access to internal maps
const forward_map_type& forward_view() const { return forward_map; }
const reverse_map_type& reverse_view() const { return reverse_map; }
// Lookup value given key. Returns nullptr if not found.
template <typename ConvertibleToKey>
const V* find_forward(const ConvertibleToKey& k) const {
auto iter = forward_map.find(
#if __cplusplus >= 201402L
k // heterogeneous lookup enabled (C++14)
#else
// copy temporary rvalue if necessary
ForwardReferenceElseConstruct<K>()(k)
#endif
);
return iter == forward_map.end() ? nullptr : iter->second;
}
// Lookup key given value. Returns nullptr if not found.
template <typename ConvertibleToValue>
const K* find_reverse(const ConvertibleToValue& v) const {
auto iter = reverse_map.find(
#if __cplusplus >= 201402L
v // heterogeneous lookup enabled (C++14)
#else
// copy temporary rvalue if necessary
ForwardReferenceElseConstruct<V>()(v)
#endif
);
return iter == reverse_map.end() ? nullptr : iter->second;
}
bool insert(const std::pair<K, V>& p) { return insert(p.first, p.second); }
// Establishes 1-1 association between key and value.
bool insert(const K& k, const V& v) {
const auto fwd_p = forward_map.insert(std::make_pair(k, nullptr));
const auto rev_p = reverse_map.insert(std::make_pair(v, nullptr));
if (fwd_p.second && rev_p.second) {
// cross-link
// pointers are guaranteed to be stable across non-destructive map
// mutations.
fwd_p.first->second = &rev_p.first->first;
rev_p.first->second = &fwd_p.first->first;
return true;
} else { // either key or value already existed in respective set
// undo any insertions
if (fwd_p.second) {
forward_map.erase(fwd_p.first);
}
if (rev_p.second) {
reverse_map.erase(rev_p.first);
}
return false;
}
}
// function f is a (lazy) value generator that is invoked only when the key
// insertion succeeds. Retries value insertion (calling generator) until
// success. If the range of values generated is small, this could result
// in more frequent retries. Use cases for this include randomizing generated
// values, or using secondary hashes to avoid collisions.
const V* insert_using_value_generator(const K& k, std::function<V()> f) {
const auto fwd_p = forward_map.insert(std::make_pair(k, nullptr));
if (!fwd_p.second) return fwd_p.first->second; // key entry aleady exists
do {
const auto rev_p = reverse_map.insert(std::make_pair(f(), nullptr));
if (rev_p.second) { // successful value insertion
// cross-link
fwd_p.first->second = &rev_p.first->first;
rev_p.first->second = &fwd_p.first->first;
return fwd_p.first->second;
}
} while (true);
// never reached
return nullptr;
}
// TODO(fangism): erase()
private:
// Internal storage of keys, and pointers to values
forward_map_type forward_map;
// Internal storage of values, and pointers to keys
reverse_map_type reverse_map;
};
// TODO(fangism): forward and reverse view adaptors
} // namespace verible
#endif // VERIBLE_COMMON_UTIL_BIJECTIVE_MAP_H_