vtk-m2/vtkm/cont/StorageBasic.hxx
Kenneth Moreland 726546419d ArrayHandle reallocations of 0
The basic storage has an implicit invariant that if the size of the
storage is 0 then the array is a null pointer. That invariant was broken
if the array was allocated and then Shrink or Allocate was called with
0. In that case, the array remained allocated by the size was set to 0.

This fixes the problem by making sure a Shrink(0) actually does an
Allocate(0) (to clear out the data) and that the basic storage always
frees its memory when allocating a 0 sized array.
2017-03-08 18:53:06 -05:00

173 lines
4.7 KiB
C++

//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//
// Copyright 2014 Sandia Corporation.
// Copyright 2014 UT-Battelle, LLC.
// Copyright 2014 Los Alamos National Security.
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
//
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//============================================================================
namespace vtkm {
namespace cont {
namespace internal {
template<typename T>
Storage<T, vtkm::cont::StorageTagBasic>::Storage(const T *array, vtkm::Id numberOfValues)
: Array(const_cast<T *>(array)),
NumberOfValues(numberOfValues),
AllocatedSize(numberOfValues),
DeallocateOnRelease(false),
UserProvidedMemory( array == nullptr ? false : true)
{
}
template<typename T>
Storage<T, vtkm::cont::StorageTagBasic>::~Storage()
{
this->ReleaseResources();
}
template<typename T>
Storage<T, vtkm::cont::StorageTagBasic>::Storage(const Storage<T, StorageTagBasic> &src)
: Array(src.Array),
NumberOfValues(src.NumberOfValues),
AllocatedSize(src.AllocatedSize),
DeallocateOnRelease(false),
UserProvidedMemory(src.UserProvidedMemory)
{
if (src.DeallocateOnRelease)
{
throw vtkm::cont::ErrorBadValue(
"Attempted to copy a storage array that needs deallocation. "
"This is disallowed to prevent complications with deallocation.");
}
}
template<typename T>
Storage<T, vtkm::cont::StorageTagBasic>&
Storage<T, vtkm::cont::StorageTagBasic>::operator=(const Storage<T, StorageTagBasic> &src)
{
if (src.DeallocateOnRelease)
{
throw vtkm::cont::ErrorBadValue(
"Attempted to copy a storage array that needs deallocation. "
"This is disallowed to prevent complications with deallocation.");
}
this->ReleaseResources();
this->Array = src.Array;
this->NumberOfValues = src.NumberOfValues;
this->AllocatedSize = src.AllocatedSize;
this->DeallocateOnRelease = src.DeallocateOnRelease;
this->UserProvidedMemory = src.UserProvidedMemory;
return *this;
}
template<typename T>
void Storage<T, vtkm::cont::StorageTagBasic>::ReleaseResources()
{
if (this->NumberOfValues > 0)
{
VTKM_ASSERT(this->Array != nullptr);
if (this->DeallocateOnRelease)
{
AllocatorType allocator;
allocator.deallocate(this->Array,
static_cast<std::size_t>(this->AllocatedSize) );
}
this->Array = nullptr;
this->NumberOfValues = 0;
this->AllocatedSize = 0;
}
else
{
VTKM_ASSERT(this->Array == nullptr);
}
}
template<typename T>
void Storage<T, vtkm::cont::StorageTagBasic>::Allocate(vtkm::Id numberOfValues)
{
// If we are allocating less data, just shrink the array.
// (If allocation empty, drop down so we can deallocate memory.)
if ((numberOfValues <= this->AllocatedSize) &&
(numberOfValues > 0))
{
this->NumberOfValues = numberOfValues;
return;
}
if(this->UserProvidedMemory)
{
throw vtkm::cont::ErrorBadValue(
"User allocated arrays cannot be reallocated.");
}
this->ReleaseResources();
try
{
if (numberOfValues > 0)
{
AllocatorType allocator;
this->Array = allocator.allocate(static_cast<std::size_t>(numberOfValues));
this->AllocatedSize = numberOfValues;
this->NumberOfValues = numberOfValues;
}
else
{
// ReleaseResources should have already set AllocatedSize to 0.
VTKM_ASSERT(this->AllocatedSize == 0);
}
}
catch (std::bad_alloc&)
{
// Make sureour state is OK.
this->Array = nullptr;
this->NumberOfValues = 0;
this->AllocatedSize = 0;
throw vtkm::cont::ErrorBadAllocation(
"Could not allocate basic control array.");
}
this->DeallocateOnRelease = true;
this->UserProvidedMemory = false;
}
template<typename T>
void Storage<T, vtkm::cont::StorageTagBasic>::Shrink(vtkm::Id numberOfValues)
{
if (numberOfValues > this->GetNumberOfValues())
{
throw vtkm::cont::ErrorBadValue(
"Shrink method cannot be used to grow array.");
}
this->NumberOfValues = numberOfValues;
}
template<typename T>
T* Storage<T, vtkm::cont::StorageTagBasic>::StealArray()
{
T *saveArray = this->Array;
this->Array = nullptr;
this->NumberOfValues = 0;
this->AllocatedSize = 0;
return saveArray;
}
} // namespace internal
}
} // namespace vtkm::cont