Commit 919c1635 authored by Yohann Bearzi's avatar Yohann Bearzi

better handling of initialization of last byte

In vtkBitArray. Unused bits of last byte need to be set w.r.t
NumberOfValues instead of Size.
parent 9e752561
Pipeline #196267 running with stage
......@@ -110,6 +110,7 @@ vtk_add_test_cxx(vtkCommonCoreCxxTests tests
TestArrayUniqueValueDetection.cxx
TestArrayUserTypes.cxx
TestArrayVariants.cxx
TestBitArray.cxx
TestCollection.cxx
TestConditionVariable.cxx
# TestCxxFeatures.cxx # This is in its own exe too.
......
/*=========================================================================
Program: Visualization Toolkit
Module: TestBitArray.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm 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.
=========================================================================*/
#include "vtkBitArray.h"
#include "vtkNew.h"
#include <bitset>
#include <string>
namespace
{
static constexpr unsigned char BitMask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
} // anonymous namespace
// This test makes sure that the unsused reachable bits of the last byte are set
// to zero.
int TestBitArray(int, char*[])
{
vtkNew<vtkBitArray> array;
array->SetNumberOfComponents(1);
array->SetNumberOfValues(1);
// [1]
array->SetValue(0, 1);
unsigned char* data = static_cast<unsigned char*>(array->GetVoidPointer(0));
std::string str;
str = std::bitset<8>(data[0]).to_string();
if (str != std::string("10000000"))
{
std::cerr << "Bit array not initialized as supposed. The raw data is " << str
<< ", it should be 10000000" << std::endl;
return EXIT_FAILURE;
}
array->SetNumberOfValues(0);
// [1111 1011 | 101]
array->InsertValue(0, 1);
array->InsertNextValue(1);
array->InsertNextValue(1);
array->InsertNextValue(1);
array->InsertNextValue(1);
array->InsertNextValue(0);
array->InsertNextValue(1);
array->InsertNextValue(1);
array->InsertNextValue(1);
array->InsertNextValue(0);
array->InsertNextValue(1);
data = static_cast<unsigned char*>(array->GetVoidPointer(0));
str =
std::bitset<8>(data[0]).to_string() + std::string(" ") + std::bitset<8>(data[1]).to_string();
if (str != std::string("11111011 10100000"))
{
std::cerr << "Bit array not initialized as supposed. The raw data is " << str
<< ", it should be 11111011 10100000" << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
unsigned char* ptr = array->WritePointer(0, 18);
// [1111 1011 | 1111 0011 | 10]
ptr[1] = 0xf3;
ptr[2] = (ptr[2] & 0x3f) | 0x80;
str = std::bitset<8>(data[0]).to_string() + std::string(" ") +
std::bitset<8>(data[1]).to_string() + std::string(" ") + std::bitset<8>(data[2]).to_string();
data = static_cast<unsigned char*>(array->GetVoidPointer(0));
if (str != std::string("11111011 11110011 10000000"))
{
std::cerr << "Bit array not initialized as supposed. The raw data is " << str
<< ", it should be 11111011 10110011 10000000" << std::endl;
return EXIT_FAILURE;
}
array->Resize(2);
str = std::bitset<8>(data[0]).to_string();
data = static_cast<unsigned char*>(array->GetVoidPointer(0));
if (str != std::string("11000000"))
{
std::cerr << "Bit array not initialized as supposed. The raw data is " << str
<< ", it should be 11000000" << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
......@@ -18,15 +18,6 @@
#include "vtkIdList.h"
#include "vtkObjectFactory.h"
namespace
{
// This is used to initialize the last byte of the array when allocating memory
// to prevent the presence of uninitialized bits that are out of range for the
// array.
static constexpr unsigned char InitializationMaskForUnusedBitsOfLastByte[8] = { 0xff, 0x80, 0xc0,
0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
} // anonymous namespace
//------------------------------------------------------------------------------
class vtkBitArrayLookup
{
......@@ -89,7 +80,15 @@ unsigned char* vtkBitArray::WritePointer(vtkIdType id, vtkIdType number)
}
if ((--newSize) > this->MaxId)
{
this->MaxId = newSize;
if (newSize < 0 || newSize / 8 == this->MaxId / 8)
{
this->MaxId = newSize;
}
else
{
this->MaxId = newSize;
this->InitializeUnusedBitsInLastByte();
}
}
this->DataChanged();
return this->Array + id / 8;
......@@ -119,7 +118,15 @@ void vtkBitArray::SetArray(unsigned char* array, vtkIdType size, int save, int d
this->Array = array;
this->Size = size;
this->MaxId = size - 1;
if (size < 1 || this->MaxId / 8 == (size - 1) / 8)
{
this->MaxId = size - 1;
}
else
{
this->MaxId = size - 1;
this->InitializeUnusedBitsInLastByte();
}
if (save != 0)
{
......@@ -171,8 +178,6 @@ vtkTypeBool vtkBitArray::Allocate(vtkIdType sz, vtkIdType vtkNotUsed(ext))
this->Size = (sz > 0 ? sz : 1);
if ((this->Array = new unsigned char[(this->Size + 7) / 8]) == nullptr)
{
this->Array[(this->Size + 7) / 8 - 1] &=
InitializationMaskForUnusedBitsOfLastByte[this->Size % 8];
return 0;
}
this->DeleteFunction = ::operator delete[];
......@@ -301,14 +306,16 @@ unsigned char* vtkBitArray::ResizeAndExtend(vtkIdType sz)
}
}
newArray[(newSize + 7) / 8 - 1] &= InitializationMaskForUnusedBitsOfLastByte[newSize % 8];
this->Size = newSize;
this->Array = newArray;
if (newSize < this->Size)
{
this->MaxId = newSize - 1;
if (this->MaxId > 0)
{
this->InitializeUnusedBitsInLastByte();
}
}
this->Size = newSize;
this->Array = newArray;
this->DeleteFunction = ::operator delete[];
this->DataChanged();
......@@ -349,14 +356,20 @@ vtkTypeBool vtkBitArray::Resize(vtkIdType sz)
}
}
newArray[(newSize + 7) / 8 - 1] &= InitializationMaskForUnusedBitsOfLastByte[newSize % 8];
this->Size = newSize;
this->Array = newArray;
if (newSize < this->Size)
{
this->MaxId = newSize - 1;
if (newSize < 1 || this->MaxId / 8 == (newSize - 1) / 8)
{
this->MaxId = newSize - 1;
}
else
{
this->MaxId = newSize - 1;
this->InitializeUnusedBitsInLastByte();
}
}
this->Size = newSize;
this->Array = newArray;
this->DeleteFunction = ::operator delete[];
this->DataChanged();
......@@ -370,6 +383,21 @@ void vtkBitArray::SetNumberOfTuples(vtkIdType number)
this->SetNumberOfValues(number * this->NumberOfComponents);
}
//------------------------------------------------------------------------------
bool vtkBitArray::SetNumberOfValues(vtkIdType number)
{
vtkIdType previousMaxId = this->MaxId;
if (!this->Superclass::SetNumberOfValues(number))
{
return false;
}
if (previousMaxId < 0 || previousMaxId / 8 != this->MaxId / 8)
{
this->InitializeUnusedBitsInLastByte();
}
return true;
}
//------------------------------------------------------------------------------
// Description:
// Set the tuple at the ith location using the jth tuple in the source array.
......
......@@ -65,6 +65,12 @@ public:
*/
void SetNumberOfTuples(vtkIdType number) override;
/**
* In addition to setting the number of values, this method also sets the
* unused bits of the last byte of the array.
*/
bool SetNumberOfValues(vtkIdType number) override;
/**
* Set the tuple at the ith location using the jth tuple in the source array.
* This method assumes that the two arrays have the same type
......@@ -295,6 +301,17 @@ protected:
vtkBitArray();
~vtkBitArray() override;
/**
* This method should be called
* whenever MaxId needs to be changed, as this method fills the unused bits of
* the last byte to zero. If those bits are kept uninitialized, one can
* trigger errors when reading the last byte.
*
* @warning The buffer `this->Array` needs to already be allocated prior to calling this
* method.
*/
virtual void InitializeUnusedBitsInLastByte();
unsigned char* Array; // pointer to data
unsigned char* ResizeAndExtend(vtkIdType sz);
// function to resize data
......@@ -338,7 +355,15 @@ inline void vtkBitArray::InsertValue(vtkIdType id, int i)
: (this->Array[id / 8] & (~(0x80 >> id % 8))));
if (id > this->MaxId)
{
this->MaxId = id;
if (this->MaxId > 0 && id / 8 == this->MaxId / 8)
{
this->MaxId = id;
}
else
{
this->MaxId = id;
this->InitializeUnusedBitsInLastByte();
}
}
this->DataChanged();
}
......@@ -355,7 +380,7 @@ inline void vtkBitArray::InsertVariantValue(vtkIdType id, vtkVariant value)
inline vtkIdType vtkBitArray::InsertNextValue(int i)
{
this->InsertValue(++this->MaxId, i);
this->InsertValue(this->MaxId + 1, i);
this->DataChanged();
return this->MaxId;
}
......@@ -365,4 +390,15 @@ inline void vtkBitArray::Squeeze()
this->ResizeAndExtend(this->MaxId + 1);
}
inline void vtkBitArray::InitializeUnusedBitsInLastByte()
{
// This is used to initialize the last byte of the array when allocating memory
// to prevent the presence of uninitialized bits that are out of range for the
// array.
static constexpr unsigned char InitializationMaskForUnusedBitsOfLastByte[8] = { 0x80, 0xc0, 0xe0,
0xf0, 0xf8, 0xfc, 0xfe, 0xff };
this->Array[this->MaxId / 8] &= InitializationMaskForUnusedBitsOfLastByte[this->MaxId % 8];
}
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment