Index: include/llvm/Support/EndianStream.h =================================================================== --- include/llvm/Support/EndianStream.h +++ include/llvm/Support/EndianStream.h @@ -18,16 +18,46 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/Endian.h" #include "llvm/Support/raw_ostream.h" +#include namespace llvm { namespace support { namespace endian { -template -inline void write(raw_ostream &os, value_type value, endianness endian) { - value = byte_swap(value, endian); - os.write((const char *)&value, sizeof(value_type)); +namespace detail { + template + using enable_if_convertible = + typename std::enable_if::value || + std::is_convertible::value>; +} // end namespace detail + +template +inline typename detail::enable_if_convertible::type +write(raw_ostream &os, value_type value, endianness endian) { + constexpr bool wantsCheck = !std::is_same::value; + using actual_serialized_type = + typename std::conditional::type; + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wsign-compare" +#endif + // Disable -Wsign-compare on this check; too much existing code converts + // from signed to unsigned integers without thinking about it. + auto serializedValue = static_cast(value); + assert(serializedValue == value && "value was truncated"); +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + + serializedValue = byte_swap(serializedValue, endian); + os.write((const char *)&serializedValue, sizeof(actual_serialized_type)); +} + +template <> +inline void write(raw_ostream &os, float value, endianness endian) { + write(os, FloatToBits(value), endian); } template <> @@ -36,11 +66,27 @@ } template <> -inline void write(raw_ostream &os, double value, - endianness endian) { +inline void write(raw_ostream &os, float value, endianness endian) { + write(os, DoubleToBits(static_cast(value)), endian); +} + +template <> +inline void write(raw_ostream &os, double value, endianness endian) { + write(os, DoubleToBits(value), endian); +} + +template <> +inline void write(raw_ostream &os, double value, endianness endian) { write(os, DoubleToBits(value), endian); } +template <> +inline void write(raw_ostream &os, double value, endianness endian) { + assert(std::isinf(value) == std::isinf(static_cast(value)) && + "magnitude of double value is too large to be stored as a float"); + write(os, FloatToBits(static_cast(value)), endian); +} + template inline void write(raw_ostream &os, ArrayRef vals, endianness endian) { @@ -53,11 +99,15 @@ raw_ostream &OS; endianness Endian; Writer(raw_ostream &OS, endianness Endian) : OS(OS), Endian(Endian) {} + template void write(ArrayRef Val) { endian::write(OS, Val, Endian); } - template void write(value_type Val) { - endian::write(OS, Val, Endian); + + template + typename detail::enable_if_convertible::type + write(value_type Val) { + endian::write(OS, Val, Endian); } }; Index: unittests/Support/EndianStreamTest.cpp =================================================================== --- unittests/Support/EndianStreamTest.cpp +++ unittests/Support/EndianStreamTest.cpp @@ -204,5 +204,52 @@ EXPECT_EQ(static_cast(Data[7]), 0x46); } +#ifndef NDEBUG + +TEST(EndianStream, AssertOnTruncation_uint8) { + SmallString<16> Data; + raw_svector_ostream OS(Data); + endian::Writer LE(OS, little); + LE.write(255); + EXPECT_DEATH({ LE.write(256); }, "value was truncated"); +} + +TEST(EndianStream, AssertOnTruncation_int8_Positive) { + SmallString<16> Data; + raw_svector_ostream OS(Data); + endian::Writer LE(OS, little); + LE.write(127); + EXPECT_DEATH({ LE.write(128); }, "value was truncated"); +} + +TEST(EndianStream, AssertOnTruncation_int8_Negative) { + SmallString<16> Data; + raw_svector_ostream OS(Data); + endian::Writer LE(OS, little); + LE.write(-128); + EXPECT_DEATH({ LE.write(-129); }, "value was truncated"); +} + +TEST(EndianStream, AssertOnFloatOverflow_Positive) { + SmallString<16> Data; + raw_svector_ostream OS(Data); + endian::Writer LE(OS, little); + LE.write(std::numeric_limits::infinity()); + LE.write(std::numeric_limits::min()); + EXPECT_DEATH({ LE.write(std::numeric_limits::max()); }, + "magnitude of double value is too large to be stored as a float"); +} + +TEST(EndianStream, AssertOnFloatOverflow_Negative) { + SmallString<16> Data; + raw_svector_ostream OS(Data); + endian::Writer LE(OS, little); + LE.write(-std::numeric_limits::infinity()); + LE.write(-std::numeric_limits::min()); + EXPECT_DEATH({ LE.write(-std::numeric_limits::max()); }, + "magnitude of double value is too large to be stored as a float"); +} + +#endif // NDEBUG } // end anon namespace