/*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkVariableLengthVector_hxx #define itkVariableLengthVector_hxx #include "itkNumericTraitsVariableLengthVectorPixel.h" #include "itkMath.h" #include #include namespace itk { template VariableLengthVector::VariableLengthVector(unsigned int length) : m_Data(nullptr) { Reserve(length); // postcondition(s) itkAssertInDebugAndIgnoreInReleaseMacro(m_Data != nullptr); } template VariableLengthVector::VariableLengthVector(ValueType * datain, unsigned int sz, bool LetArrayManageMemory) : m_LetArrayManageMemory(LetArrayManageMemory) , m_Data(datain) , m_NumElements(sz) {} template VariableLengthVector::VariableLengthVector(const ValueType * datain, unsigned int sz, bool LetArrayManageMemory) : m_LetArrayManageMemory(LetArrayManageMemory) { m_Data = const_cast(datain); m_NumElements = sz; } template VariableLengthVector::VariableLengthVector(const VariableLengthVector & v) { m_NumElements = v.Size(); m_LetArrayManageMemory = true; if (m_NumElements != 0) { m_Data = this->AllocateElements(m_NumElements); itkAssertInDebugAndIgnoreInReleaseMacro(m_Data != nullptr); itkAssertInDebugAndIgnoreInReleaseMacro(v.m_Data != nullptr); std::copy_n(&v.m_Data[0], m_NumElements, &this->m_Data[0]); } else { m_Data = nullptr; } } template VariableLengthVector::VariableLengthVector(Self && v) noexcept : m_LetArrayManageMemory(v.m_LetArrayManageMemory) , m_Data(v.m_Data) , m_NumElements(v.m_NumElements) { v.m_LetArrayManageMemory = true; v.m_Data = nullptr; v.m_NumElements = 0; } template VariableLengthVector & VariableLengthVector::operator=(Self && v) noexcept { itkAssertInDebugAndIgnoreInReleaseMacro(&v != this); // Possible cases: // - both are proxy // => a shallow assignment is enough // - none are proxy // => this->m_Data is released, v content is stolen by *this // - v is a proxy, but not *this // => Fall back to usual copy-assignment // - *this is a proxy, but not v // => v content is stolen by *this, nothing to delete[] if (!IsAProxy() && v.IsAProxy()) { // Fall back to usual copy-assignment return *this = v; } // Delete old data, when data is stolen if (!IsAProxy() && !v.IsAProxy()) { delete[] m_Data; } // Shallow copy of the information m_LetArrayManageMemory = v.m_LetArrayManageMemory; m_Data = v.m_Data; m_NumElements = v.m_NumElements; // Reset v to something assignable and destructible // NB: It's not necessary to always reset v. The choice made is to avoid a // test v.m_LetArrayManageMemory = true; v.m_Data = nullptr; v.m_NumElements = 0; return *this; } template template VariableLengthVector::VariableLengthVector( VariableLengthVectorExpression const & rhs) { m_NumElements = rhs.Size(); m_LetArrayManageMemory = true; m_Data = this->AllocateElements(m_NumElements); // allocate Elements post-condition itkAssertInDebugAndIgnoreInReleaseMacro(m_Data != nullptr); for (ElementIdentifier i = 0; i < m_NumElements; ++i) { this->m_Data[i] = static_cast(rhs[i]); } } template template VariableLengthVector & VariableLengthVector::operator=( VariableLengthVectorExpression const & rhs) { ElementIdentifier const N = rhs.Size(); this->SetSize(N, DontShrinkToFit(), DumpOldValues()); for (ElementIdentifier i = 0; i < N; ++i) { this->m_Data[i] = static_cast(rhs[i]); } return *this; } template VariableLengthVector::~VariableLengthVector() { // if data exists and we are responsible for its memory, get rid of it.. if (m_LetArrayManageMemory) { delete[] m_Data; } } template void VariableLengthVector::Reserve(ElementIdentifier size) { if (m_Data) { if (size > m_NumElements) { TValue * temp = this->AllocateElements(size); itkAssertInDebugAndIgnoreInReleaseMacro(temp); itkAssertInDebugAndIgnoreInReleaseMacro(m_NumElements == 0 || (m_NumElements > 0 && m_Data != nullptr)); // only copy the portion of the data used in the old buffer std::copy_n(m_Data, m_NumElements, temp); if (m_LetArrayManageMemory) { delete[] m_Data; } m_Data = temp; m_LetArrayManageMemory = true; m_NumElements = size; } } else { m_Data = this->AllocateElements(size); m_NumElements = size; m_LetArrayManageMemory = true; } itkAssertInDebugAndIgnoreInReleaseMacro(m_Data != nullptr); } template TValue * VariableLengthVector::AllocateElements(ElementIdentifier size) const { try { return new TValue[size]; } catch (...) { // Intercept std::bad_alloc and any exception thrown from TValue // default constructor. itkGenericExceptionMacro("Failed to allocate memory of length " << size << " for VariableLengthVector."); } } template void VariableLengthVector::SetData(TValue * datain, bool LetArrayManageMemory) { // Free any existing data if we manage its memory if (m_LetArrayManageMemory) { delete[] m_Data; } m_LetArrayManageMemory = LetArrayManageMemory; m_Data = datain; } template void VariableLengthVector::SetData(TValue * datain, unsigned int sz, bool LetArrayManageMemory) { // Free any existing data if we manage its memory if (m_LetArrayManageMemory) { delete[] m_Data; } m_LetArrayManageMemory = LetArrayManageMemory; m_Data = datain; m_NumElements = sz; } template void VariableLengthVector::DestroyExistingData() { // Free any existing data if we manage its memory. if (m_LetArrayManageMemory) { delete[] m_Data; } m_Data = nullptr; m_NumElements = 0; } template template void VariableLengthVector::SetSize(unsigned int sz, TReallocatePolicy reallocatePolicy, TKeepValuesPolicy keepValues) { static_assert( std::is_base_of_v, "The allocation policy does not inherit from itk::VariableLengthVector::AllocateRootPolicy as expected"); static_assert( std::is_base_of_v, "The old values keeping policy does not inherit from itk::VariableLengthVector::KeepValuesRootPolicy as expected"); if (reallocatePolicy(sz, m_NumElements) || !m_LetArrayManageMemory) { TValue * temp = this->AllocateElements(sz); // may throw itkAssertInDebugAndIgnoreInReleaseMacro(temp); itkAssertInDebugAndIgnoreInReleaseMacro(m_NumElements == 0 || (m_NumElements > 0 && m_Data != nullptr)); keepValues(sz, m_NumElements, m_Data, temp); // possible leak if TValue copy may throw // commit changes if (m_LetArrayManageMemory) { delete[] m_Data; } m_Data = temp; m_LetArrayManageMemory = true; } m_NumElements = sz; } template void VariableLengthVector::Fill(TValue const & v) { itkAssertInDebugAndIgnoreInReleaseMacro(m_NumElements == 0 || (m_NumElements > 0 && m_Data != nullptr)); // VC++ version of std::fill_n() expects the output iterator to be valid // instead of expecting the range [OutIt, OutIt+n) to be valid. std::fill(&this->m_Data[0], &this->m_Data[m_NumElements], v); } template VariableLengthVector & VariableLengthVector::operator=(const Self & v) { // No self assignment test is done. Indeed: // - the operator already resists self assignment through a strong exception // guarantee // - the test becomes a pessimization as we never write "v = v;". ElementIdentifier const N = v.Size(); this->SetSize(N, DontShrinkToFit(), DumpOldValues()); // VC++ version of std::copy expects the input range to be valid, and the // output iterator as well (as it's a pointer, it's expected non null) // Hence the manual loop instead itkAssertInDebugAndIgnoreInReleaseMacro(N == 0 || this->m_Data != nullptr); itkAssertInDebugAndIgnoreInReleaseMacro(N == 0 || v.m_Data != nullptr); for (ElementIdentifier i = 0; i != N; ++i) { this->m_Data[i] = v.m_Data[i]; } itkAssertInDebugAndIgnoreInReleaseMacro(m_LetArrayManageMemory); return *this; } template inline VariableLengthVector & VariableLengthVector::FastAssign(const Self & v) { itkAssertInDebugAndIgnoreInReleaseMacro(this->m_LetArrayManageMemory); ElementIdentifier const N = v.Size(); itkAssertInDebugAndIgnoreInReleaseMacro(N > 0); itkAssertInDebugAndIgnoreInReleaseMacro(N == this->Size()); // Redundant precondition checks itkAssertInDebugAndIgnoreInReleaseMacro(v.m_Data != nullptr); itkAssertInDebugAndIgnoreInReleaseMacro(this->m_Data != nullptr); std::copy_n(&v.m_Data[0], N, &this->m_Data[0]); return *this; } template VariableLengthVector & VariableLengthVector::operator=(TValue const & v) { this->Fill(v); return *this; } template VariableLengthVector & VariableLengthVector::operator-() { for (ElementIdentifier i = 0; i < m_NumElements; ++i) { m_Data[i] = -m_Data[i]; } return *this; } template bool VariableLengthVector::operator==(const Self & v) const { if (m_NumElements != v.Size()) { return false; } for (ElementIdentifier i = 0; i < m_NumElements; ++i) { if (Math::NotExactlyEquals(m_Data[i], v[i])) { return false; } } return true; } template auto VariableLengthVector::GetNorm() const -> RealValueType { using std::sqrt; return static_cast(sqrt(this->GetSquaredNorm())); } template auto VariableLengthVector::GetSquaredNorm() const -> RealValueType { RealValueType sum = 0.0; for (ElementIdentifier i = 0; i < this->m_NumElements; ++i) { const RealValueType value = (*this)[i]; sum += value * value; } return sum; } template auto VariableLengthVectorExpression::GetNorm() const -> RealValueType { return itk::GetNorm(*this); } template auto VariableLengthVectorExpression::GetSquaredNorm() const -> RealValueType { return itk::GetSquaredNorm(*this); } } // namespace itk #endif