Skip to content
Snippets Groups Projects
Commit d0cf6eb9 authored by Adam Edward Barton's avatar Adam Edward Barton Committed by Adam Edward Barton
Browse files

Adjust IdentifiableValueCache for greater compatability, add documentation

parent b10899ec
No related branches found
No related tags found
No related merge requests found
......@@ -8,49 +8,78 @@
#include <atomic>
#include <vector>
/**
* This class is to provide an event wide MT container for concurrent storing of basic types, like ints
* This is a version of the identifiable container optimized for basic types
* The cache is designed for event level storage and concurrent writing.
*/
template<class T, T EMPTYVALUE>
template<class T>
class IdentifiableValueCache{
public:
static constexpr T emptyValue() {
return EMPTYVALUE;
///Return the empty value that is interpreted as an empty entry
const T& emptyValue() const {
return m_emptyValue;
}
//Prevent accidental copying
IdentifiableValueCache(const IdentifiableValueCache&) = delete;
IdentifiableValueCache(size_t maxSize)
: m_vec(maxSize)
///Pass the maximum hash to size the cache and the defaultValue which will be interpreted as an empty value
IdentifiableValueCache(size_t maxSize, T emptyValuein)
: m_vec(maxSize), m_emptyValue(std::move(emptyValuein))
{
for(auto &x : m_vec) x.store(emptyValue(), std::memory_order_relaxed);
}
void forceReset(){
for(auto &x : m_vec) x.store(emptyValue(), std::memory_order_relaxed);
}
///Forceable empty the container, DO NOT USE THIS IN MT ENVIRONMENT
void forceReset();
///Return the maxSize of the collection
size_t maxSize() const { return m_vec.size(); }
~IdentifiableValueCache() = default;
T retrieve(size_t i){
return m_vec.at(i).load();
}
///Retrieve the Value stored in that hash
T retrieve(size_t i){ return m_vec.at(i).load(); }
bool present(size_t i){
return m_vec.at(i).load() != emptyValue();
}
///Returns true if the value is set to anything but the emptyValue
bool present(size_t i){ return m_vec.at(i).load() != emptyValue(); }
bool setOrDrop(size_t i, const T &value){
T val = emptyValue();
return m_vec.at(i).compare_exchange_strong(val, value);
}
///Set the given hash to the value
bool setOrDrop(size_t i, const T &value);
///Make a vector of hashes and values, convenient for iteration and other uses.
std::vector<std::pair<size_t, T>> getAll() const;
const std::vector<std::atomic<T>>& rawReadAccess() const { return m_vec; }
private:
std::vector<std::atomic<T>> m_vec;
const T m_emptyValue;
};
template<class T>
std::vector<std::pair<size_t, T>>
IdentifiableValueCache<T>::getAll() const{
std::vector<std::pair<size_t, T>> list;
for(size_t i =0; i<m_vec.size(); i++){
T item = m_vec[i].load();
if(item!=m_emptyValue) list.emplace_back(i, std::move(item));
}
return list;
}
template<class T>
void IdentifiableValueCache<T>::forceReset(){
for(auto &x : m_vec) x.store(emptyValue(), std::memory_order_relaxed);
}
template<class T>
bool IdentifiableValueCache<T>::setOrDrop(size_t i, const T &value){
T val = emptyValue();
return m_vec.at(i).compare_exchange_strong(val, value);
}
#endif
......@@ -7,73 +7,113 @@
#include "EventContainers/IdentifiableValueCache.h"
template<class T, T EMPTYVALUE>
/*
* This class is the view specific container that can link to the IdentifiableValueCache
* It allows you to link to an external cache and keep a mask to track the items in your specific view
*/
template<class T>
class IdentifiableValueContainer{
public:
static constexpr T emptyValue() {
return EMPTYVALUE;
}
IdentifiableValueContainer(const IdentifiableValueContainer<T,EMPTYVALUE>&) = delete;
//Prevent accidental copying
IdentifiableValueContainer(const IdentifiableValueContainer<T>&) = delete;
~IdentifiableValueContainer() { if(m_own) delete m_cache; }
IdentifiableValueContainer(size_t maxSize) : m_mask(maxSize, false), m_own(true)
///Self Owning Constructor
///Pass the maximum hash to size the cache and the defaultValue which will be interpreted as an empty value
IdentifiableValueContainer(size_t maxSize, T defaultValue) : m_mask(maxSize, false), m_own(true)
{
m_cache = new IdentifiableValueCache<T, EMPTYVALUE>(maxSize);
m_cache = new IdentifiableValueCache<T>(maxSize, std::move(defaultValue));
}
IdentifiableValueContainer(IdentifiableValueCache<T,EMPTYVALUE> *ptr) : m_mask(ptr->maxSize()),
///External Cache Constructor
///Pass the external cache to set up a view specific view interface
IdentifiableValueContainer(IdentifiableValueCache<T> *ptr) : m_mask(ptr->maxSize()),
m_cache(ptr), m_own(false)
{}
bool present(size_t i) const
{
return m_mask.at(i);
}
bool setOrDrop(size_t i, const T &value){
bool b = m_cache->setOrDrop(i, value);
m_mask[i] = true;
return b;
}
///Return the empty value that is interpreted as an empty entry
const T& emptyValue() const { return m_cache->emptyValue(); }
///Is the value for this has set and also accepted in the mask
bool present(size_t i) const;
///Set the value for the given hash
bool setOrDrop(size_t i, const T &value);
///Return the maxSize of the collection
size_t maxSize() const { return m_mask.size(); }
size_t numberSet() const{
size_t count = 0;
for(bool b : m_mask) count += b;
return count;
}
///Return the number of entries set and accessible according to the mask.
///This is not a trivial function do not repeatedly call.
size_t numberSet() const;
bool tryAddFromCache(size_t i){
if(i >= m_mask.size()) return false;
bool b = m_cache->present(i);
m_mask[i] = b;
return b;
}
///Returns true if the value is also in the external cache, sets mask to true if it is
bool tryAddFromCache(size_t i);
T retrieve(size_t i) const{
if(m_mask[i]) return m_cache->retrieve(i);
else return emptyValue();
}
///Retrieve the value of the hash, if accessible according to the mask
T retrieve(size_t i) const;
std::vector<std::pair<size_t, T>> getAll() const{
std::vector<std::pair<size_t, T>> list;
const auto& raw = m_cache->rawReadAccess();
for(size_t i =0; i<m_mask.size(); i++){
if(m_mask[i]) list.emplace_back(i, raw[i].load());
}
return list;
}
///Make a vector of hashes and values, convenient for iteration and other uses.
std::vector<std::pair<size_t, T>> getAll() const;
///Get read only access to the whole external cache. This could be useful for special situations
const std::vector<std::atomic<T>>& wholeEventReadAccess() const { return m_cache->rawReadAccess(); }
private:
std::vector<bool> m_mask;
IdentifiableValueCache<T, EMPTYVALUE> *m_cache;
IdentifiableValueCache<T> *m_cache;
bool m_own;
};
template< class T >
bool IdentifiableValueContainer<T>::present(size_t i) const
{
return m_mask.at(i);
}
template< class T >
std::vector<std::pair<size_t, T>> IdentifiableValueContainer<T>::getAll() const{
std::vector<std::pair<size_t, T>> list;
const auto& raw = m_cache->rawReadAccess();
for(size_t i =0; i<m_mask.size(); i++){
if(m_mask[i]) list.emplace_back(i, raw[i].load());
}
return list;
}
template< class T >
T IdentifiableValueContainer<T>::retrieve(size_t i) const{
if(m_mask[i]) return m_cache->retrieve(i);
else return m_cache->emptyValue();
}
template< class T >
bool IdentifiableValueContainer<T>::tryAddFromCache(size_t i){
if(i >= m_mask.size()) return false;
bool b = m_cache->present(i);
m_mask[i] = b;
return b;
}
template< class T >
size_t IdentifiableValueContainer<T>::numberSet() const{
size_t count = 0;
for(bool b : m_mask) count += b;
return count;
}
template< class T >
bool IdentifiableValueContainer<T>::setOrDrop(size_t i, const T &value){
bool b = m_cache->setOrDrop(i, value);
m_mask[i] = true;
return b;
}
#endif
......@@ -8,11 +8,11 @@
#include <limits>
#include <cassert>
typedef IdentifiableValueCache<int, std::numeric_limits<int>::min()> int100cache;
typedef IdentifiableValueContainer<int, std::numeric_limits<int>::min()> int100container;
typedef IdentifiableValueCache<int> int100cache;
typedef IdentifiableValueContainer<int> int100container;
int main(){
auto *cache = new int100cache(100);
auto *cache = new int100cache(100, std::numeric_limits<int>::min());
auto *container = new int100container(cache);
auto *container2 = new int100container(cache);
assert(cache->emptyValue() == std::numeric_limits<int>::min());
......@@ -27,6 +27,7 @@ int main(){
if(container2->tryAddFromCache(50) == false) std::abort();
if(container2->getAll().size()!= 1) std::abort();
if(container->getAll().size()!= 3) std::abort();
if(cache->getAll().size()!= 3) std::abort();
if(container2->retrieve(50) != 29) std::abort();
if(container2->present(51) == true) std::abort();
if(container2->retrieve(51) != container2->emptyValue()) std::abort();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment