145 lines
5.2 KiB
C
145 lines
5.2 KiB
C
|
#ifndef VTR_VECTOR_MAP
|
||
|
#define VTR_VECTOR_MAP
|
||
|
#include <vector>
|
||
|
|
||
|
#include "vtr_assert.h"
|
||
|
#include "vtr_sentinels.h"
|
||
|
|
||
|
namespace vtr {
|
||
|
|
||
|
//A vector-like container which is indexed by K (instead of size_t as in std::vector).
|
||
|
//
|
||
|
//The main use of this container is to behave like a std::vector which is indexed by
|
||
|
//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.
|
||
|
//
|
||
|
//This results in a container that is somewhat similar to a std::map (i.e. converts from one
|
||
|
//type to another), but requires contiguously ascending (i.e. linear) keys. Unlike std::map
|
||
|
//only the values are stored (at the specified index/key), reducing memory usage and improving
|
||
|
//cache locality. Furthermore, operator[] and find() return the value or iterator directly
|
||
|
//associated with the value (like std::vector) rather than a std::pair (like std::map).
|
||
|
//insert() takes both the key and value as separate arguments and has no return value.
|
||
|
//
|
||
|
//Additionally, vector_map will silently create values for 'gaps' in the index range (i.e.
|
||
|
//those elements are initialized with Sentinel::INVALID()).
|
||
|
//
|
||
|
//If you need a fully featured std::map like container without the above differences see
|
||
|
//vtr::linear_map.
|
||
|
//
|
||
|
//If you do not need std::map-like features see vtr::vector.
|
||
|
//
|
||
|
//Note that it is possible to use vector_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(), insert() and
|
||
|
//update() methods which handle non-existing keys gracefully.
|
||
|
template<typename K, typename V, typename Sentinel=DefaultSentinel<V>>
|
||
|
class vector_map {
|
||
|
public: //Public types
|
||
|
typedef typename std::vector<V>::const_reference const_reference;
|
||
|
typedef typename std::vector<V>::reference reference;
|
||
|
|
||
|
typedef typename std::vector<V>::iterator iterator;
|
||
|
typedef typename std::vector<V>::const_iterator const_iterator;
|
||
|
typedef typename std::vector<V>::const_reverse_iterator const_reverse_iterator;
|
||
|
|
||
|
public: //Constructor
|
||
|
template<typename... Args>
|
||
|
vector_map(Args&&... args)
|
||
|
: vec_(std::forward<Args>(args)...)
|
||
|
{ }
|
||
|
|
||
|
public: //Accessors
|
||
|
|
||
|
//Iterators
|
||
|
const_iterator begin() const { return vec_.begin(); }
|
||
|
const_iterator end() const { return vec_.end(); }
|
||
|
const_reverse_iterator rbegin() const { return vec_.rbegin(); }
|
||
|
const_reverse_iterator rend() const { return vec_.rend(); }
|
||
|
|
||
|
//Indexing
|
||
|
const_reference operator[] (const K n) const {
|
||
|
size_t index = size_t(n);
|
||
|
VTR_ASSERT_SAFE_MSG(index >= 0 && index < vec_.size(), "Out-of-range index");
|
||
|
return vec_[index];
|
||
|
}
|
||
|
|
||
|
const_iterator find(const K key) const {
|
||
|
if(size_t(key) < vec_.size()) {
|
||
|
return vec_.begin() + size_t(key);
|
||
|
} else {
|
||
|
return vec_.end();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::size_t size() const { return vec_.size(); }
|
||
|
|
||
|
bool empty() const { return vec_.empty(); }
|
||
|
|
||
|
bool contains(const K key) const { return size_t(key) < vec_.size(); }
|
||
|
size_t count(const K key) const { return contains(key) ? 1 : 0; }
|
||
|
|
||
|
public: //Mutators
|
||
|
|
||
|
//Delegate potentially overloaded functions to the underlying vector with perfect
|
||
|
//forwarding
|
||
|
template<typename... Args>
|
||
|
void push_back(Args&&... args) { vec_.push_back(std::forward<Args>(args)...); }
|
||
|
|
||
|
template<typename... Args>
|
||
|
void emplace_back(Args&&... args) { vec_.emplace_back(std::forward<Args>(args)...); }
|
||
|
|
||
|
template<typename... Args>
|
||
|
void resize(Args&&... args) { vec_.resize(std::forward<Args>(args)...); }
|
||
|
|
||
|
void clear() { vec_.clear(); }
|
||
|
|
||
|
size_t capacity() const { return vec_.capacity(); }
|
||
|
void shrink_to_fit() { vec_.shrink_to_fit(); }
|
||
|
|
||
|
//Iterators
|
||
|
iterator begin() { return vec_.begin(); }
|
||
|
iterator end() { return vec_.end(); }
|
||
|
|
||
|
//Indexing
|
||
|
reference operator[] (const K n) {
|
||
|
VTR_ASSERT_SAFE_MSG(size_t(n) < vec_.size(), "Out-of-range index");
|
||
|
return vec_[size_t(n)];
|
||
|
}
|
||
|
|
||
|
iterator find(const K key) {
|
||
|
if(size_t(key) < vec_.size()) {
|
||
|
return vec_.begin() + size_t(key);
|
||
|
} else {
|
||
|
return vec_.end();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void insert(const K key, const V value) {
|
||
|
if(size_t(key) >= vec_.size()) {
|
||
|
//Resize so key is in range
|
||
|
vec_.resize(size_t(key) + 1, Sentinel::INVALID());
|
||
|
}
|
||
|
|
||
|
//Insert the value
|
||
|
operator[](key) = value;
|
||
|
}
|
||
|
|
||
|
void update(const K key, const V value) { insert(key, value); }
|
||
|
|
||
|
//Swap (this enables std::swap via ADL)
|
||
|
friend void swap(vector_map<K,V>& x, vector_map<K,V>& y) {
|
||
|
std::swap(x.vec_, y.vec_);
|
||
|
}
|
||
|
private:
|
||
|
std::vector<V> vec_;
|
||
|
};
|
||
|
|
||
|
|
||
|
} //namespace
|
||
|
#endif
|