#ifndef VTR_LINEAR_MAP_H #define VTR_LINEAR_MAP_H #include #include #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 linear_map { public: typedef K key_type; typedef T mapped_type; typedef std::pair value_type; typedef value_type& reference; typedef const value_type& const_reference; typedef typename std::vector::iterator iterator; typedef typename std::vector::const_iterator const_iterator; typedef typename std::vector::reverse_iterator reverse_iterator; typedef typename std::vector::const_reverse_iterator const_reverse_iterator; typedef typename std::vector::difference_type difference_type; typedef typename std::vector::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(const_cast(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 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 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 std::pair 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)...); 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(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(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(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 equal_range(const key_type& key) { auto const_iter_pair = const_cast(this)->equal_range(key); return std::pair(iterator(const_iter_pair.first), iterator(const_iter_pair.second)); } std::pair 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(i, const_iter)); return i; } constexpr K sentinel() const { return Sentinel::INVALID(); } private: std::vector vec_; }; } // namespace vtr #endif