1 // Copyright 2019 The Dawn Authors 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 15 #ifndef DAWNWIRE_CLIENT_OBJECTALLOCATOR_H_ 16 #define DAWNWIRE_CLIENT_OBJECTALLOCATOR_H_ 17 18 #include "common/Assert.h" 19 #include "common/Compiler.h" 20 #include "dawn_wire/WireCmd_autogen.h" 21 22 #include <limits> 23 #include <memory> 24 #include <vector> 25 26 namespace dawn_wire { namespace client { 27 28 template <typename T> 29 class ObjectAllocator { 30 public: 31 struct ObjectAndSerial { ObjectAndSerialObjectAndSerial32 ObjectAndSerial(std::unique_ptr<T> object, uint32_t generation) 33 : object(std::move(object)), generation(generation) { 34 } 35 std::unique_ptr<T> object; 36 uint32_t generation; 37 }; 38 ObjectAllocator()39 ObjectAllocator() { 40 // ID 0 is nullptr 41 mObjects.emplace_back(nullptr, 0); 42 } 43 44 template <typename Client> New(Client * client)45 ObjectAndSerial* New(Client* client) { 46 uint32_t id = GetNewId(); 47 auto object = std::make_unique<T>(client, 1, id); 48 client->TrackObject(object.get()); 49 50 if (id >= mObjects.size()) { 51 ASSERT(id == mObjects.size()); 52 mObjects.emplace_back(std::move(object), 0); 53 } else { 54 ASSERT(mObjects[id].object == nullptr); 55 56 mObjects[id].generation++; 57 // The generation should never overflow. We don't recycle ObjectIds that would 58 // overflow their next generation. 59 ASSERT(mObjects[id].generation != 0); 60 61 mObjects[id].object = std::move(object); 62 } 63 64 return &mObjects[id]; 65 } Free(T * obj)66 void Free(T* obj) { 67 ASSERT(obj->IsInList()); 68 if (DAWN_LIKELY(mObjects[obj->id].generation != std::numeric_limits<uint32_t>::max())) { 69 // Only recycle this ObjectId if the generation won't overflow on the next 70 // allocation. 71 FreeId(obj->id); 72 } 73 mObjects[obj->id].object = nullptr; 74 } 75 GetObject(uint32_t id)76 T* GetObject(uint32_t id) { 77 if (id >= mObjects.size()) { 78 return nullptr; 79 } 80 return mObjects[id].object.get(); 81 } 82 GetGeneration(uint32_t id)83 uint32_t GetGeneration(uint32_t id) { 84 if (id >= mObjects.size()) { 85 return 0; 86 } 87 return mObjects[id].generation; 88 } 89 90 private: GetNewId()91 uint32_t GetNewId() { 92 if (mFreeIds.empty()) { 93 return mCurrentId++; 94 } 95 uint32_t id = mFreeIds.back(); 96 mFreeIds.pop_back(); 97 return id; 98 } FreeId(uint32_t id)99 void FreeId(uint32_t id) { 100 mFreeIds.push_back(id); 101 } 102 103 // 0 is an ID reserved to represent nullptr 104 uint32_t mCurrentId = 1; 105 std::vector<uint32_t> mFreeIds; 106 std::vector<ObjectAndSerial> mObjects; 107 }; 108 }} // namespace dawn_wire::client 109 110 #endif // DAWNWIRE_CLIENT_OBJECTALLOCATOR_H_ 111