#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2023 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library 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 IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // If the library is compiled as a DLL, this ensures symbols are exported #define NUCLEX_PLATFORM_SOURCE 1 #include "Nuclex/Platform/Tasks/ResourceManifest.h" #include // for std::uint8_t #include // for assert() #include // for std::array namespace { // ------------------------------------------------------------------------------------------- // /// Calculates the offset of the requirements from the resource manifest /// /// The offset, in bytes, from a pointer to a resource manifest to the first resource /// requirement stored in it /// template constexpr std::size_t getResourceRequirementOffset() { constexpr std::size_t extraByteCount = ( sizeof(TResourceManifest) % alignof(Nuclex::Platform::Tasks::ResourceManifest::Entry) ); return ( sizeof(TResourceManifest) + ( (extraByteCount == 0) ? (0) : (sizeof(TResourceManifest) - extraByteCount) ) ); } // ------------------------------------------------------------------------------------------- // /// Custom alloctor that allocates a shared resource manifest /// /// Type that will be allocated, expected to derive from ResourceManifest /// /// /// Normally, a non-templated implementation of this allocator would seem to suffice, /// but std::allocate_shared() implementations will very likely /// (via the type-changing copy constructor) allocate a type inherited from our /// that packages the reference counter of /// the std::shared_ptr together with the instance. /// template class ResourceManifestAllocator { /// Type of element the allocator is for, required by standard public: typedef TResourceManifest value_type; /// Initializes a new allocator using the specified appended list size /// Number of resources to allocate extra space for public: ResourceManifestAllocator(std::size_t resourceCount) : resourceCount(resourceCount) {} /// /// Creates this allocator as a clone of an allocator for a different type /// /// Type the existing allocator is allocating for /// Existing allocator whose attributes will be copied public: template ResourceManifestAllocator( const ResourceManifestAllocator &other ) : resourceCount(other.resourceCount) {} /// Allocates memory for the specified number of elements (must be 1) /// Number of elements to allocate memory for (must be 1) /// The allocated (but not initialized) memory for the requested type public: TResourceManifest *allocate(std::size_t count) { NUCLEX_PLATFORM_NDEBUG_UNUSED(count); assert(count == 1); std::size_t requiredByteCount = getResourceRequirementOffset() + ( sizeof(Nuclex::Platform::Tasks::ResourceManifest::Entry[2]) * this->resourceCount / 2 ); return reinterpret_cast(new std::uint8_t[requiredByteCount]); } /// Frees memory for the specified element (count must be 1) /// Instance for which memory will be freed /// Number of instances that will be freed (must be 1) public: void deallocate(TResourceManifest *instance, std::size_t count) { NUCLEX_PLATFORM_NDEBUG_UNUSED(count); assert(count == 1); delete[] reinterpret_cast(instance); } /// Size of the resource list for which extra space will be allocated public: std::size_t resourceCount; }; // ------------------------------------------------------------------------------------------- // } // anonymous namespace namespace Nuclex { namespace Platform { namespace Tasks { // ------------------------------------------------------------------------------------------- // std::shared_ptr ResourceManifest::Create( Tasks::ResourceType resourceType, std::size_t resourceAmount ) { std::shared_ptr resourceManifest = ( std::allocate_shared( ResourceManifestAllocator(1) ) ); Entry *resources = reinterpret_cast( reinterpret_cast(resourceManifest.get()) + getResourceRequirementOffset() ); resourceManifest->Count = 1; resourceManifest->Resources = resources; resourceManifest->AccessedHardDriveMask = 0; resources[0].Amount = resourceAmount; resources[0].Type = resourceType; return resourceManifest; } // ------------------------------------------------------------------------------------------- // std::shared_ptr ResourceManifest::Create( Tasks::ResourceType resource1Type, std::size_t resource1Amount, Tasks::ResourceType resource2Type, std::size_t resource2Amount ) { std::shared_ptr resourceManifest = ( std::allocate_shared( ResourceManifestAllocator(2) ) ); Entry *resources = reinterpret_cast( reinterpret_cast(resourceManifest.get()) + getResourceRequirementOffset() ); resourceManifest->Count = 2; resourceManifest->Resources = resources; resourceManifest->AccessedHardDriveMask = 0; resources[0].Amount = resource1Amount; resources[0].Type = resource1Type; resources[1].Amount = resource2Amount; resources[1].Type = resource2Type; return resourceManifest; } // ------------------------------------------------------------------------------------------- // std::shared_ptr ResourceManifest::Create( Tasks::ResourceType resource1Type, std::size_t resource1Amount, Tasks::ResourceType resource2Type, std::size_t resource2Amount, Tasks::ResourceType resource3Type, std::size_t resource3Amount ) { std::shared_ptr resourceManifest = ( std::allocate_shared( ResourceManifestAllocator(3) ) ); Entry *resources = reinterpret_cast( reinterpret_cast(resourceManifest.get()) + getResourceRequirementOffset() ); resourceManifest->Count = 3; resourceManifest->Resources = resources; resourceManifest->AccessedHardDriveMask = 0; resources[0].Amount = resource1Amount; resources[0].Type = resource1Type; resources[1].Amount = resource2Amount; resources[1].Type = resource2Type; resources[2].Amount = resource3Amount; resources[2].Type = resource3Type; return resourceManifest; } // ------------------------------------------------------------------------------------------- // std::shared_ptr ResourceManifest::Combine( const std::shared_ptr &first, const std::shared_ptr &second ) { // Determine the resource types that will be present in the combined manifest std::array presentResourceTypes; { for(std::size_t index = 0; index < MaximumResourceType + 1; ++index) { presentResourceTypes[index] = false; } for(std::size_t index = 0; index < first->Count; ++index) { std::size_t resourceIndex = static_cast(first->Resources[index].Type); presentResourceTypes[resourceIndex] = true; } for(std::size_t index = 0; index < second->Count; ++index) { std::size_t resourceIndex = static_cast(second->Resources[index].Type); presentResourceTypes[resourceIndex] = true; } } // Count the number of unique resource types (so we know how many resource entries // to allocate in the combined manifest) std::size_t resourceTypeCount = 0; for(std::size_t index = 0; index < MaximumResourceType + 1; ++index) { if(presentResourceTypes[index]) { ++resourceTypeCount; } } // Now we can do the usual thing to allocate and fill a combined resource manifest std::shared_ptr resourceManifest = ( std::allocate_shared( ResourceManifestAllocator(resourceTypeCount) ) ); Entry *resources = reinterpret_cast( reinterpret_cast(resourceManifest.get()) + getResourceRequirementOffset() ); resourceManifest->Count = resourceTypeCount; resourceManifest->Resources = resources; resourceManifest->AccessedHardDriveMask = ( first->AccessedHardDriveMask | second->AccessedHardDriveMask ); // Copy the resources from the first manifest over directly std::size_t addedResourceTypeCount = 0; for(std::size_t index = 0; index < first->Count; ++index) { resources[addedResourceTypeCount].Type = first->Resources[index].Type; resources[addedResourceTypeCount].Amount = first->Resources[index].Amount; ++addedResourceTypeCount; } // Then go over the entries in the second manifest and figure out whether // their resource type is already present and needs to be summed or whether // to add a new resource entry to the combined manifest. for(std::size_t index = 0; index < second->Count; ++index) { std::size_t existingIndex; for(existingIndex = 0; existingIndex < first->Count; ++existingIndex) { if(resources[existingIndex].Type == second->Resources[index].Type) { break; } } if(existingIndex < first->Count) { resources[existingIndex].Amount += second->Resources[index].Amount; } else { resources[addedResourceTypeCount].Type = second->Resources[index].Type; resources[addedResourceTypeCount].Amount = second->Resources[index].Amount; ++addedResourceTypeCount; } } return resourceManifest; } // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Platform::Tasks