Skip to content

Commit 678b97e

Browse files
N-Dekkerhjmjohnson
authored andcommitted
BUG: VTKPolyDataMeshIO should not hang on inf and NaN in ASCII .vtk file
`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 #3539 "MeshFileReader/VTKPolyDataMeshIO hangs forever on non-numeric chars in "POINTS" section (including "Infinity" and "NaN")"
1 parent 87f8716 commit 678b97e

File tree

2 files changed

+105
-15
lines changed

2 files changed

+105
-15
lines changed

Modules/IO/MeshVTK/include/itkVTKPolyDataMeshIO.h

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,7 @@ class ITKIOMeshVTK_EXPORT VTKPolyDataMeshIO : public MeshIOBase
205205
if (line.find("POINTS") != std::string::npos)
206206
{
207207
/** Load the point coordinates into the itk::Mesh */
208-
SizeValueType numberOfComponents = this->m_NumberOfPoints * this->m_PointDimension;
209-
for (SizeValueType ii = 0; ii < numberOfComponents; ++ii)
210-
{
211-
inputFile >> buffer[ii];
212-
}
208+
Self::ReadComponentsAsASCII(inputFile, buffer, this->m_NumberOfPoints * this->m_PointDimension);
213209
}
214210
}
215211
}
@@ -281,11 +277,8 @@ class ITKIOMeshVTK_EXPORT VTKPolyDataMeshIO : public MeshIOBase
281277
}
282278

283279
/** for VECTORS or NORMALS or TENSORS, we could read them directly */
284-
SizeValueType numberOfComponents = this->m_NumberOfPointPixels * this->m_NumberOfPointPixelComponents;
285-
for (SizeValueType ii = 0; ii < numberOfComponents; ++ii)
286-
{
287-
inputFile >> buffer[ii];
288-
}
280+
Self::ReadComponentsAsASCII(
281+
inputFile, buffer, this->m_NumberOfPointPixels * this->m_NumberOfPointPixelComponents);
289282
}
290283
}
291284
}
@@ -376,11 +369,8 @@ class ITKIOMeshVTK_EXPORT VTKPolyDataMeshIO : public MeshIOBase
376369
}
377370

378371
/** for VECTORS or NORMALS or TENSORS, we could read them directly */
379-
SizeValueType numberOfComponents = this->m_NumberOfCellPixels * this->m_NumberOfCellPixelComponents;
380-
for (SizeValueType ii = 0; ii < numberOfComponents; ++ii)
381-
{
382-
inputFile >> buffer[ii];
383-
}
372+
Self::ReadComponentsAsASCII(
373+
inputFile, buffer, this->m_NumberOfCellPixels * this->m_NumberOfCellPixelComponents);
384374
}
385375
}
386376
}
@@ -1150,6 +1140,30 @@ class ITKIOMeshVTK_EXPORT VTKPolyDataMeshIO : public MeshIOBase
11501140
/** Convenience method returns the IOComponentEnum corresponding to a string. */
11511141
IOComponentEnum
11521142
GetComponentTypeFromString(const std::string & pointType);
1143+
1144+
private:
1145+
/** Reads the specified number of components from the specified input file into the specified buffer.
1146+
* \note This member function is overloaded for `float` and `double`, in order to support reading infinity and NaN
1147+
* values.
1148+
*/
1149+
template <typename T>
1150+
static void
1151+
ReadComponentsAsASCII(std::ifstream & inputFile, T * const buffer, const SizeValueType numberOfComponents)
1152+
{
1153+
for (SizeValueType i = 0; i < numberOfComponents; ++i)
1154+
{
1155+
if (!(inputFile >> buffer[i]))
1156+
{
1157+
itkGenericExceptionMacro("Failed to read a component from the specified ASCII input file!");
1158+
}
1159+
}
1160+
}
1161+
1162+
static void
1163+
ReadComponentsAsASCII(std::ifstream & inputFile, float * const buffer, const SizeValueType numberOfComponents);
1164+
1165+
static void
1166+
ReadComponentsAsASCII(std::ifstream & inputFile, double * const buffer, const SizeValueType numberOfComponents);
11531167
};
11541168
} // end namespace itk
11551169

Modules/IO/MeshVTK/src/itkVTKPolyDataMeshIO.cxx

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,69 @@
1919
#include "itkVTKPolyDataMeshIO.h"
2020

2121
#include "itksys/SystemTools.hxx"
22+
23+
#include <double-conversion/string-to-double.h>
24+
2225
#include <fstream>
26+
#include <limits>
2327

2428
namespace itk
2529
{
30+
31+
namespace
32+
{
33+
template <typename TFloatingPoint>
34+
void
35+
ReadFloatingPointsAsASCII(std::ifstream & inputFile,
36+
TFloatingPoint * const buffer,
37+
const SizeValueType numberOfFloatingPoints)
38+
{
39+
std::string str;
40+
41+
for (SizeValueType i = 0; i < numberOfFloatingPoints; ++i)
42+
{
43+
buffer[i] = [&str, &inputFile] {
44+
if (inputFile >> str)
45+
{
46+
using NumericLimits = std::numeric_limits<TFloatingPoint>;
47+
48+
if ((str == "NaN") || (str == "nan"))
49+
{
50+
return NumericLimits::quiet_NaN();
51+
}
52+
if (str == "Infinity")
53+
{
54+
return NumericLimits::infinity();
55+
}
56+
if (str == "-Infinity")
57+
{
58+
return -NumericLimits::infinity();
59+
}
60+
const auto numberOfChars = str.size();
61+
62+
if (numberOfChars <= std::numeric_limits<int>::max())
63+
{
64+
constexpr auto double_NaN = std::numeric_limits<double>::quiet_NaN();
65+
int processedCharCount{ 0 };
66+
const double_conversion::StringToDoubleConverter converter(0, double_NaN, double_NaN, "inf", "nan");
67+
const auto conversionResult =
68+
converter.StringTo<TFloatingPoint>(str.c_str(), static_cast<int>(numberOfChars), &processedCharCount);
69+
70+
if (processedCharCount == static_cast<int>(numberOfChars) && !std::isnan(conversionResult))
71+
{
72+
return conversionResult;
73+
}
74+
}
75+
}
76+
itkGenericExceptionMacro("Failed to read a floating point component from the specified ASCII input file!"
77+
<< (str.empty() ? "" : (" Read characters: \"" + str + "\"")));
78+
}();
79+
}
80+
}
81+
82+
} // namespace
83+
84+
2685
// Constructor
2786
VTKPolyDataMeshIO ::VTKPolyDataMeshIO()
2887
{
@@ -1584,4 +1643,21 @@ VTKPolyDataMeshIO ::PrintSelf(std::ostream & os, Indent indent) const
15841643
os << indent << "cellTensorDataName : " << dataName << std::endl;
15851644
}
15861645
}
1646+
1647+
void
1648+
VTKPolyDataMeshIO::ReadComponentsAsASCII(std::ifstream & inputFile,
1649+
float * const buffer,
1650+
const SizeValueType numberOfComponents)
1651+
{
1652+
ReadFloatingPointsAsASCII(inputFile, buffer, numberOfComponents);
1653+
}
1654+
1655+
void
1656+
VTKPolyDataMeshIO::ReadComponentsAsASCII(std::ifstream & inputFile,
1657+
double * const buffer,
1658+
const SizeValueType numberOfComponents)
1659+
{
1660+
ReadFloatingPointsAsASCII(inputFile, buffer, numberOfComponents);
1661+
}
1662+
15871663
} // end of namespace itk

0 commit comments

Comments
 (0)