• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <functional>
20 #include <iterator>
21 #include <list>
22 #include <mutex>
23 #include <optional>
24 #include <thread>
25 #include <type_traits>
26 #include <unordered_map>
27 
28 namespace bluetooth {
29 namespace common {
30 
31 // A map that maintains order of its element as a list. An element that is put earlier will appear before an element
32 // that is put later when iterating through this map's entries. Keys must be unique.
33 //
34 // Performance:
35 //   - Key look-up and modification is O(1)
36 //   - Value operated by replacement, no in-place modification
37 //   - Memory consumption is:
38 //     O(2*capacity*sizeof(K) + capacity*(sizeof(nullptr)+sizeof(V)))
39 //   - NOT THREAD SAFE
40 //
41 // Template:
42 //   - Key key
43 //   - T value
44 template <typename Key, typename T>
45 class ListMap {
46  public:
47   using value_type = std::pair<const Key, T>;
48   // different from c++17 node_type on purpose as we want node to be copyable
49   using node_type = std::pair<Key, T>;
50   using iterator = typename std::list<value_type>::iterator;
51   using const_iterator = typename std::list<value_type>::const_iterator;
52 
53   // Constructor of the list map
54   ListMap() = default;
55 
56   // for move
57   ListMap(ListMap&& other) noexcept = default;
58   ListMap& operator=(ListMap&& other) noexcept = default;
59 
60   // copy-constructor
61   // iterators in key_map_ cannot be copied directly
ListMap(const ListMap & other)62   ListMap(const ListMap& other) : node_list_(other.node_list_) {
63     for (auto iter = node_list_.begin(); iter != node_list_.end(); iter++) {
64       key_map_.emplace(iter->first, iter);
65     }
66   }
67 
68   // copy-assignment
69   // iterators in key_map_ cannot be copied directly
70   ListMap& operator=(const ListMap& other) {
71     if (&other == this) {
72       return *this;
73     }
74     node_list_ = other.node_list_;
75     key_map_.clear();
76     for (auto iter = node_list_.begin(); iter != node_list_.end(); iter++) {
77       key_map_.emplace(iter->first, iter);
78     }
79     return *this;
80   }
81 
82   // comparison operators
83   bool operator==(const ListMap& rhs) const {
84     return node_list_ == rhs.node_list_;
85   }
86   bool operator!=(const ListMap& rhs) const {
87     return !(*this == rhs);
88   }
89 
~ListMap()90   ~ListMap() {
91     clear();
92   }
93 
94   // Clear the list map
clear()95   void clear() {
96     key_map_.clear();
97     node_list_.clear();
98   }
99 
100   // const version of find()
find(const Key & key)101   const_iterator find(const Key& key) const {
102     return const_cast<ListMap*>(this)->find(key);
103   }
104 
105   // Get the value of a key. Return iterator to the item if found, end() if not found
find(const Key & key)106   iterator find(const Key& key) {
107     auto map_iterator = key_map_.find(key);
108     if (map_iterator == key_map_.end()) {
109       return end();
110     }
111     return map_iterator->second;
112   }
113 
114   // Check if key exist in the map. Return true if key exist in map, false if not.
contains(const Key & key)115   bool contains(const Key& key) const {
116     return find(key) != end();
117   }
118 
119   // Try emplace an element before a specific position |pos| of the list map. If the |key| already exists, does nothing.
120   // Moved arguments won't be moved when key already exists. Return <iterator, true> when key does not exist, <iterator,
121   // false> when key exist and iterator is the position where it was placed.
122   template <class... Args>
try_emplace(const_iterator pos,const Key & key,Args &&...args)123   std::pair<iterator, bool> try_emplace(const_iterator pos, const Key& key, Args&&... args) {
124     auto map_iterator = key_map_.find(key);
125     if (map_iterator != key_map_.end()) {
126       return std::make_pair(end(), false);
127     }
128     auto list_iterator = node_list_.emplace(pos, key, std::forward<Args>(args)...);
129     key_map_.emplace(key, list_iterator);
130     return std::make_pair(list_iterator, true);
131   }
132 
133   // Try emplace an element before the end of the list map. If the key already exists, does nothing. Moved arguments
134   // won't be moved when key already exists return <iterator, true> when key does not exist, <iterator, false> when key
135   // exist and iterator is the position where it was placed
136   template <class... Args>
try_emplace_back(const Key & key,Args &&...args)137   std::pair<iterator, bool> try_emplace_back(const Key& key, Args&&... args) {
138     return try_emplace(end(), key, std::forward<Args>(args)...);
139   }
140 
141   // Put a key-value pair to the map before position. If key already exist, |pos| will be ignored and existing value
142   // will be replaced
insert_or_assign(const_iterator pos,const Key & key,T value)143   void insert_or_assign(const_iterator pos, const Key& key, T value) {
144     auto map_iterator = key_map_.find(key);
145     if (map_iterator != key_map_.end()) {
146       map_iterator->second->second = std::move(value);
147       return;
148     }
149     auto list_iterator = node_list_.emplace(pos, key, std::move(value));
150     key_map_.emplace(key, list_iterator);
151   }
152 
153   // Put a key-value pair to the tail of the map or replace the current value without moving the key if key exists
insert_or_assign(const Key & key,T value)154   void insert_or_assign(const Key& key, T value) {
155     insert_or_assign(end(), key, std::move(value));
156   }
157 
158   // STL splice, same as std::list::splice
159   // - pos: element before which the content will be inserted
160   // - other: another container to transfer the content from
161   // - it: the element to transfer from other to *this
splice(const_iterator pos,ListMap<Key,T> & other,const_iterator it)162   void splice(const_iterator pos, ListMap<Key, T>& other, const_iterator it) {
163     if (&other != this) {
164       auto map_node = other.key_map_.extract(it->first);
165       key_map_.insert(std::move(map_node));
166     }
167     node_list_.splice(pos, other.node_list_, it);
168   }
169 
170   // Remove a key from the list map and return removed value if key exits, std::nullopt if not. The return value will be
171   // evaluated to true in a boolean context if a value is contained by std::optional, false otherwise.
extract(const Key & key)172   std::optional<node_type> extract(const Key& key) {
173     auto map_iterator = key_map_.find(key);
174     if (map_iterator == key_map_.end()) {
175       return std::nullopt;
176     }
177     std::optional<node_type> removed_node(std::move(*map_iterator->second));
178     node_list_.erase(map_iterator->second);
179     key_map_.erase(map_iterator);
180     return removed_node;
181   }
182 
183   // Remove an iterator pointed item from the list map and return the iterator immediately after the erased item
erase(const_iterator iter)184   iterator erase(const_iterator iter) {
185     key_map_.erase(iter->first);
186     return node_list_.erase(iter);
187   }
188 
189   // Return size of the list map
size()190   inline size_t size() const {
191     return node_list_.size();
192   }
193 
194   // Return iterator interface for begin
begin()195   inline iterator begin() {
196     return node_list_.begin();
197   }
198 
199   // Iterator interface for begin, const
begin()200   inline const_iterator begin() const {
201     return node_list_.begin();
202   }
203 
204   // Iterator interface for end
end()205   inline iterator end() {
206     return node_list_.end();
207   }
208 
209   // Iterator interface for end, const
end()210   inline const_iterator end() const {
211     return node_list_.end();
212   }
213 
214  private:
215   std::list<value_type> node_list_;
216   std::unordered_map<Key, iterator> key_map_;
217 };
218 
219 }  // namespace common
220 }  // namespace bluetooth