Skip to content

Commit

Permalink
BUG: VTKPolyDataMeshIO should not hang on inf and NaN in ASCII .vtk file
Browse files Browse the repository at this point in the history
`MeshFileReader`/`VTKPolyDataMeshIO` did hang forever when trying to read an
ASCII .vtk file that contained non-numeric chars in "POINTS" section, for
example "Infinity" or "NaN".

With this commit, "inf" and "Infinity" will be interpreted as the floating point
value infinity, while "nan" and "NaN" will be interpreted as Not-A-Number. Other
strings of non-numeric characters in the list of point components (coordinates)
will cause the reader to throw an appropriate `itk::ExceptionObject`.

Addresses issue InsightSoftwareConsortium#3539
"MeshFileReader/VTKPolyDataMeshIO hangs forever on non-numeric chars in "POINTS"
section (including "Infinity" and "NaN")"
  • Loading branch information
N-Dekker authored and hjmjohnson committed Aug 17, 2022
1 parent 42f4530 commit 156c88e
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 15 deletions.
44 changes: 29 additions & 15 deletions Modules/IO/MeshVTK/include/itkVTKPolyDataMeshIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,7 @@ class ITKIOMeshVTK_EXPORT VTKPolyDataMeshIO : public MeshIOBase
if (line.find("POINTS") != std::string::npos)
{
/** Load the point coordinates into the itk::Mesh */
SizeValueType numberOfComponents = this->m_NumberOfPoints * this->m_PointDimension;
for (SizeValueType ii = 0; ii < numberOfComponents; ++ii)
{
inputFile >> buffer[ii];
}
Self::ReadComponentsAsASCII(inputFile, buffer, this->m_NumberOfPoints * this->m_PointDimension);
}
}
}
Expand Down Expand Up @@ -281,11 +277,8 @@ class ITKIOMeshVTK_EXPORT VTKPolyDataMeshIO : public MeshIOBase
}

/** for VECTORS or NORMALS or TENSORS, we could read them directly */
SizeValueType numberOfComponents = this->m_NumberOfPointPixels * this->m_NumberOfPointPixelComponents;
for (SizeValueType ii = 0; ii < numberOfComponents; ++ii)
{
inputFile >> buffer[ii];
}
Self::ReadComponentsAsASCII(
inputFile, buffer, this->m_NumberOfPointPixels * this->m_NumberOfPointPixelComponents);
}
}
}
Expand Down Expand Up @@ -376,11 +369,8 @@ class ITKIOMeshVTK_EXPORT VTKPolyDataMeshIO : public MeshIOBase
}

/** for VECTORS or NORMALS or TENSORS, we could read them directly */
SizeValueType numberOfComponents = this->m_NumberOfCellPixels * this->m_NumberOfCellPixelComponents;
for (SizeValueType ii = 0; ii < numberOfComponents; ++ii)
{
inputFile >> buffer[ii];
}
Self::ReadComponentsAsASCII(
inputFile, buffer, this->m_NumberOfCellPixels * this->m_NumberOfCellPixelComponents);
}
}
}
Expand Down Expand Up @@ -1150,6 +1140,30 @@ class ITKIOMeshVTK_EXPORT VTKPolyDataMeshIO : public MeshIOBase
/** Convenience method returns the IOComponentEnum corresponding to a string. */
IOComponentEnum
GetComponentTypeFromString(const std::string & pointType);

private:
/** Reads the specified number of components from the specified input file into the specified buffer.
* \note This member function is overloaded for `float` and `double`, in order to support reading infinity and NaN
* values.
*/
template <typename T>
static void
ReadComponentsAsASCII(std::ifstream & inputFile, T * const buffer, const SizeValueType numberOfComponents)
{
for (SizeValueType i = 0; i < numberOfComponents; ++i)
{
if (!(inputFile >> buffer[i]))
{
itkGenericExceptionMacro("Failed to read a component from the specified ASCII input file!");
}
}
}

static void
ReadComponentsAsASCII(std::ifstream & inputFile, float * const buffer, const SizeValueType numberOfComponents);

static void
ReadComponentsAsASCII(std::ifstream & inputFile, double * const buffer, const SizeValueType numberOfComponents);
};
} // end namespace itk

Expand Down
76 changes: 76 additions & 0 deletions Modules/IO/MeshVTK/src/itkVTKPolyDataMeshIO.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,69 @@
#include "itkVTKPolyDataMeshIO.h"

#include "itksys/SystemTools.hxx"

#include <double-conversion/string-to-double.h>

#include <fstream>
#include <limits>

namespace itk
{

namespace
{
template <typename TFloatingPoint>
void
ReadFloatingPointsAsASCII(std::ifstream & inputFile,
TFloatingPoint * const buffer,
const SizeValueType numberOfFloatingPoints)
{
std::string str;

for (SizeValueType i = 0; i < numberOfFloatingPoints; ++i)
{
buffer[i] = [&str, &inputFile] {
if (inputFile >> str)
{
using NumericLimits = std::numeric_limits<TFloatingPoint>;

if ((str == "NaN") || (str == "nan"))
{
return NumericLimits::quiet_NaN();
}
if (str == "Infinity")
{
return NumericLimits::infinity();
}
if (str == "-Infinity")
{
return -NumericLimits::infinity();
}
const auto numberOfChars = str.size();

if (numberOfChars <= std::numeric_limits<int>::max())
{
constexpr auto double_NaN = std::numeric_limits<double>::quiet_NaN();
int processedCharCount{ 0 };
const double_conversion::StringToDoubleConverter converter(0, double_NaN, double_NaN, "inf", "nan");
const auto conversionResult =
converter.StringTo<TFloatingPoint>(str.c_str(), static_cast<int>(numberOfChars), &processedCharCount);

if (processedCharCount == static_cast<int>(numberOfChars) && !std::isnan(conversionResult))
{
return conversionResult;
}
}
}
itkGenericExceptionMacro("Failed to read a floating point component from the specified ASCII input file!"
<< (str.empty() ? "" : (" Read characters: \"" + str + "\"")));
}();
}
}

} // namespace


// Constructor
VTKPolyDataMeshIO ::VTKPolyDataMeshIO()
{
Expand Down Expand Up @@ -1584,4 +1643,21 @@ VTKPolyDataMeshIO ::PrintSelf(std::ostream & os, Indent indent) const
os << indent << "cellTensorDataName : " << dataName << std::endl;
}
}

void
VTKPolyDataMeshIO::ReadComponentsAsASCII(std::ifstream & inputFile,
float * const buffer,
const SizeValueType numberOfComponents)
{
ReadFloatingPointsAsASCII(inputFile, buffer, numberOfComponents);
}

void
VTKPolyDataMeshIO::ReadComponentsAsASCII(std::ifstream & inputFile,
double * const buffer,
const SizeValueType numberOfComponents)
{
ReadFloatingPointsAsASCII(inputFile, buffer, numberOfComponents);
}

} // end of namespace itk

0 comments on commit 156c88e

Please sign in to comment.