/* * Copyright 2018 Google * SPDX-License-Identifier: MIT */ #pragma once #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #endif namespace gfxstream { /** * Do not abuse this by using any complicated T. Use it for POD or primitives */ template class AlignedBuf { constexpr static bool triviallyCopyable() { #if (defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 4) || defined(__OLD_STD_VERSION__) // Older g++ doesn't support std::is_trivially_copyable. constexpr bool triviallyCopyable = std::has_trivial_copy_constructor::value; #else constexpr bool triviallyCopyable = std::is_trivially_copyable::value; #endif return triviallyCopyable; } static_assert(triviallyCopyable() && std::is_standard_layout::value && std::is_trivially_default_constructible::value); public: explicit AlignedBuf(size_t size) { static_assert(align && ((align & (align - 1)) == 0), "AlignedBuf only supports power-of-2 aligments."); resizeImpl(size); } AlignedBuf(const AlignedBuf& other) : AlignedBuf(other.mSize) { if (other.mBuffer) { // could have got moved out std::copy(other.mBuffer, other.mBuffer + other.mSize, mBuffer); } } AlignedBuf& operator=(const AlignedBuf& other) { if (this != &other) { AlignedBuf tmp(other); *this = std::move(tmp); } return *this; } AlignedBuf(AlignedBuf&& other) { *this = std::move(other); } AlignedBuf& operator=(AlignedBuf&& other) { mBuffer = other.mBuffer; mSize = other.mSize; other.mBuffer = nullptr; other.mSize = 0; return *this; } ~AlignedBuf() { if (mBuffer) freeImpl(mBuffer); } // account for getting moved out void resize(size_t newSize) { resizeImpl(newSize); } size_t size() const { return mSize; } T* data() { return mBuffer; } T& operator[](size_t index) { return mBuffer[index]; } const T& operator[](size_t index) const { return mBuffer[index]; } bool operator==(const AlignedBuf& other) const { return 0 == std::memcmp(mBuffer, other.mBuffer, sizeof(T) * std::min(mSize, other.mSize)); } private: T* getNewBuffer(size_t newSize) { if (newSize == 0) { return nullptr; } size_t pad = std::max(align, sizeof(T)); size_t newSizeBytes = ((align - 1 + newSize * sizeof(T) + pad) / align) * align; return static_cast(reallocImpl(nullptr, newSizeBytes)); } void resizeImpl(size_t newSize) { T* new_buffer = getNewBuffer(newSize); if (new_buffer && mBuffer) { size_t keepSize = std::min(newSize, mSize); std::copy(mBuffer, mBuffer + keepSize, new_buffer); } if (mBuffer) { freeImpl(mBuffer); } mBuffer = new_buffer; mSize = (new_buffer ? newSize : 0); } void* reallocImpl(void* oldPtr, size_t sizeBytes) { if (oldPtr) { freeImpl(oldPtr); } // Platform aligned malloc might not behave right // if we give it an alignment value smaller than sizeof(void*). size_t actualAlign = std::max(align, sizeof(void*)); #ifdef _WIN32 return _aligned_malloc(sizeBytes, actualAlign); #else void* res; if (posix_memalign(&res, actualAlign, sizeBytes)) { fprintf(stderr, "%s: failed to alloc aligned memory\n", __func__); abort(); } return res; #endif } void freeImpl(void* ptr) { #ifdef _WIN32 _aligned_free(ptr); #else free(ptr); #endif } T* mBuffer = nullptr; size_t mSize = 0; }; // Convenience function for aligned malloc across platforms void* aligned_buf_alloc(size_t align, size_t size); void aligned_buf_free(void* buf); } // namespace gfxstream