mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-25 17:21: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_
|
||||
#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"
|
||||
@ -231,6 +235,48 @@ class AssemblyContext {
|
||||
// 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>;
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include "TestFixture.h"
|
||||
#include "UnitSPIRV.h"
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@ -451,4 +450,159 @@ INSTANTIATE_TEST_CASE_P(
|
||||
{"!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
|
||||
|
Loading…
Reference in New Issue
Block a user