1 //============================================================================
2 //  Copyright (c) Kitware, Inc.
3 //  All rights reserved.
4 //  See LICENSE.txt for details.
5 //  This software is distributed WITHOUT ANY WARRANTY; without even
6 //  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
7 //  PURPOSE.  See the above copyright notice for more information.
8 //
9 //  Copyright 2014 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
10 //  Copyright 2014 UT-Battelle, LLC.
11 //  Copyright 2014 Los Alamos National Security.
12 //
13 //  Under the terms of Contract DE-NA0003525 with NTESS,
14 //  the U.S. Government retains certain rights in this software.
15 //
16 //  Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
17 //  Laboratory (LANL), the U.S. Government retains certain rights in
18 //  this software.
19 //============================================================================
20 
21 #define vtkm_cont_StorageBasic_cxx
22 #include <vtkm/cont/Logging.h>
23 #include <vtkm/cont/StorageBasic.h>
24 #include <vtkm/internal/Configure.h>
25 
26 #if defined(VTKM_POSIX)
27 #define VTKM_MEMALIGN_POSIX
28 #elif defined(_WIN32)
29 #define VTKM_MEMALIGN_WIN
30 #elif defined(__SSE__)
31 #define VTKM_MEMALIGN_SSE
32 #else
33 #define VTKM_MEMALIGN_NONE
34 #endif
35 
36 #if defined(VTKM_MEMALIGN_POSIX)
37 #include <stdlib.h>
38 #elif defined(VTKM_MEMALIGN_WIN)
39 #include <malloc.h>
40 #elif defined(VTKM_MEMALIGN_SSE)
41 #include <xmmintrin.h>
42 #else
43 #include <malloc.h>
44 #endif
45 
46 #include <cstddef>
47 #include <cstdlib>
48 
49 namespace vtkm
50 {
51 namespace cont
52 {
53 namespace internal
54 {
55 
free_memory(void * mem)56 void free_memory(void* mem)
57 {
58 #if defined(VTKM_MEMALIGN_POSIX)
59   free(mem);
60 #elif defined(VTKM_MEMALIGN_WIN)
61   _aligned_free(mem);
62 #elif defined(VTKM_MEMALIGN_SSE)
63   _mm_free(mem);
64 #else
65   free(mem);
66 #endif
67 }
68 
allocate(size_t size,size_t align)69 void* StorageBasicAllocator::allocate(size_t size, size_t align)
70 {
71 #if defined(VTKM_MEMALIGN_POSIX)
72   void* mem = nullptr;
73   if (posix_memalign(&mem, align, size) != 0)
74   {
75     mem = nullptr;
76   }
77 #elif defined(VTKM_MEMALIGN_WIN)
78   void* mem = _aligned_malloc(size, align);
79 #elif defined(VTKM_MEMALIGN_SSE)
80   void* mem = _mm_malloc(size, align);
81 #else
82   void* mem = malloc(size);
83 #endif
84   return mem;
85 }
86 
StorageBasicBase()87 StorageBasicBase::StorageBasicBase()
88   : Array(nullptr)
89   , AllocatedByteSize(0)
90   , NumberOfValues(0)
91   , DeleteFunction(internal::free_memory)
92 {
93 }
94 
StorageBasicBase(const void * array,vtkm::Id numberOfValues,vtkm::UInt64 sizeOfValue)95 StorageBasicBase::StorageBasicBase(const void* array,
96                                    vtkm::Id numberOfValues,
97                                    vtkm::UInt64 sizeOfValue)
98   : Array(const_cast<void*>(array))
99   , AllocatedByteSize(static_cast<vtkm::UInt64>(numberOfValues) * sizeOfValue)
100   , NumberOfValues(numberOfValues)
101   , DeleteFunction(array == nullptr ? internal::free_memory : nullptr)
102 {
103 }
104 
StorageBasicBase(const void * array,vtkm::Id numberOfValues,vtkm::UInt64 sizeOfValue,void (* deleteFunction)(void *))105 StorageBasicBase::StorageBasicBase(const void* array,
106                                    vtkm::Id numberOfValues,
107                                    vtkm::UInt64 sizeOfValue,
108                                    void (*deleteFunction)(void*))
109   : Array(const_cast<void*>(array))
110   , AllocatedByteSize(static_cast<vtkm::UInt64>(numberOfValues) * sizeOfValue)
111   , NumberOfValues(numberOfValues)
112   , DeleteFunction(deleteFunction)
113 {
114 }
115 
~StorageBasicBase()116 StorageBasicBase::~StorageBasicBase()
117 {
118   this->ReleaseResources();
119 }
120 
StorageBasicBase(StorageBasicBase && src)121 StorageBasicBase::StorageBasicBase(StorageBasicBase&& src)
122   : Array(src.Array)
123   , AllocatedByteSize(src.AllocatedByteSize)
124   , NumberOfValues(src.NumberOfValues)
125   , DeleteFunction(src.DeleteFunction)
126 
127 {
128   src.Array = nullptr;
129   src.AllocatedByteSize = 0;
130   src.NumberOfValues = 0;
131   src.DeleteFunction = nullptr;
132 }
133 
StorageBasicBase(const StorageBasicBase & src)134 StorageBasicBase::StorageBasicBase(const StorageBasicBase& src)
135   : Array(src.Array)
136   , AllocatedByteSize(src.AllocatedByteSize)
137   , NumberOfValues(src.NumberOfValues)
138   , DeleteFunction(src.DeleteFunction)
139 
140 {
141   if (src.DeleteFunction)
142   {
143     throw vtkm::cont::ErrorBadValue(
144       "Attempted to copy a storage array that needs deallocation. "
145       "This is disallowed to prevent complications with deallocation.");
146   }
147 }
148 
operator =(StorageBasicBase && src)149 StorageBasicBase StorageBasicBase::operator=(StorageBasicBase&& src)
150 {
151   this->ReleaseResources();
152   this->Array = src.Array;
153   this->AllocatedByteSize = src.AllocatedByteSize;
154   this->NumberOfValues = src.NumberOfValues;
155   this->DeleteFunction = src.DeleteFunction;
156 
157   src.Array = nullptr;
158   src.AllocatedByteSize = 0;
159   src.NumberOfValues = 0;
160   src.DeleteFunction = nullptr;
161   return *this;
162 }
163 
operator =(const StorageBasicBase & src)164 StorageBasicBase StorageBasicBase::operator=(const StorageBasicBase& src)
165 {
166   if (src.DeleteFunction)
167   {
168     throw vtkm::cont::ErrorBadValue(
169       "Attempted to copy a storage array that needs deallocation. "
170       "This is disallowed to prevent complications with deallocation.");
171   }
172 
173   this->ReleaseResources();
174   this->Array = src.Array;
175   this->AllocatedByteSize = src.AllocatedByteSize;
176   this->NumberOfValues = src.NumberOfValues;
177   this->DeleteFunction = src.DeleteFunction;
178   return *this;
179 }
180 
AllocateValues(vtkm::Id numberOfValues,vtkm::UInt64 sizeOfValue)181 void StorageBasicBase::AllocateValues(vtkm::Id numberOfValues, vtkm::UInt64 sizeOfValue)
182 {
183   if (numberOfValues < 0)
184   {
185     throw vtkm::cont::ErrorBadAllocation("Cannot allocate an array with negative size.");
186   }
187 
188   // Check that the number of bytes won't be more than a size_t can hold.
189   const size_t maxNumValues = std::numeric_limits<size_t>::max() / sizeOfValue;
190   if (static_cast<vtkm::UInt64>(numberOfValues) > maxNumValues)
191   {
192     throw ErrorBadAllocation("Requested allocation exceeds size_t capacity.");
193   }
194 
195   // If we are allocating less data, just shrink the array.
196   // (If allocation empty, drop down so we can deallocate memory.)
197   vtkm::UInt64 allocsize = static_cast<vtkm::UInt64>(numberOfValues) * sizeOfValue;
198   if ((allocsize <= this->AllocatedByteSize) && (numberOfValues > 0))
199   {
200     this->NumberOfValues = numberOfValues;
201     return;
202   }
203 
204   if (!this->DeleteFunction)
205   {
206     throw vtkm::cont::ErrorBadValue("User allocated arrays cannot be reallocated.");
207   }
208 
209   this->ReleaseResources();
210 
211   if (numberOfValues > 0)
212   {
213     this->Array = AllocatorType{}.allocate(allocsize, VTKM_ALLOCATION_ALIGNMENT);
214     this->AllocatedByteSize = allocsize;
215     this->NumberOfValues = numberOfValues;
216     this->DeleteFunction = internal::free_memory;
217     if (this->Array == nullptr)
218     {
219       // Make sure our state is OK.
220       this->AllocatedByteSize = 0;
221       this->NumberOfValues = 0;
222       VTKM_LOG_F(vtkm::cont::LogLevel::MemCont,
223                  "Could not allocate control array of %s.",
224                  vtkm::cont::GetSizeString(allocsize).c_str());
225       throw vtkm::cont::ErrorBadAllocation("Could not allocate basic control array.");
226     }
227     VTKM_LOG_F(vtkm::cont::LogLevel::MemCont,
228                "Allocated control array of %s.",
229                vtkm::cont::GetSizeString(allocsize).c_str());
230   }
231   else
232   {
233     // ReleaseResources should have already set NumberOfValues to 0.
234     VTKM_ASSERT(this->NumberOfValues == 0);
235     VTKM_ASSERT(this->AllocatedByteSize == 0);
236   }
237 }
238 
Shrink(vtkm::Id numberOfValues)239 void StorageBasicBase::Shrink(vtkm::Id numberOfValues)
240 {
241   if (numberOfValues > this->NumberOfValues)
242   {
243     throw vtkm::cont::ErrorBadValue("Shrink method cannot be used to grow array.");
244   }
245 
246   this->NumberOfValues = numberOfValues;
247 }
248 
ReleaseResources()249 void StorageBasicBase::ReleaseResources()
250 {
251   if (this->AllocatedByteSize > 0)
252   {
253     VTKM_ASSERT(this->Array != nullptr);
254     if (this->DeleteFunction)
255     {
256       VTKM_LOG_F(vtkm::cont::LogLevel::MemCont,
257                  "Freeing control allocation of %s.",
258                  vtkm::cont::GetSizeString(this->AllocatedByteSize).c_str());
259       this->DeleteFunction(this->Array);
260     }
261     this->Array = nullptr;
262     this->AllocatedByteSize = 0;
263     this->NumberOfValues = 0;
264   }
265   else
266   {
267     VTKM_ASSERT(this->Array == nullptr);
268   }
269 }
270 
SetBasePointer(const void * ptr,vtkm::Id numberOfValues,vtkm::UInt64 sizeOfValue,void (* deleteFunction)(void *))271 void StorageBasicBase::SetBasePointer(const void* ptr,
272                                       vtkm::Id numberOfValues,
273                                       vtkm::UInt64 sizeOfValue,
274                                       void (*deleteFunction)(void*))
275 {
276   this->ReleaseResources();
277   this->Array = const_cast<void*>(ptr);
278   this->AllocatedByteSize = static_cast<vtkm::UInt64>(numberOfValues) * sizeOfValue;
279   this->NumberOfValues = numberOfValues;
280   this->DeleteFunction = deleteFunction;
281 }
282 
GetBasePointer() const283 void* StorageBasicBase::GetBasePointer() const
284 {
285   return this->Array;
286 }
287 
GetEndPointer(vtkm::Id numberOfValues,vtkm::UInt64 sizeOfValue) const288 void* StorageBasicBase::GetEndPointer(vtkm::Id numberOfValues, vtkm::UInt64 sizeOfValue) const
289 {
290   VTKM_ASSERT(this->NumberOfValues == numberOfValues);
291   if (!this->Array)
292   {
293     return nullptr;
294   }
295 
296   auto p = static_cast<vtkm::UInt8*>(this->Array);
297   auto offset = static_cast<vtkm::UInt64>(this->NumberOfValues) * sizeOfValue;
298   return static_cast<void*>(p + offset);
299 }
300 
GetCapacityPointer() const301 void* StorageBasicBase::GetCapacityPointer() const
302 {
303   if (!this->Array)
304   {
305     return nullptr;
306   }
307   auto v = static_cast<vtkm::UInt8*>(this->Array) + AllocatedByteSize;
308   return static_cast<void*>(v);
309 }
310 
311 #define _VTKM_STORAGE_INSTANTIATE(Type)                                                            \
312   template class VTKM_CONT_EXPORT Storage<Type, StorageTagBasic>;                                  \
313   template class VTKM_CONT_EXPORT Storage<vtkm::Vec<Type, 2>, StorageTagBasic>;                    \
314   template class VTKM_CONT_EXPORT Storage<vtkm::Vec<Type, 3>, StorageTagBasic>;                    \
315   template class VTKM_CONT_EXPORT Storage<vtkm::Vec<Type, 4>, StorageTagBasic>;
316 
317 _VTKM_STORAGE_INSTANTIATE(char)
318 _VTKM_STORAGE_INSTANTIATE(vtkm::Int8)
319 _VTKM_STORAGE_INSTANTIATE(vtkm::UInt8)
320 _VTKM_STORAGE_INSTANTIATE(vtkm::Int16)
321 _VTKM_STORAGE_INSTANTIATE(vtkm::UInt16)
322 _VTKM_STORAGE_INSTANTIATE(vtkm::Int32)
323 _VTKM_STORAGE_INSTANTIATE(vtkm::UInt32)
324 _VTKM_STORAGE_INSTANTIATE(vtkm::Int64)
325 _VTKM_STORAGE_INSTANTIATE(vtkm::UInt64)
326 _VTKM_STORAGE_INSTANTIATE(vtkm::Float32)
327 _VTKM_STORAGE_INSTANTIATE(vtkm::Float64)
328 }
329 }
330 } // namespace vtkm::cont::internal
331