Code refactor: move more memory allocation logic into device API.
[blender-staging.git] / intern / cycles / util / util_vector.h
1 /*
2  * Copyright 2011-2013 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifndef __UTIL_VECTOR_H__
18 #define __UTIL_VECTOR_H__
19
20 /* Vector */
21
22 #include <cassert>
23 #include <cstring>
24 #include <vector>
25
26 #include "util/util_aligned_malloc.h"
27 #include "util/util_guarded_allocator.h"
28 #include "util/util_types.h"
29
30 CCL_NAMESPACE_BEGIN
31
32 /* Vector
33  *
34  * Own subclass-ed vestion of std::vector. Subclass is needed because:
35  *
36  * - Use own allocator which keeps track of used/peak memory.
37  *
38  * - Have method to ensure capacity is re-set to 0.
39  */
40 template<typename value_type,
41          typename allocator_type = GuardedAllocator<value_type> >
42 class vector : public std::vector<value_type, allocator_type>
43 {
44 public:
45         /* Default constructor. */
46         explicit vector() : std::vector<value_type, allocator_type>() {  }
47
48         /* Fill constructor. */
49         explicit vector(size_t n, const value_type& val = value_type())
50                 : std::vector<value_type, allocator_type>(n, val) {  }
51
52         /* Range constructor. */
53         template <class InputIterator>
54         vector(InputIterator first, InputIterator last)
55                 : std::vector<value_type, allocator_type>(first, last) {  }
56
57         /* Copy constructor. */
58         vector(const vector &x) : std::vector<value_type, allocator_type>(x) {  }
59
60         void shrink_to_fit(void)
61         {
62 #if __cplusplus < 201103L
63                 vector<value_type>().swap(*this);
64 #else
65                 std::vector<value_type, allocator_type>::shrink_to_fit();
66 #endif
67         }
68
69         void free_memory(void)
70         {
71                 std::vector<value_type, allocator_type>::resize(0);
72                 shrink_to_fit();
73         }
74
75         /* Some external API might demand working with std::vector. */
76         operator std::vector<value_type>()
77         {
78                 return std::vector<value_type>(this->begin(), this->end());
79         }
80 };
81
82 /* Array
83  *
84  * Simplified version of vector, serving multiple purposes:
85  * - somewhat faster in that it does not clear memory on resize/alloc,
86  *   this was actually showing up in profiles quite significantly. it
87  *   also does not run any constructors/destructors
88  * - if this is used, we are not tempted to use inefficient operations
89  * - aligned allocation for SSE data types */
90
91 template<typename T, size_t alignment = 16>
92 class array
93 {
94 public:
95         array()
96         : data_(NULL),
97           datasize_(0),
98           capacity_(0)
99         {}
100
101         explicit array(size_t newsize)
102         {
103                 if(newsize == 0) {
104                         data_ = NULL;
105                         datasize_ = 0;
106                         capacity_ = 0;
107                 }
108                 else {
109                         data_ = mem_allocate(newsize);
110                         datasize_ = newsize;
111                         capacity_ = datasize_;
112                 }
113         }
114
115         array(const array& from)
116         {
117                 if(from.datasize_ == 0) {
118                         data_ = NULL;
119                         datasize_ = 0;
120                         capacity_ = 0;
121                 }
122                 else {
123                         data_ = mem_allocate(from.datasize_);
124                         memcpy(data_, from.data_, from.datasize_*sizeof(T));
125                         datasize_ = from.datasize_;
126                         capacity_ = datasize_;
127                 }
128         }
129
130         array& operator=(const array& from)
131         {
132                 if(this != &from) {
133                         resize(from.size());
134                         memcpy(data_, from.data_, datasize_*sizeof(T));
135                 }
136
137                 return *this;
138         }
139
140         array& operator=(const vector<T>& from)
141         {
142                 resize(from.size());
143
144                 if(from.size() > 0) {
145                         memcpy(data_, &from[0], datasize_*sizeof(T));
146                 }
147
148                 return *this;
149         }
150
151         ~array()
152         {
153                 mem_free(data_, capacity_);
154         }
155
156         bool operator==(const array<T>& other) const
157         {
158                 if(datasize_ != other.datasize_) {
159                         return false;
160                 }
161
162                 return memcmp(data_, other.data_, datasize_*sizeof(T)) == 0;
163         }
164
165         void steal_data(array& from)
166         {
167                 if(this != &from) {
168                         clear();
169
170                         data_ = from.data_;
171                         datasize_ = from.datasize_;
172                         capacity_ = from.capacity_;
173
174                         from.data_ = NULL;
175                         from.datasize_ = 0;
176                         from.capacity_ = 0;
177                 }
178         }
179
180         T *steal_pointer()
181         {
182                 T *ptr = data_;
183                 data_ = NULL;
184                 clear();
185                 return ptr;
186         }
187
188         T* resize(size_t newsize)
189         {
190                 if(newsize == 0) {
191                         clear();
192                 }
193                 else if(newsize != datasize_) {
194                         if(newsize > capacity_) {
195                                 T *newdata = mem_allocate(newsize);
196                                 if(newdata == NULL) {
197                                         /* Allocation failed, likely out of memory. */
198                                         clear();
199                                         return NULL;
200                                 }
201                                 else if(data_ != NULL) {
202                                         memcpy(newdata, data_, ((datasize_ < newsize)? datasize_: newsize)*sizeof(T));
203                                         mem_free(data_, capacity_);
204                                 }
205                                 data_ = newdata;
206                                 capacity_ = newsize;
207                         }
208                         datasize_ = newsize;
209                 }
210                 return data_;
211         }
212
213         void clear()
214         {
215                 if(data_ != NULL) {
216                         mem_free(data_, capacity_);
217                         data_ = NULL;
218                 }
219                 datasize_ = 0;
220                 capacity_ = 0;
221         }
222
223         size_t empty() const
224         {
225                 return datasize_ == 0;
226         }
227
228         size_t size() const
229         {
230                 return datasize_;
231         }
232
233         T* data()
234         {
235                 return data_;
236         }
237
238         const T* data() const
239         {
240                 return data_;
241         }
242
243         T& operator[](size_t i) const
244         {
245                 assert(i < datasize_);
246                 return data_[i];
247         }
248
249         void reserve(size_t newcapacity)
250         {
251                 if(newcapacity > capacity_) {
252                         T *newdata = mem_allocate(newcapacity);
253                         if(data_ != NULL) {
254                                 memcpy(newdata, data_, ((datasize_ < newcapacity)? datasize_: newcapacity)*sizeof(T));
255                                 mem_free(data_, capacity_);
256                         }
257                         data_ = newdata;
258                         capacity_ = newcapacity;
259                 }
260         }
261
262         size_t capacity() const
263         {
264                 return capacity_;
265         }
266
267         // do not use this method unless you are sure the code is not performance critical
268         void push_back_slow(const T& t)
269         {
270                 if(capacity_ == datasize_)
271                 {
272                         reserve(datasize_ == 0 ? 1 : (size_t)((datasize_ + 1) * 1.2));
273                 }
274
275                 data_[datasize_++] = t;
276         }
277
278         void push_back_reserved(const T& t)
279         {
280                 assert(datasize_ < capacity_);
281                 push_back_slow(t);
282         }
283
284         void append(const array<T>& from)
285         {
286                 if(from.size()) {
287                         size_t old_size = size();
288                         resize(old_size + from.size());
289                         memcpy(data_ + old_size, from.data(), sizeof(T) * from.size());
290                 }
291         }
292
293 protected:
294         inline T* mem_allocate(size_t N)
295         {
296                 if(N == 0) {
297                         return NULL;
298                 }
299                 T *mem = (T*)util_aligned_malloc(sizeof(T)*N, alignment);
300                 if(mem != NULL) {
301                         util_guarded_mem_alloc(sizeof(T)*N);
302                 }
303                 else {
304                         throw std::bad_alloc();
305                 }
306                 return mem;
307         }
308
309         inline void mem_free(T *mem, size_t N)
310         {
311                 if(mem != NULL) {
312                         util_guarded_mem_free(sizeof(T)*N);
313                         util_aligned_free(mem);
314                 }
315         }
316
317         T *data_;
318         size_t datasize_;
319         size_t capacity_;
320 };
321
322 CCL_NAMESPACE_END
323
324 #endif /* __UTIL_VECTOR_H__ */
325