mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2025-01-13 09:50:06 +00:00
Add AssemblyContext::parseNumber
It parses a text string for a value of a given target type.
This commit is contained in:
parent
ac508b0d80
commit
6274120eb9
@ -27,9 +27,13 @@
|
|||||||
#ifndef _LIBSPIRV_UTIL_TEXT_HANDLER_H_
|
#ifndef _LIBSPIRV_UTIL_TEXT_HANDLER_H_
|
||||||
#define _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 <unordered_map>
|
||||||
|
|
||||||
|
#include <libspirv/libspirv.h>
|
||||||
#include "diagnostic.h"
|
#include "diagnostic.h"
|
||||||
#include "instruction.h"
|
#include "instruction.h"
|
||||||
#include "operand.h"
|
#include "operand.h"
|
||||||
@ -231,6 +235,48 @@ class AssemblyContext {
|
|||||||
// Tracks the relationship between the value and its type.
|
// Tracks the relationship between the value and its type.
|
||||||
spv_result_t recordTypeIdForValue(uint32_t value, uint32_t 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:
|
private:
|
||||||
// Maps ID names to their corresponding numerical ids.
|
// Maps ID names to their corresponding numerical ids.
|
||||||
using spv_named_id_table = std::unordered_map<std::string, uint32_t>;
|
using spv_named_id_table = std::unordered_map<std::string, uint32_t>;
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
#include "TestFixture.h"
|
#include "TestFixture.h"
|
||||||
#include "UnitSPIRV.h"
|
#include "UnitSPIRV.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iomanip>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -451,4 +450,159 @@ INSTANTIATE_TEST_CASE_P(
|
|||||||
{"!0xff800001", 0xff800001}, // NaN
|
{"!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
|
} // anonymous namespace
|
||||||
|
Loading…
Reference in New Issue
Block a user