Lingfeng Yang | c02cb03 | 2020-10-26 14:21:25 -0700 | [diff] [blame] | 1 | // Copyright (C) 2019 The Android Open Source Project |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | #pragma once |
| 15 | |
| 16 | #include "base/Lookup.h" |
| 17 | #include "base/Optional.h" |
| 18 | |
| 19 | #include <functional> |
| 20 | #include <unordered_map> |
| 21 | #include <vector> |
| 22 | |
| 23 | #include <inttypes.h> |
| 24 | #include <stdio.h> |
| 25 | |
| 26 | #define ENTITY_MANAGER_DEBUG 0 |
| 27 | |
| 28 | #if ENTITY_MANAGER_DEBUG |
| 29 | |
| 30 | #define EM_DBG(fmt,...) fprintf(stderr, "%s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__); |
| 31 | |
| 32 | #else |
| 33 | #define EM_DBG(...) |
| 34 | #endif |
| 35 | |
| 36 | #define INVALID_ENTITY_HANDLE 0 |
| 37 | #define INVALID_COMPONENT_HANDLE 0 |
| 38 | |
| 39 | namespace android { |
| 40 | namespace base { |
| 41 | |
| 42 | // EntityManager: A way to represent an abstrat space of objects with handles. |
| 43 | // Each handle is associated with data of type Item for quick access from handles to data. |
| 44 | // Otherwise, entity data is spread through ComponentManagers. |
| 45 | template<size_t indexBits, |
| 46 | size_t generationBits, |
| 47 | size_t typeBits, |
| 48 | class Item> |
| 49 | class EntityManager { |
| 50 | public: |
| 51 | |
| 52 | static_assert(64 == (indexBits + generationBits + typeBits), |
| 53 | "bits of index, generation, and type must add to 64"); |
| 54 | |
| 55 | using EntityHandle = uint64_t; |
| 56 | using IteratorFunc = std::function<void(bool live, EntityHandle h, Item& item)>; |
| 57 | using ConstIteratorFunc = std::function<void(bool live, EntityHandle h, const Item& item)>; |
| 58 | |
| 59 | static size_t getHandleIndex(EntityHandle h) { |
| 60 | return static_cast<size_t>(h & ((1ULL << indexBits) - 1ULL)); |
| 61 | } |
| 62 | |
| 63 | static size_t getHandleGeneration(EntityHandle h) { |
| 64 | return static_cast<size_t>( |
| 65 | (h >> indexBits) & |
| 66 | ((1ULL << generationBits) - 1ULL)); |
| 67 | } |
| 68 | |
| 69 | static size_t getHandleType(EntityHandle h) { |
| 70 | return static_cast<size_t>( |
| 71 | (h >> (indexBits + generationBits)) & |
| 72 | ((1ULL << typeBits) - 1ULL)); |
| 73 | return h & ((1ULL << indexBits) - 1ULL); |
| 74 | } |
| 75 | |
| 76 | static EntityHandle makeHandle( |
| 77 | size_t index, |
| 78 | size_t generation, |
| 79 | size_t type) { |
| 80 | EntityHandle res = index; |
| 81 | res |= generation << indexBits; |
| 82 | res |= type << (indexBits + generationBits); |
| 83 | return res; |
| 84 | } |
| 85 | |
| 86 | static EntityHandle withIndex(EntityHandle h, size_t i) { |
| 87 | return makeHandle(i, getHandleGeneration(h), getHandleType(h)); |
| 88 | } |
| 89 | |
| 90 | static EntityHandle withGeneration(EntityHandle h, size_t nextGen) { |
| 91 | return makeHandle(getHandleIndex(h), nextGen, getHandleType(h)); |
| 92 | } |
| 93 | |
| 94 | static EntityHandle withType(EntityHandle h, size_t newType) { |
| 95 | return makeHandle(getHandleIndex(h), getHandleGeneration(h), newType); |
| 96 | } |
| 97 | |
| 98 | EntityManager() : EntityManager(0) { } |
| 99 | |
Lingfeng Yang | 62dcb4e | 2021-01-06 14:57:56 -0800 | [diff] [blame] | 100 | EntityManager(size_t initialItems) : |
| 101 | mEntries(initialItems), |
| 102 | mFirstFreeIndex(0), |
| 103 | mLiveEntries(0) { |
| 104 | reserve(initialItems); |
| 105 | } |
| 106 | |
Lingfeng Yang | c02cb03 | 2020-10-26 14:21:25 -0700 | [diff] [blame] | 107 | ~EntityManager() { clear(); } |
| 108 | |
| 109 | struct EntityEntry { |
| 110 | EntityHandle handle = 0; |
| 111 | size_t nextFreeIndex = 0; |
| 112 | // 0 is a special generation for brand new entries |
| 113 | // that are not used yet |
| 114 | size_t liveGeneration = 1; |
| 115 | Item item; |
| 116 | }; |
| 117 | |
Lingfeng Yang | 62dcb4e | 2021-01-06 14:57:56 -0800 | [diff] [blame] | 118 | void clear() { |
| 119 | reserve(mEntries.size()); |
Lingfeng Yang | c02cb03 | 2020-10-26 14:21:25 -0700 | [diff] [blame] | 120 | mFirstFreeIndex = 0; |
| 121 | mLiveEntries = 0; |
| 122 | } |
| 123 | |
Lingfeng Yang | 62dcb4e | 2021-01-06 14:57:56 -0800 | [diff] [blame] | 124 | size_t nextFreeIndex() const { |
| 125 | return mFirstFreeIndex; |
| 126 | } |
| 127 | |
Lingfeng Yang | c02cb03 | 2020-10-26 14:21:25 -0700 | [diff] [blame] | 128 | EntityHandle add(const Item& item, size_t type) { |
| 129 | |
| 130 | if (!type) return INVALID_ENTITY_HANDLE; |
| 131 | |
| 132 | size_t maxElements = (1ULL << indexBits); |
| 133 | if (mLiveEntries == maxElements) return INVALID_ENTITY_HANDLE; |
| 134 | |
| 135 | size_t newIndex = mFirstFreeIndex; |
| 136 | |
| 137 | EM_DBG("newIndex/firstFree: %zu type: %zu", newIndex, type); |
| 138 | |
| 139 | size_t neededCapacity = newIndex + 1; |
| 140 | if (maxElements < neededCapacity) return INVALID_ENTITY_HANDLE; |
| 141 | |
| 142 | size_t currentCapacity = mEntries.size(); |
| 143 | size_t nextCapacity = neededCapacity << 1; |
| 144 | if (nextCapacity > maxElements) nextCapacity = maxElements; |
| 145 | |
| 146 | EM_DBG("needed/current/next capacity: %zu %zu %zu", |
| 147 | neededCapacity, |
| 148 | currentCapacity, |
| 149 | nextCapacity); |
| 150 | |
| 151 | if (neededCapacity > mEntries.size()) { |
| 152 | mEntries.resize(nextCapacity); |
| 153 | for (size_t i = currentCapacity; i < nextCapacity; ++i) { |
| 154 | mEntries[i].handle = makeHandle(i, 0, type); |
| 155 | mEntries[i].nextFreeIndex = i + 1; |
| 156 | EM_DBG("new un-init entry: index %zu nextFree %zu", |
| 157 | i, i + 1); |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | mEntries[newIndex].handle = |
| 162 | makeHandle(newIndex, mEntries[newIndex].liveGeneration, type); |
| 163 | mEntries[newIndex].item = item; |
| 164 | |
| 165 | mFirstFreeIndex = mEntries[newIndex].nextFreeIndex; |
| 166 | EM_DBG("created. new first free: %zu", mFirstFreeIndex); |
| 167 | |
| 168 | ++mLiveEntries; |
| 169 | |
| 170 | EM_DBG("result handle: 0x%llx", (unsigned long long)mEntries[newIndex].handle); |
| 171 | |
| 172 | return mEntries[newIndex].handle; |
| 173 | } |
| 174 | |
| 175 | EntityHandle addFixed(EntityHandle fixedHandle, const Item& item, size_t type) { |
| 176 | // 3 cases: |
| 177 | // 1. handle is not allocated and doesn't correspond to mFirstFreeIndex |
| 178 | bool isFreeListNonHead = false; |
| 179 | // 2. handle already exists (replace) |
| 180 | bool isAlloced = false; |
| 181 | // 3. index(handle) == mFirstFreeIndex |
| 182 | bool isFreeListHead = false; |
| 183 | |
| 184 | if (!type) return INVALID_ENTITY_HANDLE; |
| 185 | |
| 186 | size_t maxElements = (1ULL << indexBits); |
| 187 | if (mLiveEntries == maxElements) return INVALID_ENTITY_HANDLE; |
| 188 | |
| 189 | size_t newIndex = getHandleIndex(fixedHandle); |
| 190 | |
| 191 | EM_DBG("newIndex/firstFree: %zu type: %zu", newIndex, type); |
| 192 | |
| 193 | size_t neededCapacity = newIndex + 1; |
| 194 | |
| 195 | if (maxElements < neededCapacity) return INVALID_ENTITY_HANDLE; |
| 196 | |
| 197 | size_t currentCapacity = mEntries.size(); |
| 198 | size_t nextCapacity = neededCapacity << 1; |
| 199 | if (nextCapacity > maxElements) nextCapacity = maxElements; |
| 200 | |
| 201 | EM_DBG("needed/current/next capacity: %zu %zu %zu", |
| 202 | neededCapacity, |
| 203 | currentCapacity, |
| 204 | nextCapacity); |
| 205 | |
| 206 | if (neededCapacity > mEntries.size()) { |
| 207 | mEntries.resize(nextCapacity); |
| 208 | for (size_t i = currentCapacity; i < nextCapacity; ++i) { |
| 209 | mEntries[i].handle = makeHandle(i, 0, type); |
| 210 | mEntries[i].nextFreeIndex = i + 1; |
| 211 | EM_DBG("new un-init entry: index %zu nextFree %zu", |
| 212 | i, i + 1); |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | // Now we ensured that there is enough space to talk about the entry of |
| 217 | // this |fixedHandle|. |
| 218 | if (mFirstFreeIndex == newIndex) { |
| 219 | isFreeListHead = true; |
| 220 | } else { |
| 221 | auto& entry = mEntries[newIndex]; |
| 222 | if (entry.liveGeneration == getHandleGeneration(entry.handle)) { |
| 223 | isAlloced = true; |
| 224 | } else { |
| 225 | isFreeListNonHead = true; |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | mEntries[newIndex].handle = fixedHandle; |
| 230 | mEntries[newIndex].liveGeneration = getHandleGeneration(fixedHandle); |
| 231 | mEntries[newIndex].item = item; |
| 232 | |
| 233 | EM_DBG("new index: %zu", newIndex); |
| 234 | |
| 235 | if (isFreeListHead) { |
| 236 | |
| 237 | EM_DBG("first free index reset from %zu to %zu", |
| 238 | mFirstFreeIndex, mEntries[newIndex].nextFreeIndex); |
| 239 | |
| 240 | mFirstFreeIndex = mEntries[newIndex].nextFreeIndex; |
| 241 | |
| 242 | ++mLiveEntries; |
| 243 | |
| 244 | } else if (isAlloced) { |
| 245 | // Already replaced whatever is there, and since it's already allocated, |
| 246 | // no need to update freelist. |
| 247 | EM_DBG("entry at %zu already alloced. replacing.", newIndex); |
| 248 | } else if (isFreeListNonHead) { |
| 249 | // Go through the freelist and skip over the entry we just added. |
| 250 | size_t prevEntryIndex = mFirstFreeIndex; |
| 251 | |
| 252 | EM_DBG("in free list but not head. reorganizing freelist. " |
| 253 | "start at %zu -> %zu", |
| 254 | mFirstFreeIndex, mEntries[prevEntryIndex].nextFreeIndex); |
| 255 | |
| 256 | while (mEntries[prevEntryIndex].nextFreeIndex != newIndex) { |
| 257 | EM_DBG("next: %zu -> %zu", |
| 258 | prevEntryIndex, |
| 259 | mEntries[prevEntryIndex].nextFreeIndex); |
| 260 | prevEntryIndex = |
| 261 | mEntries[prevEntryIndex].nextFreeIndex; |
| 262 | } |
| 263 | |
| 264 | EM_DBG("finished. set prev entry %zu to new entry's next, %zu", |
| 265 | prevEntryIndex, mEntries[newIndex].nextFreeIndex); |
| 266 | |
| 267 | mEntries[prevEntryIndex].nextFreeIndex = |
| 268 | mEntries[newIndex].nextFreeIndex; |
| 269 | |
| 270 | ++mLiveEntries; |
| 271 | } |
| 272 | |
| 273 | return fixedHandle; |
| 274 | } |
| 275 | void remove(EntityHandle h) { |
| 276 | |
| 277 | if (get(h) == nullptr) return; |
| 278 | |
| 279 | size_t index = getHandleIndex(h); |
| 280 | |
| 281 | EM_DBG("remove handle: 0x%llx -> index %zu", (unsigned long long)h, index); |
| 282 | |
| 283 | auto& entry = mEntries[index]; |
| 284 | |
| 285 | EM_DBG("handle gen: %zu entry gen: %zu", getHandleGeneration(h), entry.liveGeneration); |
| 286 | |
| 287 | ++entry.liveGeneration; |
| 288 | if ((entry.liveGeneration == 0) || |
| 289 | (entry.liveGeneration == (1ULL << generationBits))) { |
| 290 | entry.liveGeneration = 1; |
| 291 | } |
| 292 | |
| 293 | entry.nextFreeIndex = mFirstFreeIndex; |
| 294 | |
| 295 | mFirstFreeIndex = index; |
| 296 | |
| 297 | EM_DBG("new first free: %zu next free: %zu", mFirstFreeIndex, entry.nextFreeIndex); |
| 298 | |
| 299 | --mLiveEntries; |
| 300 | } |
| 301 | |
| 302 | Item* get(EntityHandle h) { |
Lingfeng Yang | 62dcb4e | 2021-01-06 14:57:56 -0800 | [diff] [blame] | 303 | EM_DBG("get 0x%llx", (unsigned long long)h); |
| 304 | size_t index = getHandleIndex(h); |
| 305 | if (index >= mEntries.size()) { |
| 306 | return nullptr; |
| 307 | } |
| 308 | |
| 309 | auto& entry = mEntries[index]; |
| 310 | if (entry.liveGeneration != getHandleGeneration(h)) { |
| 311 | return nullptr; |
| 312 | } |
| 313 | |
| 314 | return &entry.item; |
| 315 | } |
| 316 | |
| 317 | const Item* get_const(EntityHandle h) const { |
Lingfeng Yang | c02cb03 | 2020-10-26 14:21:25 -0700 | [diff] [blame] | 318 | size_t index = getHandleIndex(h); |
| 319 | if (index >= mEntries.size()) return nullptr; |
| 320 | |
Lingfeng Yang | 62dcb4e | 2021-01-06 14:57:56 -0800 | [diff] [blame] | 321 | const auto& entry = mEntries.data() + index; |
| 322 | if (entry->liveGeneration != getHandleGeneration(h)) return nullptr; |
Lingfeng Yang | c02cb03 | 2020-10-26 14:21:25 -0700 | [diff] [blame] | 323 | |
Lingfeng Yang | 62dcb4e | 2021-01-06 14:57:56 -0800 | [diff] [blame] | 324 | return &entry->item; |
Lingfeng Yang | c02cb03 | 2020-10-26 14:21:25 -0700 | [diff] [blame] | 325 | } |
| 326 | |
| 327 | bool isLive(EntityHandle h) const { |
| 328 | size_t index = getHandleIndex(h); |
| 329 | if (index >= mEntries.size()) return false; |
| 330 | |
| 331 | const auto& entry = mEntries[index]; |
| 332 | |
| 333 | return (entry.liveGeneration == getHandleGeneration(h)); |
| 334 | } |
| 335 | |
| 336 | void forEachEntry(IteratorFunc func) { |
| 337 | for (auto& entry: mEntries) { |
| 338 | auto handle = entry.handle; |
| 339 | bool live = isLive(handle); |
| 340 | auto& item = entry.item; |
| 341 | func(live, handle, item); |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | void forEachLiveEntry(IteratorFunc func) { |
| 346 | for (auto& entry: mEntries) { |
| 347 | auto handle = entry.handle; |
| 348 | bool live = isLive(handle); |
| 349 | |
| 350 | if (!live) continue; |
| 351 | |
| 352 | auto& item = entry.item; |
| 353 | func(live, handle, item); |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | void forEachLiveEntry_const(ConstIteratorFunc func) const { |
| 358 | for (auto& entry: mEntries) { |
| 359 | auto handle = entry.handle; |
| 360 | bool live = isLive(handle); |
| 361 | |
| 362 | if (!live) continue; |
| 363 | |
| 364 | const auto& item = entry.item; |
| 365 | func(live, handle, item); |
| 366 | } |
| 367 | } |
| 368 | |
| 369 | private: |
Lingfeng Yang | 62dcb4e | 2021-01-06 14:57:56 -0800 | [diff] [blame] | 370 | void reserve(size_t count) { |
| 371 | if (mEntries.size() < count) { |
| 372 | mEntries.resize(count); |
| 373 | } |
| 374 | for (size_t i = 0; i < count; ++i) { |
| 375 | mEntries[i].handle = makeHandle(i, 0, 1); |
| 376 | mEntries[i].nextFreeIndex = i + 1; |
| 377 | ++mEntries[i].liveGeneration; |
| 378 | if ((mEntries[i].liveGeneration == 0) || |
| 379 | (mEntries[i].liveGeneration == (1ULL << generationBits))) { |
| 380 | mEntries[i].liveGeneration = 1; |
| 381 | } |
| 382 | EM_DBG("new un-init entry: index %zu nextFree %zu", |
| 383 | i, i + 1); |
| 384 | } |
| 385 | } |
Lingfeng Yang | c02cb03 | 2020-10-26 14:21:25 -0700 | [diff] [blame] | 386 | |
| 387 | std::vector<EntityEntry> mEntries; |
| 388 | size_t mFirstFreeIndex; |
| 389 | size_t mLiveEntries; |
| 390 | }; |
| 391 | |
| 392 | // Tracks components over a given space of entities. |
| 393 | // Looking up by entity index is slower, but takes less space overall versus |
| 394 | // a flat array that parallels the entities. |
| 395 | template<size_t indexBits, |
| 396 | size_t generationBits, |
| 397 | size_t typeBits, |
| 398 | class Data> |
| 399 | class ComponentManager { |
| 400 | public: |
| 401 | |
| 402 | static_assert(64 == (indexBits + generationBits + typeBits), |
| 403 | "bits of index, generation, and type must add to 64"); |
| 404 | |
| 405 | using ComponentHandle = uint64_t; |
| 406 | using EntityHandle = uint64_t; |
| 407 | using ComponentIteratorFunc = std::function<void(bool, ComponentHandle componentHandle, EntityHandle entityHandle, Data& data)>; |
| 408 | using ConstComponentIteratorFunc = std::function<void(bool, ComponentHandle componentHandle, EntityHandle entityHandle, const Data& data)>; |
| 409 | |
| 410 | // Adds the given |data| and associates it with EntityHandle. |
| 411 | // We can also opt-in to immediately tracking the handle in the reverse mapping, |
| 412 | // which has an upfront cost in runtime. |
| 413 | // Many uses of ComponentManager don't really need to track the associated entity handle, |
| 414 | // so it is opt-in. |
| 415 | |
| 416 | ComponentHandle add( |
| 417 | EntityHandle h, |
| 418 | const Data& data, |
| 419 | size_t type, |
| 420 | bool tracked = false) { |
| 421 | |
| 422 | InternalItem item = { h, data, tracked }; |
| 423 | auto res = static_cast<ComponentHandle>(mData.add(item, type)); |
| 424 | |
| 425 | if (tracked) { |
| 426 | mEntityToComponentMap[h] = res; |
| 427 | } |
| 428 | |
| 429 | return res; |
| 430 | } |
| 431 | |
| 432 | void clear() { |
| 433 | mData.clear(); |
| 434 | mEntityToComponentMap.clear(); |
| 435 | } |
| 436 | |
| 437 | // If we didn't explicitly track, just fail. |
| 438 | ComponentHandle getComponentHandle(EntityHandle h) const { |
| 439 | auto componentHandlePtr = android::base::find(mEntityToComponentMap, h); |
| 440 | if (!componentHandlePtr) return INVALID_COMPONENT_HANDLE; |
| 441 | return *componentHandlePtr; |
| 442 | } |
| 443 | |
| 444 | EntityHandle getEntityHandle(ComponentHandle h) const { |
| 445 | return mData.get(h)->entityHandle; |
| 446 | } |
| 447 | |
| 448 | void removeByEntity(EntityHandle h) { |
| 449 | auto componentHandle = getComponentHandle(h); |
| 450 | removeByComponent(componentHandle); |
| 451 | } |
| 452 | |
| 453 | void removeByComponent(ComponentHandle h) { |
| 454 | auto item = mData.get(h); |
| 455 | |
| 456 | if (!item) return; |
| 457 | if (item->tracked) { |
| 458 | mEntityToComponentMap.erase(item->entityHandle); |
| 459 | } |
| 460 | |
| 461 | mData.remove(h); |
| 462 | } |
| 463 | |
| 464 | Data* getByEntity(EntityHandle h) { |
| 465 | return getByComponent(getComponentHandle(h)); |
| 466 | } |
| 467 | |
| 468 | Data* getByComponent(ComponentHandle h) { |
| 469 | auto item = mData.get(h); |
| 470 | if (!item) return nullptr; |
| 471 | return &(item->data); |
| 472 | } |
| 473 | |
| 474 | void forEachComponent(ComponentIteratorFunc func) { |
| 475 | mData.forEachEntry( |
| 476 | [func](bool live, typename InternalEntityManager::EntityHandle componentHandle, InternalItem& item) { |
| 477 | func(live, componentHandle, item.entityHandle, item.data); |
| 478 | }); |
| 479 | } |
| 480 | |
| 481 | void forEachLiveComponent(ComponentIteratorFunc func) { |
| 482 | mData.forEachLiveEntry( |
| 483 | [func](bool live, typename InternalEntityManager::EntityHandle componentHandle, InternalItem& item) { |
| 484 | func(live, componentHandle, item.entityHandle, item.data); |
| 485 | }); |
| 486 | } |
| 487 | |
| 488 | void forEachLiveComponent_const(ConstComponentIteratorFunc func) const { |
| 489 | mData.forEachLiveEntry_const( |
| 490 | [func](bool live, typename InternalEntityManager::EntityHandle componentHandle, const InternalItem& item) { |
| 491 | func(live, componentHandle, item.entityHandle, item.data); |
| 492 | }); |
| 493 | } |
| 494 | |
| 495 | private: |
| 496 | struct InternalItem { |
| 497 | EntityHandle entityHandle; |
| 498 | Data data; |
| 499 | bool tracked; |
| 500 | }; |
| 501 | |
| 502 | using InternalEntityManager = EntityManager<indexBits, generationBits, typeBits, InternalItem>; |
| 503 | using EntityToComponentMap = std::unordered_map<EntityHandle, ComponentHandle>; |
| 504 | |
| 505 | mutable InternalEntityManager mData; |
| 506 | EntityToComponentMap mEntityToComponentMap; |
| 507 | }; |
| 508 | |
| 509 | // ComponentManager, but unpacked; uses the same index space as the associated |
| 510 | // entities. Takes more space by default, but not more if all entities have this component. |
| 511 | template<size_t indexBits, |
| 512 | size_t generationBits, |
| 513 | size_t typeBits, |
| 514 | class Data> |
| 515 | class UnpackedComponentManager { |
| 516 | public: |
| 517 | using ComponentHandle = uint64_t; |
| 518 | using EntityHandle = uint64_t; |
| 519 | using ComponentIteratorFunc = |
| 520 | std::function<void(bool, ComponentHandle componentHandle, EntityHandle entityHandle, Data& data)>; |
| 521 | using ConstComponentIteratorFunc = |
| 522 | std::function<void(bool, ComponentHandle componentHandle, EntityHandle entityHandle, const Data& data)>; |
| 523 | |
| 524 | EntityHandle add(EntityHandle h, const Data& data) { |
| 525 | |
| 526 | size_t index = indexOfEntity(h); |
| 527 | |
| 528 | if (index + 1 > mItems.size()) { |
| 529 | mItems.resize((index + 1) * 2); |
| 530 | } |
| 531 | |
| 532 | mItems[index].live = true; |
| 533 | mItems[index].handle = h; |
| 534 | mItems[index].data = data; |
| 535 | |
| 536 | return h; |
| 537 | } |
| 538 | |
| 539 | void clear() { |
| 540 | mItems.clear(); |
| 541 | } |
| 542 | |
| 543 | void remove(EntityHandle h) { |
| 544 | size_t index = indexOfEntity(h); |
| 545 | if (index >= mItems.size()) return; |
| 546 | mItems[index].live = false; |
| 547 | } |
| 548 | |
| 549 | Data* get(EntityHandle h) { |
| 550 | size_t index = indexOfEntity(h); |
| 551 | |
| 552 | if (index + 1 > mItems.size()) { |
| 553 | mItems.resize((index + 1) * 2); |
| 554 | } |
| 555 | |
| 556 | auto item = mItems.data() + index; |
| 557 | if (!item->live) return nullptr; |
| 558 | return &item->data; |
| 559 | } |
| 560 | |
| 561 | const Data* get_const(EntityHandle h) const { |
| 562 | size_t index = indexOfEntity(h); |
| 563 | |
| 564 | if (index + 1 > mItems.size()) { |
| 565 | return nullptr; |
| 566 | } |
| 567 | |
| 568 | auto item = mItems.data() + index; |
| 569 | if (!item->live) return nullptr; |
| 570 | return &item->data; |
| 571 | } |
| 572 | |
| 573 | void forEachComponent(ComponentIteratorFunc func) { |
| 574 | for (auto& item : mItems) { |
| 575 | func(item.live, item.handle, item.handle, item.data); |
| 576 | } |
| 577 | } |
| 578 | |
| 579 | void forEachLiveComponent(ComponentIteratorFunc func) { |
| 580 | for (auto& item : mItems) { |
| 581 | if (item.live) func(item.live, item.handle, item.handle, item.data); |
| 582 | } |
| 583 | } |
| 584 | |
| 585 | void forEachLiveComponent_const(ConstComponentIteratorFunc func) const { |
| 586 | for (auto& item : mItems) { |
| 587 | if (item.live) func(item.live, item.handle, item.handle, item.data); |
| 588 | } |
| 589 | } |
| 590 | |
| 591 | private: |
| 592 | static size_t indexOfEntity(EntityHandle h) { |
| 593 | return EntityManager<indexBits, generationBits, typeBits, int>::getHandleIndex(h); |
| 594 | } |
| 595 | |
| 596 | struct InternalItem { |
| 597 | bool live = false; |
| 598 | EntityHandle handle = 0; |
| 599 | Data data; |
| 600 | }; |
| 601 | |
| 602 | std::vector<InternalItem> mItems; |
| 603 | }; |
| 604 | |
| 605 | } // namespace android |
| 606 | } // namespace base |