blob: 4572752552cec1026917fb3c129caaeaa79fb8df [file] [log] [blame]
// Copyright 2019 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef DAWNWIRE_CLIENT_OBJECTALLOCATOR_H_
#define DAWNWIRE_CLIENT_OBJECTALLOCATOR_H_
#include "common/Assert.h"
#include "common/Compiler.h"
#include "dawn_wire/WireCmd_autogen.h"
#include <limits>
#include <memory>
#include <vector>
namespace dawn_wire { namespace client {
template <typename T>
class ObjectAllocator {
public:
struct ObjectAndSerial {
ObjectAndSerial(std::unique_ptr<T> object, uint32_t generation)
: object(std::move(object)), generation(generation) {
}
std::unique_ptr<T> object;
uint32_t generation;
};
ObjectAllocator() {
// ID 0 is nullptr
mObjects.emplace_back(nullptr, 0);
}
template <typename Client>
ObjectAndSerial* New(Client* client) {
uint32_t id = GetNewId();
auto object = std::make_unique<T>(client, 1, id);
client->TrackObject(object.get());
if (id >= mObjects.size()) {
ASSERT(id == mObjects.size());
mObjects.emplace_back(std::move(object), 0);
} else {
ASSERT(mObjects[id].object == nullptr);
mObjects[id].generation++;
// The generation should never overflow. We don't recycle ObjectIds that would
// overflow their next generation.
ASSERT(mObjects[id].generation != 0);
mObjects[id].object = std::move(object);
}
return &mObjects[id];
}
void Free(T* obj) {
ASSERT(obj->IsInList());
if (DAWN_LIKELY(mObjects[obj->id].generation != std::numeric_limits<uint32_t>::max())) {
// Only recycle this ObjectId if the generation won't overflow on the next
// allocation.
FreeId(obj->id);
}
mObjects[obj->id].object = nullptr;
}
T* GetObject(uint32_t id) {
if (id >= mObjects.size()) {
return nullptr;
}
return mObjects[id].object.get();
}
uint32_t GetGeneration(uint32_t id) {
if (id >= mObjects.size()) {
return 0;
}
return mObjects[id].generation;
}
private:
uint32_t GetNewId() {
if (mFreeIds.empty()) {
return mCurrentId++;
}
uint32_t id = mFreeIds.back();
mFreeIds.pop_back();
return id;
}
void FreeId(uint32_t id) {
mFreeIds.push_back(id);
}
// 0 is an ID reserved to represent nullptr
uint32_t mCurrentId = 1;
std::vector<uint32_t> mFreeIds;
std::vector<ObjectAndSerial> mObjects;
};
}} // namespace dawn_wire::client
#endif // DAWNWIRE_CLIENT_OBJECTALLOCATOR_H_