1 // Copyright 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #pragma once
16 
17 #include "android/base/Compiler.h"
18 
19 #include <algorithm>
20 #include <atomic>
21 #include <cinttypes>
22 #include <cstdlib>
23 #include <cstring>
24 #include <type_traits>
25 #include <vector>
26 
27 #include <stdio.h>
28 
29 #ifdef _WIN32
30 #include <malloc.h>
31 #endif
32 
33 namespace android {
34 
35 template <class T, size_t align>
36 class AlignedBuf {
37 public:
AlignedBuf(size_t size)38     explicit AlignedBuf(size_t size) {
39         static_assert(align && ((align & (align - 1)) == 0),
40                       "AlignedBuf only supports power-of-2 aligments.");
41         resizeImpl(size);
42     }
43 
AlignedBuf(const AlignedBuf & other)44     AlignedBuf(const AlignedBuf& other) : AlignedBuf(other.mSize) {
45         if (other.mBuffer) { // could have got moved out
46             std::copy(other.mBuffer, other.mBuffer + other.mSize, mBuffer);
47         }
48     }
49 
50     AlignedBuf& operator=(const AlignedBuf& other) {
51         if (this != &other) {
52             AlignedBuf tmp(other);
53             *this = std::move(tmp);
54         }
55         return *this;
56     }
57 
AlignedBuf(AlignedBuf && other)58     AlignedBuf(AlignedBuf&& other) { *this = std::move(other); }
59 
60     AlignedBuf& operator=(AlignedBuf&& other) {
61         mBuffer = other.mBuffer;
62         mSize = other.mSize;
63 
64         other.mBuffer = nullptr;
65         other.mSize = 0;
66 
67         return *this;
68     }
69 
~AlignedBuf()70     ~AlignedBuf() { if (mBuffer) freeImpl(mBuffer); } // account for getting moved out
71 
resize(size_t newSize)72     void resize(size_t newSize) {
73 #if (defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 4) || \
74         defined(__OLD_STD_VERSION__)
75         // Older g++ doesn't support std::is_trivially_copyable.
76         constexpr bool triviallyCopyable =
77                 std::has_trivial_copy_constructor<T>::value;
78 #else
79         constexpr bool triviallyCopyable = std::is_trivially_copyable<T>::value;
80 #endif
81         static_assert(triviallyCopyable,
82                       "AlignedBuf can only resize trivially copyable values");
83 
84         resizeImpl(newSize);
85     }
86 
size()87     size_t size() const { return mSize; }
88 
data()89     T* data() { return mBuffer; }
90 
91     T& operator[](size_t index) { return mBuffer[index]; }
92 
93     const T& operator[](size_t index) const { return mBuffer[index]; }
94 
95     bool operator==(const AlignedBuf& other) const {
96         return 0 == std::memcmp(mBuffer, other.mBuffer, sizeof(T) * std::min(mSize, other.mSize));
97     }
98 
99 private:
100 
resizeImpl(size_t newSize)101     void resizeImpl(size_t newSize) {
102         if (newSize) {
103             size_t pad = std::max(align, sizeof(T));
104             size_t keepSize = std::min(newSize, mSize);
105             size_t newSizeBytes = ((align - 1 + newSize * sizeof(T) + pad) / align) * align;
106 
107             std::vector<T> temp(mBuffer, mBuffer + keepSize);
108             mBuffer = static_cast<T*>(reallocImpl(mBuffer, newSizeBytes));
109             std::copy(temp.data(), temp.data() + keepSize, mBuffer);
110         } else {
111             if (mBuffer) freeImpl(mBuffer);
112             mBuffer = nullptr;
113         }
114 
115         mSize = newSize;
116     }
117 
reallocImpl(void * oldPtr,size_t sizeBytes)118     void* reallocImpl(void* oldPtr, size_t sizeBytes) {
119         if (oldPtr) { freeImpl(oldPtr); }
120         // Platform aligned malloc might not behave right
121         // if we give it an alignemnt value smaller than sizeof(void*).
122         size_t actualAlign = std::max(align, sizeof(void*));
123 #ifdef _WIN32
124         return _aligned_malloc(sizeBytes, actualAlign);
125 #else
126         void* res;
127         if (posix_memalign(&res, actualAlign, sizeBytes)) {
128             fprintf(stderr, "%s: failed to alloc aligned memory\n", __func__);
129             abort();
130         }
131         return res;
132 #endif
133     }
134 
freeImpl(void * ptr)135     void freeImpl(void* ptr) {
136 #ifdef _WIN32
137         _aligned_free(ptr);
138 #else
139         free(ptr);
140 #endif
141 
142     }
143 
144     T* mBuffer = nullptr;
145     size_t mSize = 0;
146 };
147 
148 // Convenience function for aligned malloc across platforms
149 void* aligned_buf_alloc(size_t align, size_t size);
150 void aligned_buf_free(void* buf);
151 
152 }  // namespace android
153