| #ifndef VTR_LINEAR_MAP_H |
| #define VTR_LINEAR_MAP_H |
| #include <vector> |
| #include <stdexcept> |
| |
| #include "vtr_sentinels.h" |
| |
| namespace vtr { |
| |
| //A std::map-like container which is indexed by K |
| // |
| //The main use of this container is to behave like a std::map which is optimized to hold |
| //mappings between a dense linear range of keys (e.g. vtr::StrongId). |
| // |
| //Requires that K be convertable to size_t with the size_t operator (i.e. size_t()), and |
| //that the conversion results in a linearly increasing index into the underlying vector. |
| //Also requires that K() return the sentinel value used to mark invalid entries. |
| // |
| //If you only need to access the value associated with the key consider using vtr::vector_map |
| //instead, which provides a similar but more std::vector-like interface. |
| // |
| //Note that it is possible to use linear_map with sparse/non-contiguous keys, but this is typically |
| //memory inefficient as the underlying vector will allocate space for [0..size_t(max_key)-1], |
| //where max_key is the largest key that has been inserted. |
| // |
| //As with a std::vector, it is the caller's responsibility to ensure there is sufficient space |
| //when a given index/key before it is accessed. The exception to this are the find() and insert() |
| //methods which handle non-existing keys gracefully. |
| template<class K, class T, class Sentinel = DefaultSentinel<K>> |
| class linear_map { |
| public: |
| typedef K key_type; |
| typedef T mapped_type; |
| typedef std::pair<K, T> value_type; |
| typedef value_type& reference; |
| typedef const value_type& const_reference; |
| typedef typename std::vector<value_type>::iterator iterator; |
| typedef typename std::vector<value_type>::const_iterator const_iterator; |
| typedef typename std::vector<value_type>::reverse_iterator reverse_iterator; |
| typedef typename std::vector<value_type>::const_reverse_iterator const_reverse_iterator; |
| typedef typename std::vector<value_type>::difference_type difference_type; |
| typedef typename std::vector<value_type>::size_type size_type; |
| |
| public: |
| //Standard big 5 |
| linear_map() = default; |
| linear_map(const linear_map&) = default; |
| linear_map(linear_map&&) = default; |
| linear_map& operator=(const linear_map&) = default; |
| linear_map& operator=(linear_map&&) = default; |
| |
| linear_map(size_t num_keys) |
| : vec_(num_keys, std::make_pair(sentinel(), T())) //Initialize all with sentinel values |
| {} |
| |
| iterator begin() { return vec_.begin(); } |
| const_iterator begin() const { return vec_.begin(); } |
| iterator end() { return vec_.end(); } |
| const_iterator end() const { return vec_.end(); } |
| reverse_iterator rbegin() { return vec_.rbegin(); } |
| const_reverse_iterator rbegin() const { return vec_.rbegin(); } |
| reverse_iterator rend() { return vec_.rend(); } |
| const_reverse_iterator rend() const { return vec_.rend(); } |
| const_iterator cbegin() const { return vec_.begin(); } |
| const_iterator cend() const { return vec_.end(); } |
| const_reverse_iterator crbegin() const { return vec_.rbegin(); } |
| const_reverse_iterator crend() const { return vec_.rend(); } |
| |
| bool empty() const { return vec_.empty(); } |
| size_type size() const { return vec_.size(); } |
| size_type max_size() const { return vec_.max_size(); } |
| |
| mapped_type& operator[](const key_type& key) { |
| auto iter = find(key); |
| if (iter == end()) { |
| //Not found, create it |
| iter = insert(std::make_pair(key, mapped_type())).first; |
| } |
| |
| return iter->second; |
| } |
| |
| mapped_type& at(const key_type& key) { |
| return const_cast<mapped_type&>(const_cast<const linear_map*>(this)->at(key)); |
| } |
| |
| const mapped_type& at(const key_type& key) const { |
| auto iter = find(key); |
| if (iter == end()) { |
| throw std::out_of_range("Invalid key"); |
| } |
| return iter->second; |
| } |
| |
| //Insert value |
| std::pair<iterator, bool> insert(const value_type& value) { |
| auto iter = find(value.first); |
| if (iter != end()) { |
| //Found existing |
| return std::make_pair(iter, false); |
| } else { |
| //Insert |
| size_t index = size_t(value.first); |
| |
| if (index >= vec_.size()) { |
| //Make space, initialize empty slots with sentinel values |
| vec_.resize(index + 1, std::make_pair(sentinel(), T())); |
| } |
| |
| vec_[index] = value; |
| |
| return std::make_pair(vec_.begin() + index, true); |
| } |
| } |
| |
| //Insert range |
| template<class InputIterator> |
| void insert(InputIterator first, InputIterator last) { |
| for (InputIterator iter = first; iter != last; ++iter) { |
| insert(*iter); |
| } |
| } |
| |
| //Erase by key |
| void erase(const key_type& key) { |
| auto iter = find(key); |
| if (iter != end()) { |
| erase(iter); |
| } |
| } |
| |
| //Erase at iterator |
| void erase(const_iterator position) { |
| iterator pos = convert_to_iterator(position); |
| pos->first = sentinel(); //Mark invalid |
| } |
| |
| //Erase range |
| void erase(const_iterator first, const_iterator last) { |
| for (auto iter = first; iter != last; ++iter) { |
| erase(iter); |
| } |
| } |
| |
| void swap(linear_map& other) { std::swap(vec_, other.vec_); } |
| |
| void clear() { vec_.clear(); } |
| |
| template<class... Args> |
| std::pair<iterator, bool> emplace(const key_type& key, Args&&... args) { |
| auto iter = find(key); |
| if (iter != end()) { |
| //Found |
| return std::make_pair(iter, false); |
| } else { |
| //Emplace |
| size_t index = size_t(key); |
| |
| if (index >= vec_.size()) { |
| //Make space, initialize empty slots with sentinel values |
| vec_.resize(index + 1, value_type(sentinel(), T())); |
| } |
| |
| vec_[index] = value_type(key, std::forward<Args>(args)...); |
| |
| return std::make_pair(vec_.begin() + index, true); |
| } |
| } |
| |
| void reserve(size_type n) { vec_.reserve(n); } |
| void shrink_to_fit() { vec_.shrink_to_fit(); } |
| |
| iterator find(const key_type& key) { |
| const_iterator const_iter = const_cast<const linear_map*>(this)->find(key); |
| return convert_to_iterator(const_iter); |
| } |
| |
| const_iterator find(const key_type& key) const { |
| size_t index = size_t(key); |
| |
| if (index < vec_.size() && vec_[index].first != sentinel()) { |
| return vec_.begin() + index; |
| } |
| return end(); |
| } |
| |
| size_type count(const key_type& key) const { |
| return (find(key) == end()) ? 0 : 1; |
| } |
| |
| iterator lower_bound(const key_type& key) { |
| const_iterator const_iter = const_cast<const linear_map*>(this)->lower_bound(key); |
| return convert_to_iterator(const_iter); |
| } |
| |
| const_iterator lower_bound(const key_type& key) const { |
| return find(key); |
| } |
| |
| iterator upper_bound(const key_type& key) { |
| const_iterator const_iter = const_cast<const linear_map*>(this)->upper_bound(key); |
| return convert_to_iterator(const_iter); |
| } |
| |
| const_iterator upper_bound(const key_type& key) const { |
| auto iter = find(key); |
| return (iter != end()) ? iter + 1 : end(); |
| } |
| |
| std::pair<iterator, iterator> equal_range(const key_type& key) { |
| auto const_iter_pair = const_cast<const linear_map*>(this)->equal_range(key); |
| return std::pair<iterator, iterator>(iterator(const_iter_pair.first), iterator(const_iter_pair.second)); |
| } |
| |
| std::pair<const_iterator, const_iterator> equal_range(const key_type& key) const { |
| auto lb_iter = lower_bound(key); |
| auto ub_iter = upper_bound(key); |
| return (lb_iter != end()) ? std::make_pair(lb_iter, ub_iter) : std::make_pair(ub_iter, ub_iter); |
| } |
| |
| size_type valid_size() const { |
| size_t valid_cnt = 0; |
| for (const auto& kv : vec_) { |
| if (kv.first != sentinel()) { |
| ++valid_cnt; |
| } |
| } |
| return valid_cnt; |
| } |
| |
| public: |
| friend void swap(linear_map& lhs, linear_map& rhs) { |
| std::swap(lhs.vec_, rhs.vec_); |
| } |
| |
| private: |
| iterator convert_to_iterator(const_iterator const_iter) { |
| //This is a work around for the fact that there is no conversion between |
| //a const_iterator and iterator. |
| // |
| //We intiailize i to the start of the container and then advance it by |
| //the distance to const_iter. The resulting i points to the same element |
| //as const_iter |
| // |
| //Note that to be able to call std::distance with an iterator and |
| //const_iterator we need to specify the type as const_iterator (relying |
| //on the implicit conversion from iterator to const_iterator for i) |
| // |
| //Since the iterators are really vector (i.e. random-access) iterators |
| //both distance and advance take constant time |
| iterator i = begin(); |
| std::advance(i, std::distance<const_iterator>(i, const_iter)); |
| return i; |
| } |
| |
| constexpr K sentinel() const { return Sentinel::INVALID(); } |
| |
| private: |
| std::vector<value_type> vec_; |
| }; |
| |
| } // namespace vtr |
| #endif |