osb/source/core/StarIdMap.hpp
2023-06-27 20:23:44 +10:00

152 lines
4.1 KiB
C++

#ifndef STAR_ID_MAP_HPP
#define STAR_ID_MAP_HPP
#include "StarMap.hpp"
#include "StarMathCommon.hpp"
#include "StarDataStream.hpp"
namespace Star {
STAR_EXCEPTION(IdMapException, StarException);
// Maps key ids to values with auto generated ids in a given id range. Tries
// to cycle through ids as new values are added and avoid re-using ids until
// the id space wraps around.
template <typename BaseMap>
class IdMapWrapper : private BaseMap {
public:
typedef typename BaseMap::iterator iterator;
typedef typename BaseMap::const_iterator const_iterator;
typedef typename BaseMap::key_type key_type;
typedef typename BaseMap::value_type value_type;
typedef typename BaseMap::mapped_type mapped_type;
typedef key_type IdType;
typedef value_type ValueType;
typedef mapped_type MappedType;
IdMapWrapper();
IdMapWrapper(IdType min, IdType max);
// New valid id that does not exist in this map. Tries not to immediately
// recycle ids, to avoid temporally close id repeats.
IdType nextId();
// Throws exception if key already exists
void add(IdType id, MappedType mappedType);
// Add with automatically allocated id
IdType add(MappedType mappedType);
void clear();
bool operator==(IdMapWrapper const& rhs) const;
bool operator!=(IdMapWrapper const& rhs) const;
using BaseMap::keys;
using BaseMap::values;
using BaseMap::pairs;
using BaseMap::contains;
using BaseMap::size;
using BaseMap::empty;
using BaseMap::get;
using BaseMap::ptr;
using BaseMap::maybe;
using BaseMap::take;
using BaseMap::maybeTake;
using BaseMap::remove;
using BaseMap::value;
using BaseMap::begin;
using BaseMap::end;
using BaseMap::erase;
template <typename Base>
friend DataStream& operator>>(DataStream& ds, IdMapWrapper<Base>& map);
template <typename Base>
friend DataStream& operator<<(DataStream& ds, IdMapWrapper<Base> const& map);
private:
IdType m_min;
IdType m_max;
IdType m_nextId;
};
template <class Key, class Value>
using IdMap = IdMapWrapper<Map<Key, Value>>;
template <class Key, class Value>
using IdHashMap = IdMapWrapper<HashMap<Key, Value>>;
template <typename BaseMap>
IdMapWrapper<BaseMap>::IdMapWrapper()
: m_min(lowest<IdType>()), m_max(highest<IdType>()), m_nextId(m_min) {}
template <typename BaseMap>
IdMapWrapper<BaseMap>::IdMapWrapper(IdType min, IdType max)
: m_min(min), m_max(max), m_nextId(m_min) {
starAssert(m_max > m_min);
}
template <typename BaseMap>
auto IdMapWrapper<BaseMap>::nextId() -> IdType {
if ((IdType)BaseMap::size() > m_max - m_min)
throw IdMapException("No id space left in IdMapWrapper");
IdType nextId = m_nextId;
while (BaseMap::contains(nextId))
nextId = cycleIncrement(nextId, m_min, m_max);
m_nextId = cycleIncrement(nextId, m_min, m_max);
return nextId;
}
template <typename BaseMap>
void IdMapWrapper<BaseMap>::add(IdType id, MappedType mappedType) {
if (!BaseMap::insert(make_pair(move(id), move(mappedType))).second)
throw IdMapException::format("IdMapWrapper::add(id, value) called with pre-existing id '{}'", outputAny(id));
}
template <typename BaseMap>
auto IdMapWrapper<BaseMap>::add(MappedType mappedType) -> IdType {
auto id = nextId();
BaseMap::insert(id, mappedType);
return id;
}
template <typename BaseMap>
void IdMapWrapper<BaseMap>::clear() {
BaseMap::clear();
m_nextId = m_min;
}
template <typename BaseMap>
bool IdMapWrapper<BaseMap>::operator==(IdMapWrapper const& rhs) const {
return tie(m_min, m_max) == tie(rhs.m_min, rhs.m_max) && BaseMap::operator==(rhs);
}
template <typename BaseMap>
bool IdMapWrapper<BaseMap>::operator!=(IdMapWrapper const& rhs) const {
return !operator==(rhs);
}
template <typename BaseMap>
DataStream& operator>>(DataStream& ds, IdMapWrapper<BaseMap>& map) {
ds.readMapContainer((BaseMap&)map);
ds.read(map.m_min);
ds.read(map.m_max);
ds.read(map.m_nextId);
return ds;
}
template <typename BaseMap>
DataStream& operator<<(DataStream& ds, IdMapWrapper<BaseMap> const& map) {
ds.writeMapContainer((BaseMap const&)map);
ds.write(map.m_min);
ds.write(map.m_max);
ds.write(map.m_nextId);
return ds;
}
}
#endif