| // 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_ |