Skip to content

Commit

Permalink
simplify CmptTypeSet, EntityMngr's h2a -> ts2a (avoid hash attack)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ubpa committed Aug 10, 2020
1 parent d532d8b commit 2465ac8
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 164 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)

project(UECS VERSION 0.11.2)
project(UECS VERSION 0.11.3)
message(STATUS "[Project] ${PROJECT_NAME}")

include(cmake/InitUCMake.cmake)
Expand Down
5 changes: 3 additions & 2 deletions include/UECS/EntityMngr.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include <UContainer/Pool.h>

#include <memory>

namespace Ubpa::UECS {
class World;

Expand Down Expand Up @@ -76,7 +78,6 @@ namespace Ubpa::UECS {
private:
friend class World;
EntityMngr() = default;
~EntityMngr();

static bool IsSet(const CmptType* types, size_t num);

Expand Down Expand Up @@ -104,7 +105,7 @@ namespace Ubpa::UECS {
size_t RequestEntityFreeEntry();
void RecycleEntityEntry(Entity e);

std::unordered_map<size_t, Archetype*> h2a; // archetype's hashcode to archetype
std::unordered_map<CmptTypeSet, std::unique_ptr<Archetype>> ts2a; // archetype's CmptTypeSet to archetype

mutable std::unordered_map<EntityQuery, std::set<Archetype*>> queryCache;
};
Expand Down
11 changes: 4 additions & 7 deletions include/UECS/detail/Archetype.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,17 @@ namespace Ubpa::UECS {
const RTSCmptTraits& GetRTSCmptTraits() const noexcept { return cmptTraits; }

// no Entity
size_t CmptNum() const noexcept { return types.size() - 1; }
size_t CmptNum() const noexcept { return types.data.size() - 1; }

size_t EntityNum() const noexcept { return entityNum; }
size_t EntityNumOfChunk(size_t chunkIdx) const noexcept;
size_t ChunkNum() const noexcept { return chunks.size(); }
size_t ChunkCapacity() const noexcept { return chunkCapacity; }

// add Entity
static CmptTypeSet GenCmptTypeSet(const CmptType* types, size_t num);
template<typename... Cmpts>
static constexpr size_t HashCode() noexcept { return CmptTypeSet::HashCodeOf<Entity, Cmpts...>(); }
static size_t HashCode(const CmptType* types, size_t num) {
CmptTypeSet typeset{ types,num };
typeset.Insert(CmptType::Of<Entity>);
return typeset.HashCode();
}
static CmptTypeSet GenCmptTypeSet();

private:
Archetype() = default;
Expand Down
16 changes: 13 additions & 3 deletions include/UECS/detail/Archetype.inl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace Ubpa::UECS {
template<typename... Cmpts>
Archetype::Archetype(TypeList<Cmpts...>)
: types(TypeList<Entity, Cmpts...>{})
: types(GenCmptTypeSet<Cmpts...>())
{
static_assert(IsSet_v<TypeList<Entity, Cmpts...>>,
"Archetype::Archetype: <Cmpts> must be different");
Expand All @@ -21,7 +21,7 @@ namespace Ubpa::UECS {
Archetype* rst = new Archetype;

rst->types = from->types;
rst->types.Insert(CmptType::Of<Cmpts>...);
rst->types.data.insert(CmptType::Of<Cmpts>...);
rst->cmptTraits = from->cmptTraits;
(rst->cmptTraits.Register<Cmpts>(), ...);

Expand All @@ -37,7 +37,7 @@ namespace Ubpa::UECS {

template<typename... Cmpts>
std::tuple<size_t, std::tuple<Cmpts *...>> Archetype::Create(Entity e) {
assert((types.Contains(CmptType::Of<Cmpts>) &&...) && types.size() == 1 + sizeof...(Cmpts));
assert((types.Contains(CmptType::Of<Cmpts>) &&...) && types.data.size() == 1 + sizeof...(Cmpts));
static_assert((std::is_constructible_v<Cmpts> &&...),
"Archetype::Create: <Cmpts> isn't constructible");
static_assert(IsSet_v<TypeList<Entity, Cmpts...>>,
Expand All @@ -53,4 +53,14 @@ namespace Ubpa::UECS {

return { idx,cmpts };
}

template<typename... Cmpts>
CmptTypeSet Archetype::GenCmptTypeSet() {
if constexpr(sizeof...(Cmpts) == 0)
return Archetype::GenCmptTypeSet(nullptr, 0);
else {
constexpr std::array types = { CmptType::Of<Cmpts>... };
return Archetype::GenCmptTypeSet(types.data(), types.size());
}
}
}
43 changes: 6 additions & 37 deletions include/UECS/detail/CmptTypeSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,13 @@
#include <set>

namespace Ubpa::UECS {
class CmptTypeSet : std::set<CmptType> {
public:
CmptTypeSet() : hashcode{ TypeID<CmptTypeSet> } {}
template<typename... Cmpts>
CmptTypeSet(TypeList<Cmpts...>);
template<typename... CmptTypes,
// for function overload
typename = std::enable_if_t<(std::is_same_v<CmptTypes, CmptType>&&...)>>
CmptTypeSet(CmptTypes... types) : std::set<CmptType>{ types... }, hashcode{ HashCodeOf(*this) } {}
CmptTypeSet(const CmptType* types, size_t num);
struct CmptTypeSet {
std::set<CmptType> data;

template<typename... Cmpts>
static constexpr size_t HashCodeOf() noexcept;

size_t HashCode() const noexcept { return hashcode; }

template<typename... CmptTypes>
void Insert(CmptTypes...);

template<typename... CmptTypes>
void Erase(CmptTypes...) noexcept;
size_t HashCode() const;

void Insert(const CmptType* types, size_t num);
void Erase(const CmptType* types, size_t num);
bool Contains(CmptType type) const;

template<typename CmptTypeContainer>
Expand All @@ -48,23 +33,7 @@ namespace Ubpa::UECS {

bool IsMatch(const EntityQuery& query) const;

template<typename... Cmpts>
bool Is() const;

using std::set<CmptType>::begin;
using std::set<CmptType>::end;
using std::set<CmptType>::size;

bool operator==(const CmptTypeSet& rhs) const;

private:
template<typename... Cmpts>
static constexpr size_t HashCodeOf(TypeList<Cmpts...>) noexcept;

template<typename Container>
static constexpr size_t HashCodeOf(const Container& cmpts) noexcept;

size_t hashcode;
bool operator==(const CmptTypeSet& rhs) const { return data == rhs.data; }
};
}

Expand Down
60 changes: 12 additions & 48 deletions include/UECS/detail/CmptTypeSet.inl
Original file line number Diff line number Diff line change
@@ -1,43 +1,23 @@
#pragma once

namespace Ubpa::UECS {
inline CmptTypeSet::CmptTypeSet(const CmptType* types, size_t num) {
assert(types != nullptr && num != 0);
inline void CmptTypeSet::Insert(const CmptType* types, size_t num) {
for (size_t i = 0; i < num; i++)
insert(types[i]);
hashcode = HashCodeOf(*this);
data.insert(types[i]);
}

template<typename... Cmpts>
CmptTypeSet::CmptTypeSet(TypeList<Cmpts...>)
: std::set<CmptType>{ CmptType::Of<Cmpts>... }, hashcode{ HashCodeOf<Cmpts...>() } {}

template<typename... Cmpts>
constexpr size_t CmptTypeSet::HashCodeOf() noexcept {
return HashCodeOf(QuickSort_t<TypeList<Cmpts...>, TypeID_Less>{});
}

template<typename... CmptTypes>
void CmptTypeSet::Insert(CmptTypes... types) {
static_assert((std::is_same_v<CmptTypes, CmptType> &&...));
(insert(types), ...);
hashcode = HashCodeOf(*this);
}

template<typename... CmptTypes>
void CmptTypeSet::Erase(CmptTypes... types) noexcept {
static_assert((std::is_same_v<CmptTypes, CmptType> &&...));
(erase(types), ...);
hashcode = HashCodeOf(*this);
inline void CmptTypeSet::Erase(const CmptType* types, size_t num) {
for (size_t i = 0; i < num; i++)
data.erase(types[i]);
}

inline bool CmptTypeSet::Contains(CmptType type) const {
return find(type) != cend();
return data.find(type) != data.end();
}

template<typename CmptTypeContainer>
bool CmptTypeSet::Contains(const CmptTypeContainer& types) const {
for (auto type : types) {
for (const auto& type : types) {
if (!Contains(type))
return false;
}
Expand All @@ -49,7 +29,7 @@ namespace Ubpa::UECS {
if (types.empty())
return true;

for (auto type : types) {
for (const auto& type : types) {
if (Contains(type))
return true;
}
Expand All @@ -59,7 +39,7 @@ namespace Ubpa::UECS {

template<typename CmptTypeContainer>
bool CmptTypeSet::NotContain(const CmptTypeContainer& types) const {
for (auto type : types) {
for (const auto& type : types) {
if (Contains(type))
return false;
}
Expand All @@ -84,28 +64,12 @@ namespace Ubpa::UECS {
return IsMatch(query.filter) && IsMatch(query.locator);
}

template<typename... Cmpts>
bool CmptTypeSet::Is() const {
static_assert(IsSet_v<Cmpts>);
return sizeof...(Cmpts) == size() && (Contains(CmptType::Of<Cmpts>) &&...);
}

template<typename... Cmpts>
static constexpr size_t CmptTypeSet::HashCodeOf(TypeList<Cmpts...>) noexcept {
return HashCodeOf(std::array<CmptType, sizeof...(Cmpts)>{CmptType::Of<Cmpts>...});
}

template<typename Container>
static constexpr size_t CmptTypeSet::HashCodeOf(const Container& cmpts) noexcept {
inline size_t CmptTypeSet::HashCode() const {
size_t seed = TypeID<CmptTypeSet>;
for (const CmptType& cmpt : cmpts)
seed = hash_combine(seed, cmpt.HashCode());
for (const auto& t : data)
seed = hash_combine(seed, t.HashCode());
return seed;
}

inline bool CmptTypeSet::operator==(const CmptTypeSet& rhs) const {
return static_cast<const std::set<CmptType>&>(*this) == static_cast<const std::set<CmptType>&>(rhs);
}
}

namespace std {
Expand Down
23 changes: 11 additions & 12 deletions include/UECS/detail/EntityMngr.inl
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ namespace Ubpa::UECS {
static_assert(IsSet_v<TypeList<Entity, Cmpts...>>,
"EntityMngr::GetOrCreateArchetypeOf: <Cmpts> must be different");

constexpr size_t hashcode = Archetype::HashCode<Cmpts...>();
auto target = h2a.find(hashcode);
if(target != h2a.end())
return target->second;
const auto typeset = Archetype::GenCmptTypeSet<Cmpts...>();
auto target = ts2a.find(typeset);
if(target != ts2a.end())
return target->second.get();

auto archetype = new Archetype(TypeList<Cmpts...>{});
h2a[hashcode] = archetype;
ts2a.emplace(std::move(typeset), std::unique_ptr<Archetype>{ archetype });
for (auto& [query, archetypes] : queryCache) {
if (archetype->GetCmptTypeSet().IsMatch(query))
archetypes.insert(archetype);
Expand Down Expand Up @@ -50,23 +50,22 @@ namespace Ubpa::UECS {

const auto& srcCmptTypeSet = srcArchetype->GetCmptTypeSet();
auto dstCmptTypeSet = srcCmptTypeSet;
dstCmptTypeSet.Insert(CmptType::Of<Cmpts>...);
size_t dstCmptTypeSetHashCode = dstCmptTypeSet.HashCode();
dstCmptTypeSet.data.insert(CmptType::Of<Cmpts>...);

// get dstArchetype
Archetype* dstArchetype;
auto target = h2a.find(dstCmptTypeSetHashCode);
if (target == h2a.end()) {
auto target = ts2a.find(dstCmptTypeSet);
if (target == ts2a.end()) {
dstArchetype = Archetype::Add<Cmpts...>(srcArchetype);
assert(dstCmptTypeSet == dstArchetype->GetCmptTypeSet());
h2a[dstCmptTypeSetHashCode] = dstArchetype;
for (auto& [query, archetypes] : queryCache) {
if (dstCmptTypeSet.IsMatch(query))
archetypes.insert(dstArchetype);
}
ts2a.emplace(std::move(dstCmptTypeSet), std::unique_ptr<Archetype>{ dstArchetype });
}
else
dstArchetype = target->second;
dstArchetype = target->second.get();

if (dstArchetype == srcArchetype)
return;
Expand All @@ -75,7 +74,7 @@ namespace Ubpa::UECS {
size_t dstIdxInArchetype = dstArchetype->RequestBuffer();

auto srcCmptTraits = srcArchetype->GetRTSCmptTraits();
for (auto type : srcCmptTypeSet) {
for (const auto& type : srcCmptTypeSet.data) {
auto srcCmpt = srcArchetype->At(type, srcIdxInArchetype);
auto dstCmpt = dstArchetype->At(type, dstIdxInArchetype);
srcCmptTraits.MoveConstruct(type, dstCmpt, srcCmpt);
Expand Down
Loading

0 comments on commit 2465ac8

Please sign in to comment.