namespace gmx
{
+
+namespace
+{
+
+//! Returns true if any of the argument values is smaller than zero.
+template<typename Container>
+bool anySmallerZero(Container values)
+{
+ return std::any_of(std::begin(values), std::end(values), [](auto v) { return v < 0; });
+}
+
+//! Returns true if any of the argument values is larger than a given boundary value.
+template<typename Container>
+bool anyLargerThanValue(Container values, typename Container::value_type boundaryValue)
+{
+ return std::any_of(std::begin(values), std::end(values),
+ [boundaryValue](auto v) { return v > boundaryValue; });
+}
+
+} // namespace
+
size_t numberOfExpectedDataItems(const MrcDensityMapHeader& header)
{
- if (std::any_of(std::begin(header.numColumnRowSection_), std::end(header.numColumnRowSection_),
- [](auto i) { return i < 0; }))
+ if (anySmallerZero(header.numColumnRowSection_))
{
GMX_THROW(
InternalError("Cannot determine data size, because the mrc "
header.numColumnRowSection_[XX] };
};
+bool mrcHeaderIsSane(const MrcDensityMapHeader& header)
+{
+ // Make sure all numbers of columns, row sections, extents and cell angles
+ // are positive
+ if (anySmallerZero(header.numColumnRowSection_) || anySmallerZero(header.cellAngles_)
+ || anySmallerZero(header.extent_))
+ {
+ return false;
+ }
+
+ // The maximum integer number in an mrc header to be considered sane
+ constexpr std::int32_t c_maxIntegerNumber = 100'000;
+ if (anyLargerThanValue(header.numColumnRowSection_, c_maxIntegerNumber)
+ || anyLargerThanValue(header.extent_, c_maxIntegerNumber))
+ {
+ return false;
+ }
+
+ constexpr std::int32_t c_maxCellAngle = 360;
+ if (anyLargerThanValue(header.cellAngles_, c_maxCellAngle))
+ {
+ return false; //NOLINT(readability-simplify-boolean-expr)
+ }
+
+ return true;
+}
+
} // namespace gmx
* \returns density data extents in three dimensions.
*/
dynamicExtents3D getDynamicExtents3D(const MrcDensityMapHeader& header);
+
+/*! \brief Checks if the values in the header are sane.
+ *
+ * Checks extents and numbers of columns, rows and sections, as well as unit
+ * cell angles for positivity and to be within bounds.
+ *
+ * Bounds are set generously not to hamper future creative uses of mrc files.
+ *
+ * \returns true if all header values are within resonable albeit generous bounds
+ */
+bool mrcHeaderIsSane(const MrcDensityMapHeader& header);
+
} // namespace gmx
#endif /* end of include guard: GMX_FILEIO_MRCDENSITYMAPHEADER_H */
EXPECT_EQ(expectedExtents[ZZ], extents.extent(ZZ));
}
+TEST(MrcDensityMapHeaderTest, IsSane)
+{
+ MrcDensityMapHeader header;
+ EXPECT_TRUE(mrcHeaderIsSane(header));
+
+ header.numColumnRowSection_[YY] = -1;
+ EXPECT_FALSE(mrcHeaderIsSane(header));
+
+ header.numColumnRowSection_[YY] = 10'000'000;
+ EXPECT_FALSE(mrcHeaderIsSane(header));
+
+ header = {};
+ EXPECT_TRUE(mrcHeaderIsSane(header));
+
+ header.cellAngles_[XX] = -20;
+ EXPECT_FALSE(mrcHeaderIsSane(header));
+}
+
} // namespace gmx