#pragma once //////////////////////////////////////////////////////////////////////////////// // The MIT License (MIT) // // Copyright (c) 2018 Nicholas Frechette & Animation Compression Library contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //////////////////////////////////////////////////////////////////////////////// #include "acl/core/impl/compiler_utils.h" #include "acl/core/iallocator.h" #include "acl/core/error.h" #if defined(__APPLE__) || defined(__EMSCRIPTEN__) #include // For posix_memalign #elif defined(_WIN32) #include #endif #if defined(ACL_HAS_ASSERT_CHECKS) && !defined(ACL_NO_ALLOCATOR_TRACKING) #define ACL_ALLOCATOR_TRACK_NUM_ALLOCATIONS #endif // This is used for debugging memory leaks, double frees, etc. // It should never be enabled in production! //#define ACL_ALLOCATOR_TRACK_ALL_ALLOCATIONS #if defined(ACL_ALLOCATOR_TRACK_NUM_ALLOCATIONS) #include #endif #if defined(ACL_ALLOCATOR_TRACK_ALL_ALLOCATIONS) #include #endif ACL_IMPL_FILE_PRAGMA_PUSH namespace acl { //////////////////////////////////////////////////////////////////////////////// // An ANSI allocator implementation. It uses the system malloc/free to manage // memory as well as provides some debugging functionality to track memory leaks. //////////////////////////////////////////////////////////////////////////////// class ansi_allocator : public iallocator { public: ansi_allocator() : iallocator() #if defined(ACL_ALLOCATOR_TRACK_NUM_ALLOCATIONS) , m_allocation_count(0) #endif #if defined(ACL_ALLOCATOR_TRACK_ALL_ALLOCATIONS) , m_debug_allocations() #endif {} virtual ~ansi_allocator() { #if defined(ACL_ALLOCATOR_TRACK_ALL_ALLOCATIONS) if (!m_debug_allocations.empty()) { for (const auto& pair : m_debug_allocations) { printf("Live allocation at the allocator destruction: 0x%p (%zu)\n", pair.second.ptr, pair.second.size); } } #endif #if defined(ACL_ALLOCATOR_TRACK_NUM_ALLOCATIONS) ACL_ASSERT(m_allocation_count == 0, "The number of allocations and deallocations does not match"); #endif } ansi_allocator(const ansi_allocator&) = delete; ansi_allocator& operator=(const ansi_allocator&) = delete; virtual void* allocate(size_t size, size_t alignment = k_default_alignment) override { /* * This is a common requirement for many of the aligned allocators, see * http://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_memalign.html * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-malloc?view=vs-2017 */ ACL_ASSERT(is_power_of_two(alignment), "The alignment must be power of two."); /* * Another common requirement is for size to be an integral multiple of alignment, * i.e. aligned to alignment. As this interface here is supposed to help the user out, * just silently align it for them, while keeping the tracked size intact. */ size_t aligned_size = align_to(size, alignment); #if defined(ACL_ALLOCATOR_TRACK_NUM_ALLOCATIONS) m_allocation_count.fetch_add(1, std::memory_order_relaxed); #endif void* ptr; #if defined(_WIN32) ptr = _aligned_malloc(aligned_size, alignment); #elif defined(__APPLE__) || defined(__EMSCRIPTEN__) ptr = nullptr; posix_memalign(&ptr, std::max(alignment, sizeof(void*)), aligned_size); #elif defined(__ANDROID__) // Don't bother using aligned_size here, as we're doing custom alignment, just mark the var as unused. (void)aligned_size; alignment = std::max(std::max(alignment, sizeof(void*)), sizeof(size_t)); const size_t padded_size = size + alignment + sizeof(size_t); ptr = malloc(padded_size); if (ptr != nullptr) { const void* allocated_ptr = ptr; ptr = align_to(add_offset_to_ptr(ptr, sizeof(size_t)), alignment); const size_t padding_size = safe_static_cast(reinterpret_cast(ptr) - reinterpret_cast(allocated_ptr)); size_t* padding_size_ptr = add_offset_to_ptr(ptr, -sizeof(size_t)); *padding_size_ptr = padding_size; } #else ptr = aligned_alloc(alignment, aligned_size); #endif #if defined(ACL_ALLOCATOR_TRACK_ALL_ALLOCATIONS) m_debug_allocations.insert({ {ptr, AllocationEntry{ptr, size}} }); #endif return ptr; } virtual void deallocate(void* ptr, size_t size) override { if (ptr == nullptr) return; (void)size; #if defined(_WIN32) _aligned_free(ptr); #elif defined(__ANDROID__) const size_t* padding_size_ptr = add_offset_to_ptr(ptr, -sizeof(size_t)); void* allocated_ptr = add_offset_to_ptr(ptr, -*padding_size_ptr); free(allocated_ptr); #else free(ptr); #endif #if defined(ACL_ALLOCATOR_TRACK_ALL_ALLOCATIONS) const auto it = m_debug_allocations.find(ptr); ACL_ASSERT(it != m_debug_allocations.end(), "Attempting to deallocate a pointer that isn't allocated"); ACL_ASSERT(it->second.size == size, "Allocation and deallocation size do not match"); m_debug_allocations.erase(ptr); #endif #if defined(ACL_ALLOCATOR_TRACK_NUM_ALLOCATIONS) const int32_t old_value = m_allocation_count.fetch_sub(1, std::memory_order_relaxed); ACL_ASSERT(old_value > 0, "The number of allocations and deallocations does not match"); #endif } #if defined(ACL_ALLOCATOR_TRACK_NUM_ALLOCATIONS) int32_t get_allocation_count() const { return m_allocation_count.load(std::memory_order_relaxed); } #endif private: #if defined(ACL_ALLOCATOR_TRACK_NUM_ALLOCATIONS) std::atomic m_allocation_count; #endif #if defined(ACL_ALLOCATOR_TRACK_ALL_ALLOCATIONS) struct AllocationEntry { void* ptr; size_t size; }; std::unordered_map m_debug_allocations; #endif }; } ACL_IMPL_FILE_PRAGMA_POP