#ifndef Magnum_Resource_h #define Magnum_Resource_h /* Copyright © 2010, 2011, 2012 Vladimír Vondruš This file is part of Magnum. Magnum is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3 only, as published by the Free Software Foundation. Magnum is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License version 3 for more details. */ /** @file /Resource.h * @brief Class Magnum::ResourceKey, Magnum::Resource, enum Magnum::ResourceState, Magnum::ResourceDataState, Magnum::ResourcePolicy */ namespace Magnum { /** @relates ResourceManager * @brief %Resource state * * @see Resource::state(), ResourceManager::state() */ enum class ResourceState: std::uint8_t { /** The resource is not yet loaded. */ NotLoaded, /** The resource is not yet loaded and fallback resource is used instead. */ Fallback, /** The resource is loaded, but can be changed by the manager at any time. */ Mutable, /** The resource is loaded and won't be changed by the manager anymore. */ Final }; /** @relates ResourceManager * @brief %Resource data state * * @see ResourceManager::set() */ enum class ResourceDataState: std::uint8_t { /** * The resource can be changed by the manager in the future. This is * slower, as Resource needs to ask the manager for new version every time * the data are accessed, but allows changing the data for e.g. debugging * purposes. */ Mutable = int(ResourceState::Mutable), /** * The resource cannot be changed by the manager in the future. This is * faster, as Resource instances will ask for the data only one time, thus * suitable for production code. */ Final = int(ResourceState::Final) }; /** @relates ResourceManager @brief %Resource policy @see ResourceManager::set(), ResourceManager::free() */ enum class ResourcePolicy: std::uint8_t { /** The resource will stay resident for whole lifetime of resource manager. */ Resident, /** * The resource will be unloaded when manually calling * ResourceManager::free() if nothing references it. */ Manual, /** The resource will be unloaded when last reference to it is gone. */ ReferenceCounted }; /** @brief Key for accessing resource @see ResourceManager::referenceCount(), ResourceManager::state(), ResourceManager::get(), ResourceManager::set(), Resource::key() */ class ResourceKey: public Corrade::Utility::MurmurHash2::Digest { public: /** * @brief Default constructor * * Creates zero key. Note that it is not the same as calling other * constructors with empty string. */ inline constexpr ResourceKey() {} /** @brief Constructor */ inline ResourceKey(const std::string& key): Corrade::Utility::MurmurHash2::Digest(Corrade::Utility::MurmurHash2()(key)) {} /** * @brief Constructor * @todo constexpr */ template inline constexpr ResourceKey(const char(&key)[size]): Corrade::Utility::MurmurHash2::Digest(Corrade::Utility::MurmurHash2()(key)) {} }; #ifndef DOXYGEN_GENERATING_OUTPUT namespace Implementation { template class ResourceManagerData; } #endif /** @brief %Resource reference See ResourceManager for more information. */ template class Resource { friend class Implementation::ResourceManagerData; public: /** * @brief Default constructor * * Creates empty resource. Resources are acquired from the manager by * calling ResourceManager::get(). */ inline Resource(): manager(nullptr), lastCheck(0), _state(ResourceState::Final), data(nullptr) {} /** @brief Copy constructor */ inline Resource(const Resource& other): manager(other.manager), _key(other._key), lastCheck(other.lastCheck), _state(other._state), data(other.data) { if(manager) manager->incrementReferenceCount(_key); } /** @brief Move constructor */ inline Resource(Resource&& other): manager(other.manager), _key(other._key), lastCheck(other.lastCheck), _state(other._state), data(other.data) { other.manager = nullptr; } /** @brief Destructor */ inline ~Resource() { if(manager) manager->decrementReferenceCount(_key); } /** @brief Assignment operator */ Resource& operator=(const Resource& other) { if(manager) manager->decrementReferenceCount(_key); manager = other.manager; _key = other._key; lastCheck = other.lastCheck; _state = other._state; data = other.data; if(manager) manager->incrementReferenceCount(_key); return *this; } /** @brief Assignment move operator */ Resource& operator=(Resource&& other) { if(manager) manager->decrementReferenceCount(_key); manager = other.manager; _key = other._key; lastCheck = other.lastCheck; _state = other._state; data = other.data; other.manager = nullptr; return *this; } /** @brief Resource key */ inline ResourceKey key() const { return _key; } /** * @brief %Resource state * * @see operator bool() */ inline ResourceState state() { acquire(); return _state; } /** * @brief Whether the resource is available * @return False when resource is not loaded and no fallback is * available, true otherwise. * * @see state() */ inline operator bool() { acquire(); return data; } /** * @brief %Resource data * * The resource must be loaded before accessing it. Use boolean * conversion operator or state() for testing whether it is loaded. */ inline operator U*() { acquire(); CORRADE_ASSERT(data, "Resource: accessing not loaded data with key" << key(), nullptr); return static_cast(data); } /** @overload */ inline U* operator->() { acquire(); CORRADE_ASSERT(data, "Resource: accessing not loaded data with key" << key(), nullptr); return static_cast(data); } /** @overload */ inline U& operator*() { acquire(); CORRADE_ASSERT(data, "Resource: accessing not loaded data with key" << key(), *static_cast(data)); return *static_cast(data); } private: inline Resource(Implementation::ResourceManagerData* manager, ResourceKey key): manager(manager), _key(key), lastCheck(0), _state(ResourceState::NotLoaded), data(nullptr) { manager->incrementReferenceCount(key); } void acquire() { /* The data are already final, nothing to do */ if(_state == ResourceState::Final) return; /* Nothing changed since last check */ if(manager->lastChange() < lastCheck) return; /* Acquire new data and save last check time */ const typename Implementation::ResourceManagerData::Data& d = manager->data(_key); lastCheck = manager->lastChange(); /* Try to get the data */ if((data = d.data)) _state = static_cast(d.state); else if((data = manager->fallback())) _state = ResourceState::Fallback; else _state = ResourceState::NotLoaded; } Implementation::ResourceManagerData* manager; ResourceKey _key; std::size_t lastCheck; ResourceState _state; T* data; }; } /* Make the definition complete */ #include "ResourceManager.h" #endif