#ifndef _LIBSPIRV_UTIL_TEXT_HANDLER_H_
#define _LIBSPIRV_UTIL_TEXT_HANDLER_H_
-#include <libspirv/libspirv.h>
+#include <iomanip>
+#include <limits>
+#include <sstream>
+#include <type_traits>
#include <unordered_map>
+#include <libspirv/libspirv.h>
#include "diagnostic.h"
#include "instruction.h"
#include "operand.h"
// Tracks the relationship between the value and its type.
spv_result_t recordTypeIdForValue(uint32_t value, uint32_t type);
+ // Parses a numeric value of a given type from the given text. The number
+ // should take up the entire string, and should be within bounds for the
+ // target type. On success, returns SPV_SUCCESS and populates the object
+ // referenced by value_pointer. On failure, returns SPV_FAILED_MATCH if
+ // is_optional is true, and returns SPV_ERROR_INVALID_TEXT and emits a
+ // diagnostic otherwise.
+ template <typename T>
+ spv_result_t parseNumber(const char *text, bool is_optional, T *value_pointer,
+ const char *error_message_fragment) {
+ // C++11 doesn't define std::istringstream(int8_t&), so calling this method
+ // with a single-byte type leads to implementation-defined behaviour.
+ // Similarly for uint8_t.
+ static_assert(sizeof(T) > 1, "Don't use a single-byte type this parse method");
+
+ std::istringstream text_stream(text);
+ // Allow both decimal and hex input for integers.
+ // It also allows octal input, but we don't care about that case.
+ text_stream >> std::setbase(0);
+ text_stream >> *value_pointer;
+ bool ok = true;
+
+ // We should have read something.
+ ok = (text[0] != 0) && !text_stream.bad();
+ // It should have been all the text.
+ ok = ok && text_stream.eof();
+ // It should have been in range.
+ ok = ok && !text_stream.fail();
+ // Work around a bug in the GNU C++11 library. It will happily parse
+ // "-1" for uint16_t as 65535.
+ if (ok && !std::is_signed<T>::value && (text[0] == '-') &&
+ *value_pointer != 0) {
+ ok = false;
+ // Match expected error behaviour of std::istringstream::operator>>
+ // on failure to parse.
+ *value_pointer = 0;
+ }
+
+ if (ok) return SPV_SUCCESS;
+ if (is_optional) return SPV_FAILED_MATCH;
+ return diagnostic() << error_message_fragment << text;
+ }
+
private:
// Maps ID names to their corresponding numerical ids.
using spv_named_id_table = std::unordered_map<std::string, uint32_t>;
#include "TestFixture.h"
#include "UnitSPIRV.h"
#include <algorithm>
-#include <iomanip>
#include <utility>
#include <vector>
{"!0xff800001", 0xff800001}, // NaN
}));
+TEST(AssemblyContextParseNarrowSignedIntegers, Sample) {
+ AssemblyContext context(AutoText(""), nullptr);
+ int16_t i16;
+
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("", true, &i16, ""));
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0=", true, &i16, ""));
+
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0", true, &i16, ""));
+ EXPECT_EQ(0, i16);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("32767", true, &i16, ""));
+ EXPECT_EQ(32767, i16);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-32768", true, &i16, ""));
+ EXPECT_EQ(-32768, i16);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-0", true, &i16, ""));
+ EXPECT_EQ(0, i16);
+
+ // These are out of range, so they should return an error.
+ // The error code depends on whether this is an optional value.
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("32768", true, &i16, ""));
+ EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
+ context.parseNumber("65535", false, &i16, ""));
+
+ // Check hex parsing.
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0x7fff", true, &i16, ""));
+ EXPECT_EQ(32767, i16);
+ // This is out of range.
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0xffff", true, &i16, ""));
+}
+
+TEST(AssemblyContextParseNarrowUnsignedIntegers, Sample) {
+ AssemblyContext context(AutoText(""), nullptr);
+ uint16_t u16;
+
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("", true, &u16, ""));
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0=", true, &u16, ""));
+
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0", true, &u16, ""));
+ EXPECT_EQ(0, u16);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("65535", true, &u16, ""));
+ EXPECT_EQ(65535, u16);
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("65536", true, &u16, ""));
+
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-0", true, &u16, ""));
+ EXPECT_EQ(0, u16);
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("-1", true, &u16, ""));
+ EXPECT_EQ(0, u16);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0xffff", true, &u16, ""));
+ EXPECT_EQ(0xffff, u16);
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0x10000", true, &u16, ""));
+}
+
+TEST(AssemblyContextParseWideSignedIntegers, Sample) {
+ AssemblyContext context(AutoText(""), nullptr);
+ int64_t i64;
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("", true, &i64, ""));
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0=", true, &i64, ""));
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0", true, &i64, ""));
+ EXPECT_EQ(0, i64);
+ EXPECT_EQ(SPV_SUCCESS,
+ context.parseNumber("0x7fffffffffffffff", true, &i64, ""));
+ EXPECT_EQ(0x7fffffffffffffff, i64);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-0", true, &i64, ""));
+ EXPECT_EQ(0, i64);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-1", true, &i64, ""));
+ EXPECT_EQ(-1, i64);
+}
+
+TEST(AssemblyContextParseWideUnsignedIntegers, Sample) {
+ AssemblyContext context(AutoText(""), nullptr);
+ uint64_t u64;
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("", true, &u64, ""));
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0=", true, &u64, ""));
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0", true, &u64, ""));
+ EXPECT_EQ(0, u64);
+ EXPECT_EQ(SPV_SUCCESS,
+ context.parseNumber("0xffffffffffffffff", true, &u64, ""));
+ EXPECT_EQ(0xffffffffffffffffULL, u64);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-0", true, &u64, ""));
+ EXPECT_EQ(0, u64);
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("-1", true, &u64, ""));
+}
+
+TEST(AssemblyContextParseFloat, Sample) {
+ AssemblyContext context(AutoText(""), nullptr);
+ float f;
+
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("", true, &f, ""));
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0=", true, &f, ""));
+
+ // These values are exactly representatble.
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0", true, &f, ""));
+ EXPECT_EQ(0.0f, f);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("42", true, &f, ""));
+ EXPECT_EQ(42.0f, f);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("2.5", true, &f, ""));
+ EXPECT_EQ(2.5f, f);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-32.5", true, &f, ""));
+ EXPECT_EQ(-32.5f, f);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("1e38", true, &f, ""));
+ EXPECT_EQ(1e38f, f);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-1e38", true, &f, ""));
+ EXPECT_EQ(-1e38f, f);
+
+ // Out of range.
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("1e40", true, &f, ""));
+}
+
+TEST(AssemblyContextParseDouble, Sample) {
+ AssemblyContext context(AutoText(""), nullptr);
+ double f;
+
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("", true, &f, ""));
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0=", true, &f, ""));
+
+ // These values are exactly representatble.
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0", true, &f, ""));
+ EXPECT_EQ(0.0, f);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("42", true, &f, ""));
+ EXPECT_EQ(42.0, f);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("2.5", true, &f, ""));
+ EXPECT_EQ(2.5, f);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-32.5", true, &f, ""));
+ EXPECT_EQ(-32.5, f);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("1e38", true, &f, ""));
+ EXPECT_EQ(1e38, f);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-1e38", true, &f, ""));
+ EXPECT_EQ(-1e38, f);
+ // These are out of range for 32-bit float, but in range for 64-bit float.
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("1e40", true, &f, ""));
+ EXPECT_EQ(1e40, f);
+ EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-1e40", true, &f, ""));
+ EXPECT_EQ(-1e40, f);
+
+ // Out of range.
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("1e400", true, &f, ""));
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("-1e400", true, &f, ""));
+}
+
+TEST(AssemblyContextParseMessages, Errors) {
+ spv_diagnostic diag = nullptr;
+ AssemblyContext context(AutoText(""), &diag);
+ int16_t i16;
+
+ // No message is generated for a failure to parse an optional value.
+ EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("abc", true, &i16, "bad narrow int: "));
+ EXPECT_EQ(nullptr, diag);
+
+ // For a required value, use the message fragment.
+ EXPECT_EQ(SPV_ERROR_INVALID_TEXT, context.parseNumber("abc", false, &i16, "bad narrow int: "));
+ ASSERT_NE(nullptr, diag);
+ EXPECT_EQ("bad narrow int: abc", std::string(diag->error));
+ // Don't leak.
+ spvDiagnosticDestroy(diag);
+}
+
} // anonymous namespace