/* * Copyright 2020 The Android Open Source Project * * 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. */ #pragma once #include #include #include #include #include #include #include #include namespace bluetooth { namespace common { // A map that maintains order of its element as a list. An element that is put earlier will appear before an element // that is put later when iterating through this map's entries. Keys must be unique. // // Performance: // - Key look-up and modification is O(1) // - Value operated by replacement, no in-place modification // - Memory consumption is: // O(2*capacity*sizeof(K) + capacity*(sizeof(nullptr)+sizeof(V))) // - NOT THREAD SAFE // // Template: // - Key key // - T value template class ListMap { public: using value_type = std::pair; // different from c++17 node_type on purpose as we want node to be copyable using node_type = std::pair; using iterator = typename std::list::iterator; using const_iterator = typename std::list::const_iterator; // Constructor of the list map ListMap() = default; // for move ListMap(ListMap&& other) noexcept = default; ListMap& operator=(ListMap&& other) noexcept = default; // copy-constructor // iterators in key_map_ cannot be copied directly ListMap(const ListMap& other) : node_list_(other.node_list_) { for (auto iter = node_list_.begin(); iter != node_list_.end(); iter++) { key_map_.emplace(iter->first, iter); } } // copy-assignment // iterators in key_map_ cannot be copied directly ListMap& operator=(const ListMap& other) { if (&other == this) { return *this; } node_list_ = other.node_list_; key_map_.clear(); for (auto iter = node_list_.begin(); iter != node_list_.end(); iter++) { key_map_.emplace(iter->first, iter); } return *this; } // comparison operators bool operator==(const ListMap& rhs) const { return node_list_ == rhs.node_list_; } bool operator!=(const ListMap& rhs) const { return !(*this == rhs); } ~ListMap() { clear(); } // Clear the list map void clear() { key_map_.clear(); node_list_.clear(); } // const version of find() const_iterator find(const Key& key) const { return const_cast(this)->find(key); } // Get the value of a key. Return iterator to the item if found, end() if not found iterator find(const Key& key) { auto map_iterator = key_map_.find(key); if (map_iterator == key_map_.end()) { return end(); } return map_iterator->second; } // Check if key exist in the map. Return true if key exist in map, false if not. bool contains(const Key& key) const { return find(key) != end(); } // Try emplace an element before a specific position |pos| of the list map. If the |key| already exists, does nothing. // Moved arguments won't be moved when key already exists. Return when key does not exist, when key exist and iterator is the position where it was placed. template std::pair try_emplace(const_iterator pos, const Key& key, Args&&... args) { auto map_iterator = key_map_.find(key); if (map_iterator != key_map_.end()) { return std::make_pair(end(), false); } auto list_iterator = node_list_.emplace(pos, key, std::forward(args)...); key_map_.emplace(key, list_iterator); return std::make_pair(list_iterator, true); } // Try emplace an element before the end of the list map. If the key already exists, does nothing. Moved arguments // won't be moved when key already exists return when key does not exist, when key // exist and iterator is the position where it was placed template std::pair try_emplace_back(const Key& key, Args&&... args) { return try_emplace(end(), key, std::forward(args)...); } // Put a key-value pair to the map before position. If key already exist, |pos| will be ignored and existing value // will be replaced void insert_or_assign(const_iterator pos, const Key& key, T value) { auto map_iterator = key_map_.find(key); if (map_iterator != key_map_.end()) { map_iterator->second->second = std::move(value); return; } auto list_iterator = node_list_.emplace(pos, key, std::move(value)); key_map_.emplace(key, list_iterator); } // Put a key-value pair to the tail of the map or replace the current value without moving the key if key exists void insert_or_assign(const Key& key, T value) { insert_or_assign(end(), key, std::move(value)); } // STL splice, same as std::list::splice // - pos: element before which the content will be inserted // - other: another container to transfer the content from // - it: the element to transfer from other to *this void splice(const_iterator pos, ListMap& other, const_iterator it) { if (&other != this) { auto map_node = other.key_map_.extract(it->first); key_map_.insert(std::move(map_node)); } node_list_.splice(pos, other.node_list_, it); } // Remove a key from the list map and return removed value if key exits, std::nullopt if not. The return value will be // evaluated to true in a boolean context if a value is contained by std::optional, false otherwise. std::optional extract(const Key& key) { auto map_iterator = key_map_.find(key); if (map_iterator == key_map_.end()) { return std::nullopt; } std::optional removed_node(std::move(*map_iterator->second)); node_list_.erase(map_iterator->second); key_map_.erase(map_iterator); return removed_node; } // Remove an iterator pointed item from the list map and return the iterator immediately after the erased item iterator erase(const_iterator iter) { key_map_.erase(iter->first); return node_list_.erase(iter); } // Return size of the list map inline size_t size() const { return node_list_.size(); } // Return iterator interface for begin inline iterator begin() { return node_list_.begin(); } // Iterator interface for begin, const inline const_iterator begin() const { return node_list_.begin(); } // Iterator interface for end inline iterator end() { return node_list_.end(); } // Iterator interface for end, const inline const_iterator end() const { return node_list_.end(); } private: std::list node_list_; std::unordered_map key_map_; }; } // namespace common } // namespace bluetooth