Add AssemblyContext::parseNumber

It parses a text string for a value of a given target type.
This commit is contained in:
David Neto 2015-10-13 15:51:12 -04:00
parent ac508b0d80
commit 6274120eb9
2 changed files with 202 additions and 2 deletions

View File

@ -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>;

View File

@ -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