1 /*
2  *  This file is part of RawTherapee.
3  *
4 *  Copyright (c) 2004-2012 Gabor Horvath <hgabor@rawtherapee.com>, Oliver Duis <oduis@oliverduis.de>
5  *
6  *  RawTherapee is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  RawTherapee is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with RawTherapee.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #ifndef _ALIGNEDBUFFER_
20 #define _ALIGNEDBUFFER_
21 
22 #include <cstdint>
23 #include <cstdlib>
24 #include <utility>
25 
26 inline size_t padToAlignment(size_t size, size_t align = 16) {
27     return align * ((size + align - 1) / align);
28 }
29 
30 // Aligned buffer that should be faster
31 template <class T> class AlignedBuffer
32 {
33 
34 private:
35     void* real ;
36     char alignment;
37     size_t allocatedSize;
38     int unitSize;
39 
40 public:
41     T* data ;
42     bool inUse;
43 
44     /** @brief Allocate aligned memory
45     * @param size Number of elements of size T to allocate, i.e. allocated size will be sizeof(T)*size ; set it to 0 if you want to defer the allocation
46     * @param align Expressed in bytes; SSE instructions need 128 bits alignment, which mean 16 bytes, which is the default value
47     */
real(nullptr)48     AlignedBuffer (size_t size = 0, size_t align = 16) : real(nullptr), alignment(align), allocatedSize(0), unitSize(0), data(nullptr), inUse(false)
49     {
50         if (size) {
51             resize(size);
52         }
53     }
54 
~AlignedBuffer()55     ~AlignedBuffer ()
56     {
57         if (real) {
58             free(real);
59         }
60     }
61 
62     /** @brief Return true if there's no memory allocated
63     */
isEmpty()64     bool isEmpty() const
65     {
66         return allocatedSize == 0;
67     }
68 
69     /** @brief Allocate the "size" amount of elements of "structSize" length each
70     * @param size number of elements to allocate
71     * @param structSize if non null, will let you override the default struct's size (unit: byte)
72     * @return True is everything went fine, including freeing memory when size==0, false if the allocation failed
73     */
74     bool resize(size_t size, int structSize = 0)
75     {
76         if (allocatedSize != size) {
77             if (!size) {
78                 // The user want to free the memory
79                 if (real) {
80                     free(real);
81                 }
82 
83                 real = nullptr;
84                 data = nullptr;
85                 inUse = false;
86                 allocatedSize = 0;
87                 unitSize = 0;
88             } else {
89                 unitSize = structSize ? structSize : sizeof(T);
90                 size_t oldAllocatedSize = allocatedSize;
91                 allocatedSize = size * unitSize;
92 
93                 // realloc were used here to limit memory fragmentation, specially when the size was smaller than the previous one.
94                 // But realloc copies the content to the eventually new location, which is unnecessary. To avoid this performance penalty,
95                 // we're freeing the memory and allocate it again if the new size is bigger.
96 
97                 if (allocatedSize < oldAllocatedSize) {
98                     void *temp = realloc(real, allocatedSize + alignment);
99                     if (temp) { // realloc succeeded
100                         real = temp;
101                     } else { // realloc failed => free old buffer and allocate new one
102                         if (real) {
103                             free (real);
104                         }
105                         real = malloc(allocatedSize + alignment);
106                     }
107                 } else {
108                     if (real) {
109                         free (real);
110                     }
111 
112                     real = malloc(allocatedSize + alignment);
113                 }
114 
115                 if (real) {
116                     data = (T*)( ( uintptr_t(real) + uintptr_t(alignment - 1)) / alignment * alignment);
117                     inUse = true;
118                 } else {
119                     allocatedSize = 0;
120                     unitSize = 0;
121                     data = nullptr;
122                     inUse = false;
123                     return false;
124                 }
125             }
126         }
127 
128         return true;
129     }
130 
swap(AlignedBuffer<T> & other)131     void swap(AlignedBuffer<T> &other)
132     {
133         std::swap(real, other.real);
134         std::swap(alignment, other.alignment);
135         std::swap(allocatedSize, other.allocatedSize);
136         std::swap(data, other.data);
137         std::swap(inUse, other.inUse);
138     }
139 
getSize()140     unsigned int getSize() const
141     {
142         return unitSize ? allocatedSize / unitSize : 0;
143     }
144 };
145 
146 #endif
147