1 /* 2 * Copyright 2018 Google 3 * SPDX-License-Identifier: MIT 4 */ 5 6 #pragma once 7 8 #include <stdio.h> 9 10 #include <algorithm> 11 #include <atomic> 12 #include <cinttypes> 13 #include <cstdlib> 14 #include <cstring> 15 #include <type_traits> 16 #include <vector> 17 18 #ifdef _WIN32 19 #include <malloc.h> 20 #endif 21 22 namespace gfxstream { 23 24 /** 25 * Do not abuse this by using any complicated T. Use it for POD or primitives 26 */ 27 template <class T, size_t align> 28 class AlignedBuf { triviallyCopyable()29 constexpr static bool triviallyCopyable() { 30 #if (defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 4) || defined(__OLD_STD_VERSION__) 31 // Older g++ doesn't support std::is_trivially_copyable. 32 constexpr bool triviallyCopyable = std::has_trivial_copy_constructor<T>::value; 33 #else 34 constexpr bool triviallyCopyable = std::is_trivially_copyable<T>::value; 35 #endif 36 return triviallyCopyable; 37 } 38 static_assert(triviallyCopyable() && std::is_standard_layout<T>::value && 39 std::is_trivially_default_constructible<T>::value); 40 41 public: AlignedBuf(size_t size)42 explicit AlignedBuf(size_t size) { 43 static_assert(align && ((align & (align - 1)) == 0), 44 "AlignedBuf only supports power-of-2 aligments."); 45 resizeImpl(size); 46 } 47 AlignedBuf(const AlignedBuf & other)48 AlignedBuf(const AlignedBuf& other) : AlignedBuf(other.mSize) { 49 if (other.mBuffer) { // could have got moved out 50 std::copy(other.mBuffer, other.mBuffer + other.mSize, mBuffer); 51 } 52 } 53 54 AlignedBuf& operator=(const AlignedBuf& other) { 55 if (this != &other) { 56 AlignedBuf tmp(other); 57 *this = std::move(tmp); 58 } 59 return *this; 60 } 61 AlignedBuf(AlignedBuf && other)62 AlignedBuf(AlignedBuf&& other) { *this = std::move(other); } 63 64 AlignedBuf& operator=(AlignedBuf&& other) { 65 mBuffer = other.mBuffer; 66 mSize = other.mSize; 67 68 other.mBuffer = nullptr; 69 other.mSize = 0; 70 71 return *this; 72 } 73 ~AlignedBuf()74 ~AlignedBuf() { 75 if (mBuffer) freeImpl(mBuffer); 76 } // account for getting moved out 77 resize(size_t newSize)78 void resize(size_t newSize) { resizeImpl(newSize); } 79 size()80 size_t size() const { return mSize; } 81 data()82 T* data() { return mBuffer; } 83 84 T& operator[](size_t index) { return mBuffer[index]; } 85 86 const T& operator[](size_t index) const { return mBuffer[index]; } 87 88 bool operator==(const AlignedBuf& other) const { 89 return 0 == std::memcmp(mBuffer, other.mBuffer, sizeof(T) * std::min(mSize, other.mSize)); 90 } 91 92 private: getNewBuffer(size_t newSize)93 T* getNewBuffer(size_t newSize) { 94 if (newSize == 0) { 95 return nullptr; 96 } 97 size_t pad = std::max(align, sizeof(T)); 98 size_t newSizeBytes = ((align - 1 + newSize * sizeof(T) + pad) / align) * align; 99 return static_cast<T*>(reallocImpl(nullptr, newSizeBytes)); 100 } 101 resizeImpl(size_t newSize)102 void resizeImpl(size_t newSize) { 103 T* new_buffer = getNewBuffer(newSize); 104 if (new_buffer && mBuffer) { 105 size_t keepSize = std::min(newSize, mSize); 106 std::copy(mBuffer, mBuffer + keepSize, new_buffer); 107 } 108 if (mBuffer) { 109 freeImpl(mBuffer); 110 } 111 mBuffer = new_buffer; 112 mSize = (new_buffer ? newSize : 0); 113 } 114 reallocImpl(void * oldPtr,size_t sizeBytes)115 void* reallocImpl(void* oldPtr, size_t sizeBytes) { 116 if (oldPtr) { 117 freeImpl(oldPtr); 118 } 119 // Platform aligned malloc might not behave right 120 // if we give it an alignment value smaller than sizeof(void*). 121 size_t actualAlign = std::max(align, sizeof(void*)); 122 #ifdef _WIN32 123 return _aligned_malloc(sizeBytes, actualAlign); 124 #else 125 void* res; 126 if (posix_memalign(&res, actualAlign, sizeBytes)) { 127 fprintf(stderr, "%s: failed to alloc aligned memory\n", __func__); 128 abort(); 129 } 130 return res; 131 #endif 132 } 133 freeImpl(void * ptr)134 void freeImpl(void* ptr) { 135 #ifdef _WIN32 136 _aligned_free(ptr); 137 #else 138 free(ptr); 139 #endif 140 } 141 142 T* mBuffer = nullptr; 143 size_t mSize = 0; 144 }; 145 146 // Convenience function for aligned malloc across platforms 147 void* aligned_buf_alloc(size_t align, size_t size); 148 void aligned_buf_free(void* buf); 149 150 } // namespace gfxstream 151