From 1773b95737a78bbe12f4011511d90da9d605808f Mon Sep 17 00:00:00 2001 From: qining Date: Thu, 1 Sep 2016 14:27:04 -0400 Subject: [PATCH] Pull out the number parsing logic Pull out the number parsing logic from AssemblyContext::binaryEncodeNumericLiteral() to utilities. The new utility function: `ParseAndEncodeNumber()` now accepts: * number text to parse * number type * a emit function, which is a function which will be called with each parsed uint32 word. * a pointer to std::string to be overwritten with error messages. (pass nullptr if expect no error message) and returns: * an enum result type to indicate the status Type/Structs moved to utility: * template class ClampToZeroIfUnsignedType New type: * enum EncodeNumberStatus: success or error code * NumberType: hold the number type information for the number to be parsed. * several helper functions are also added for NumberType. Functions moved to utility: * Helpers: * template checkRangeAndIfHexThenSignExtend() -> CheckRangeAndIfHex....() * Interfaces: * template parseNumber() -> ParseNumber() * binaryEncodeIntegerLiteral() -> ParseAndEncodeIntegerNumber() * binaryEncodeFloatingPointLiteral() -> ParseAndEncodeFloatingPointNumber() * binaryEncodeNumericLiteral() -> ParseAndEncodeNumber() Tests added/moved to test/ParseNumber.cpp, including tests for: * ParseNumber(): This is moved from TextToBinary.cpp to ParseNumber.cpp * ParseAndEncodeIntegerNumber(): New added * ParseAndEncodeFloatingPointNumber(): New added * ParseAndEncodeNumber(): New added Note that the error messages are kept almost the same as before, but they may be inappropriate for an utility function. Those will be fixed in another CL. --- source/CMakeLists.txt | 2 + source/text.cpp | 9 +- source/text_handler.cpp | 245 ++------- source/text_handler.h | 60 --- source/util/hex_float.h | 1 + source/util/parse_number.cpp | 199 ++++++++ source/util/parse_number.h | 249 +++++++++ test/CMakeLists.txt | 5 + test/ParseNumber.cpp | 943 +++++++++++++++++++++++++++++++++++ test/TextToBinary.cpp | 284 ----------- 10 files changed, 1455 insertions(+), 542 deletions(-) create mode 100644 source/util/parse_number.cpp create mode 100644 source/util/parse_number.h create mode 100644 test/ParseNumber.cpp diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index ec3c23e61..a045ce1a7 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -115,6 +115,7 @@ set(SPIRV_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/util/bitutils.h ${CMAKE_CURRENT_SOURCE_DIR}/util/hex_float.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.h ${CMAKE_CURRENT_SOURCE_DIR}/assembly_grammar.h ${CMAKE_CURRENT_SOURCE_DIR}/binary.h ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.h @@ -135,6 +136,7 @@ set(SPIRV_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/text_handler.h ${CMAKE_CURRENT_SOURCE_DIR}/validate.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.cpp ${CMAKE_CURRENT_SOURCE_DIR}/assembly_grammar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/binary.cpp ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.cpp diff --git a/source/text.cpp b/source/text.cpp index dc3519e77..d317264b2 100644 --- a/source/text.cpp +++ b/source/text.cpp @@ -38,6 +38,7 @@ #include "table.h" #include "text_handler.h" #include "util/bitutils.h" +#include "util/parse_number.h" bool spvIsValidIDCharacter(const char value) { return value == '_' || 0 != ::isalnum(value); @@ -159,10 +160,10 @@ spv_result_t encodeImmediate(libspirv::AssemblyContext* context, const char* text, spv_instruction_t* pInst) { assert(*text == '!'); uint32_t parse_result; - if (auto error = - context->parseNumber(text + 1, SPV_ERROR_INVALID_TEXT, &parse_result, - "Invalid immediate integer: !")) - return error; + if (!spvutils::ParseNumber(text + 1, &parse_result)) { + return context->diagnostic(SPV_ERROR_INVALID_TEXT) + << "Invalid immediate integer: !" << text + 1; + } context->binaryEncodeU32(parse_result, pInst); context->seekForward(static_cast(strlen(text))); return SPV_SUCCESS; diff --git a/source/text_handler.cpp b/source/text_handler.cpp index 65c5e0d26..8724b1cd0 100644 --- a/source/text_handler.cpp +++ b/source/text_handler.cpp @@ -27,13 +27,9 @@ #include "text.h" #include "util/bitutils.h" #include "util/hex_float.h" +#include "util/parse_number.h" namespace { - -using spvutils::BitwiseCast; -using spvutils::FloatProxy; -using spvutils::HexFloat; - // Advances |text| to the start of the next line and writes the new position to // |position|. spv_result_t advanceLine(spv_text text, spv_position position) { @@ -217,37 +213,61 @@ spv_result_t AssemblyContext::binaryEncodeU32(const uint32_t value, return SPV_SUCCESS; } -spv_result_t AssemblyContext::binaryEncodeU64(const uint64_t value, - spv_instruction_t* pInst) { - uint32_t low = uint32_t(0x00000000ffffffff & value); - uint32_t high = uint32_t((0xffffffff00000000 & value) >> 32); - binaryEncodeU32(low, pInst); - binaryEncodeU32(high, pInst); - return SPV_SUCCESS; -} - spv_result_t AssemblyContext::binaryEncodeNumericLiteral( const char* val, spv_result_t error_code, const IdType& type, spv_instruction_t* pInst) { - const bool is_bottom = type.type_class == libspirv::IdTypeClass::kBottom; - const bool is_floating = libspirv::isScalarFloating(type); - const bool is_integer = libspirv::isScalarIntegral(type); - - if (!is_bottom && !is_floating && !is_integer) { - return diagnostic(SPV_ERROR_INTERNAL) - << "The expected type is not a scalar integer or float type"; + using spvutils::EncodeNumberStatus; + // Populate the NumberType from the IdType for parsing. + spvutils::NumberType number_type; + switch (type.type_class) { + case IdTypeClass::kOtherType: + return diagnostic(SPV_ERROR_INTERNAL) + << "Unexpected numeric literal type"; + case IdTypeClass::kScalarIntegerType: + if (type.isSigned) { + number_type = {type.bitwidth, SPV_NUMBER_SIGNED_INT}; + } else { + number_type = {type.bitwidth, SPV_NUMBER_UNSIGNED_INT}; + } + break; + case IdTypeClass::kScalarFloatType: + number_type = {type.bitwidth, SPV_NUMBER_FLOATING}; + break; + case IdTypeClass::kBottom: + // kBottom means the type is unknown and we need to infer the type before + // parsing the number. The rule is: If there is a decimal point, treat + // the value as a floating point value, otherwise a integer value, then + // if the first char of the integer text is '-', treat the integer as a + // signed integer, otherwise an unsigned integer. + uint32_t bitwidth = static_cast(assumedBitWidth(type)); + if (strchr(val, '.')) { + number_type = {bitwidth, SPV_NUMBER_FLOATING}; + } else if (type.isSigned || val[0] == '-') { + number_type = {bitwidth, SPV_NUMBER_SIGNED_INT}; + } else { + number_type = {bitwidth, SPV_NUMBER_UNSIGNED_INT}; + } + break; } - // If this is bottom, but looks like a float, we should treat it like a - // float. - const bool looks_like_float = is_bottom && strchr(val, '.'); - - // If we explicitly expect a floating-point number, we should handle that - // first. - if (is_floating || looks_like_float) - return binaryEncodeFloatingPointLiteral(val, error_code, type, pInst); - - return binaryEncodeIntegerLiteral(val, error_code, type, pInst); + std::string error_msg; + EncodeNumberStatus parse_status = ParseAndEncodeNumber( + val, number_type, + [this, pInst](uint32_t d) { this->binaryEncodeU32(d, pInst); }, + &error_msg); + switch (parse_status) { + case EncodeNumberStatus::kSuccess: + return SPV_SUCCESS; + case EncodeNumberStatus::kInvalidText: + return diagnostic(error_code) << error_msg; + case EncodeNumberStatus::kUnsupported: + return diagnostic(SPV_ERROR_INTERNAL) << error_msg; + case EncodeNumberStatus::kInvalidUsage: + return diagnostic(SPV_ERROR_INVALID_TEXT) << error_msg; + } + // This line is not reachable, only added to satisfy the compiler. + return diagnostic(SPV_ERROR_INTERNAL) + << "Unexpected result code from ParseAndEncodeNumber()"; } spv_result_t AssemblyContext::binaryEncodeString(const char* value, @@ -342,167 +362,4 @@ spv_ext_inst_type_t AssemblyContext::getExtInstTypeForId(uint32_t id) const { return std::get<1>(*type); } -spv_result_t AssemblyContext::binaryEncodeFloatingPointLiteral( - const char* val, spv_result_t error_code, const IdType& type, - spv_instruction_t* pInst) { - const auto bit_width = assumedBitWidth(type); - switch (bit_width) { - case 16: { - spvutils::HexFloat> hVal(0); - if (auto error = parseNumber(val, error_code, &hVal, - "Invalid 16-bit float literal: ")) - return error; - // getAsFloat will return the spvutils::Float16 value, and get_value - // will return a uint16_t representing the bits of the float. - // The encoding is therefore correct from the perspective of the SPIR-V - // spec since the top 16 bits will be 0. - return binaryEncodeU32( - static_cast(hVal.value().getAsFloat().get_value()), pInst); - } break; - case 32: { - spvutils::HexFloat> fVal(0.0f); - if (auto error = parseNumber(val, error_code, &fVal, - "Invalid 32-bit float literal: ")) - return error; - return binaryEncodeU32(BitwiseCast(fVal), pInst); - } break; - case 64: { - spvutils::HexFloat> dVal(0.0); - if (auto error = parseNumber(val, error_code, &dVal, - "Invalid 64-bit float literal: ")) - return error; - return binaryEncodeU64(BitwiseCast(dVal), pInst); - } break; - default: - break; - } - return diagnostic() << "Unsupported " << bit_width << "-bit float literals"; -} - -// Returns SPV_SUCCESS if the given value fits within the target scalar -// integral type. The target type may have an unusual bit width. -// If the value was originally specified as a hexadecimal number, then -// the overflow bits should be zero. If it was hex and the target type is -// signed, then return the sign-extended value through the -// updated_value_for_hex pointer argument. -// On failure, return the given error code and emit a diagnostic if that error -// code is not SPV_FAILED_MATCH. -template -spv_result_t checkRangeAndIfHexThenSignExtend(T value, spv_result_t error_code, - const IdType& type, bool is_hex, - T* updated_value_for_hex) { - // The encoded result has three regions of bits that are of interest, from - // least to most significant: - // - magnitude bits, where the magnitude of the number would be stored if - // we were using a signed-magnitude representation. - // - an optional sign bit - // - overflow bits, up to bit 63 of a 64-bit number - // For example: - // Type Overflow Sign Magnitude - // --------------- -------- ---- --------- - // unsigned 8 bit 8-63 n/a 0-7 - // signed 8 bit 8-63 7 0-6 - // unsigned 16 bit 16-63 n/a 0-15 - // signed 16 bit 16-63 15 0-14 - - // We'll use masks to define the three regions. - // At first we'll assume the number is unsigned. - const uint32_t bit_width = assumedBitWidth(type); - uint64_t magnitude_mask = - (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1); - uint64_t sign_mask = 0; - uint64_t overflow_mask = ~magnitude_mask; - - if (value < 0 || type.isSigned) { - // Accommodate the sign bit. - magnitude_mask >>= 1; - sign_mask = magnitude_mask + 1; - } - - bool failed = false; - if (value < 0) { - // The top bits must all be 1 for a negative signed value. - failed = ((value & overflow_mask) != overflow_mask) || - ((value & sign_mask) != sign_mask); - } else { - if (is_hex) { - // Hex values are a bit special. They decode as unsigned values, but - // may represent a negative number. In this case, the overflow bits - // should be zero. - failed = (value & overflow_mask) != 0; - } else { - const uint64_t value_as_u64 = static_cast(value); - // Check overflow in the ordinary case. - failed = (value_as_u64 & magnitude_mask) != value_as_u64; - } - } - - if (failed) { - return error_code; - } - - // Sign extend hex the number. - if (is_hex && (value & sign_mask)) - *updated_value_for_hex = (value | overflow_mask); - - return SPV_SUCCESS; -} - -spv_result_t AssemblyContext::binaryEncodeIntegerLiteral( - const char* val, spv_result_t error_code, const IdType& type, - spv_instruction_t* pInst) { - const bool is_bottom = type.type_class == libspirv::IdTypeClass::kBottom; - const uint32_t bit_width = assumedBitWidth(type); - - if (bit_width > 64) - return diagnostic(SPV_ERROR_INTERNAL) << "Unsupported " << bit_width - << "-bit integer literals"; - - // Either we are expecting anything or integer. - bool is_negative = val[0] == '-'; - bool can_be_signed = is_bottom || type.isSigned; - - if (is_negative && !can_be_signed) { - return diagnostic() - << "Cannot put a negative number in an unsigned literal"; - } - - const bool is_hex = val[0] == '0' && (val[1] == 'x' || val[1] == 'X'); - - uint64_t decoded_bits; - if (is_negative) { - int64_t decoded_signed = 0; - - if (auto error = parseNumber(val, error_code, &decoded_signed, - "Invalid signed integer literal: ")) - return error; - if (auto error = checkRangeAndIfHexThenSignExtend( - decoded_signed, error_code, type, is_hex, &decoded_signed)) { - diagnostic(error_code) - << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase - << decoded_signed << " does not fit in a " << std::dec << bit_width - << "-bit " << (type.isSigned ? "signed" : "unsigned") << " integer"; - return error; - } - decoded_bits = decoded_signed; - } else { - // There's no leading minus sign, so parse it as an unsigned integer. - if (auto error = parseNumber(val, error_code, &decoded_bits, - "Invalid unsigned integer literal: ")) - return error; - if (auto error = checkRangeAndIfHexThenSignExtend( - decoded_bits, error_code, type, is_hex, &decoded_bits)) { - diagnostic(error_code) - << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase - << decoded_bits << " does not fit in a " << std::dec << bit_width - << "-bit " << (type.isSigned ? "signed" : "unsigned") << " integer"; - return error; - } - } - if (bit_width > 32) { - return binaryEncodeU64(decoded_bits, pInst); - } else { - return binaryEncodeU32(uint32_t(decoded_bits), pInst); - } -} } // namespace libspirv diff --git a/source/text_handler.h b/source/text_handler.h index 521d51444..9951643b8 100644 --- a/source/text_handler.h +++ b/source/text_handler.h @@ -226,68 +226,8 @@ class AssemblyContext { // id is not the id for an extended instruction type. spv_ext_inst_type_t getExtInstTypeForId(uint32_t id) const; - // 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 the given error code, - // and emits a diagnostic if that error code is not SPV_FAILED_MATCH. - template - spv_result_t parseNumber(const char* text, spv_result_t error_code, - 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; - - // We should have read something. - bool 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 && text[0] == '-') - ok = !ClampToZeroIfUnsignedType::Clamp(value_pointer); - - if (ok) return SPV_SUCCESS; - return diagnostic(error_code) << error_message_fragment << text; - } - private: - // Appends the given floating point literal to the given instruction. - // Returns SPV_SUCCESS if the value was correctly parsed. Otherwise - // returns the given error code, and emits a diagnostic if that error - // code is not SPV_FAILED_MATCH. - // Only 32 and 64 bit floating point numbers are supported. - spv_result_t binaryEncodeFloatingPointLiteral(const char* numeric_literal, - spv_result_t error_code, - const IdType& type, - spv_instruction_t* pInst); - - // Appends the given integer literal to the given instruction. - // Returns SPV_SUCCESS if the value was correctly parsed. Otherwise - // returns the given error code, and emits a diagnostic if that error - // code is not SPV_FAILED_MATCH. - // Integers up to 64 bits are supported. - spv_result_t binaryEncodeIntegerLiteral(const char* numeric_literal, - spv_result_t error_code, - const IdType& type, - spv_instruction_t* pInst); - - // Writes the given 64-bit literal value into the instruction. - // return SPV_SUCCESS if the value could be written in the instruction. - spv_result_t binaryEncodeU64(const uint64_t value, spv_instruction_t* pInst); // Maps ID names to their corresponding numerical ids. using spv_named_id_table = std::unordered_map; // Maps type-defining IDs to their IdType. diff --git a/source/util/hex_float.h b/source/util/hex_float.h index d94d900bd..ac7e00278 100644 --- a/source/util/hex_float.h +++ b/source/util/hex_float.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "bitutils.h" diff --git a/source/util/parse_number.cpp b/source/util/parse_number.cpp new file mode 100644 index 000000000..bba754537 --- /dev/null +++ b/source/util/parse_number.cpp @@ -0,0 +1,199 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util/parse_number.h" + +#include +#include +#include +#include +#include +#include + +#include "util/hex_float.h" + +namespace spvutils { + +namespace { +// A helper class that temporarily stores error messages and dump the messages +// to a string which given as as pointer when it is destructed. If the given +// pointer is a nullptr, this class does not store error message. +class ErrorMsgStream { + public: + explicit ErrorMsgStream(std::string* error_msg_sink) + : error_msg_sink_(error_msg_sink) { + if (error_msg_sink_) stream_.reset(new std::ostringstream()); + } + ~ErrorMsgStream() { + if (error_msg_sink_ && stream_) *error_msg_sink_ = stream_->str(); + } + template + ErrorMsgStream& operator<<(T val) { + if (stream_) *stream_ << val; + return *this; + } + + private: + std::unique_ptr stream_; + // The destination string to which this class dump the error message when + // destructor is called. + std::string* error_msg_sink_; +}; +} + +EncodeNumberStatus ParseAndEncodeIntegerNumber( + const char* text, const NumberType& type, + std::function emit, std::string* error_msg) { + if (!IsIntegral(type)) { + ErrorMsgStream(error_msg) << "The expected type is not a integer type"; + return EncodeNumberStatus::kInvalidUsage; + } + + const uint32_t bit_width = AssumedBitWidth(type); + + if (bit_width > 64) { + ErrorMsgStream(error_msg) << "Unsupported " << bit_width + << "-bit integer literals"; + return EncodeNumberStatus::kUnsupported; + } + + // Either we are expecting anything or integer. + bool is_negative = text[0] == '-'; + bool can_be_signed = IsSigned(type); + + if (is_negative && !can_be_signed) { + ErrorMsgStream(error_msg) + << "Cannot put a negative number in an unsigned literal"; + return EncodeNumberStatus::kInvalidUsage; + } + + const bool is_hex = text[0] == '0' && (text[1] == 'x' || text[1] == 'X'); + + uint64_t decoded_bits; + if (is_negative) { + int64_t decoded_signed = 0; + + if (!ParseNumber(text, &decoded_signed)) { + ErrorMsgStream(error_msg) << "Invalid signed integer literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + + if (!CheckRangeAndIfHexThenSignExtend(decoded_signed, type, is_hex, + &decoded_signed)) { + ErrorMsgStream(error_msg) + << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase + << decoded_signed << " does not fit in a " << std::dec << bit_width + << "-bit " << (IsSigned(type) ? "signed" : "unsigned") << " integer"; + return EncodeNumberStatus::kInvalidText; + } + decoded_bits = decoded_signed; + } else { + // There's no leading minus sign, so parse it as an unsigned integer. + if (!ParseNumber(text, &decoded_bits)) { + ErrorMsgStream(error_msg) << "Invalid unsigned integer literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + if (!CheckRangeAndIfHexThenSignExtend(decoded_bits, type, is_hex, + &decoded_bits)) { + ErrorMsgStream(error_msg) + << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase + << decoded_bits << " does not fit in a " << std::dec << bit_width + << "-bit " << (IsSigned(type) ? "signed" : "unsigned") << " integer"; + return EncodeNumberStatus::kInvalidText; + } + } + if (bit_width > 32) { + uint32_t low = uint32_t(0x00000000ffffffff & decoded_bits); + uint32_t high = uint32_t((0xffffffff00000000 & decoded_bits) >> 32); + emit(low); + emit(high); + } else { + emit(uint32_t(decoded_bits)); + } + return EncodeNumberStatus::kSuccess; +} + +EncodeNumberStatus ParseAndEncodeFloatingPointNumber( + const char* text, const NumberType& type, + std::function emit, std::string* error_msg) { + if (!IsFloating(type)) { + ErrorMsgStream(error_msg) << "The expected type is not a float type"; + return EncodeNumberStatus::kInvalidUsage; + } + + const auto bit_width = AssumedBitWidth(type); + switch (bit_width) { + case 16: { + HexFloat> hVal(0); + if (!ParseNumber(text, &hVal)) { + ErrorMsgStream(error_msg) << "Invalid 16-bit float literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + // getAsFloat will return the spvutils::Float16 value, and get_value + // will return a uint16_t representing the bits of the float. + // The encoding is therefore correct from the perspective of the SPIR-V + // spec since the top 16 bits will be 0. + emit(static_cast(hVal.value().getAsFloat().get_value())); + return EncodeNumberStatus::kSuccess; + } break; + case 32: { + HexFloat> fVal(0.0f); + if (!ParseNumber(text, &fVal)) { + ErrorMsgStream(error_msg) << "Invalid 32-bit float literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + emit(BitwiseCast(fVal)); + return EncodeNumberStatus::kSuccess; + } break; + case 64: { + HexFloat> dVal(0.0); + if (!ParseNumber(text, &dVal)) { + ErrorMsgStream(error_msg) << "Invalid 64-bit float literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + uint64_t decoded_val = BitwiseCast(dVal); + uint32_t low = uint32_t(0x00000000ffffffff & decoded_val); + uint32_t high = uint32_t((0xffffffff00000000 & decoded_val) >> 32); + emit(low); + emit(high); + return EncodeNumberStatus::kSuccess; + } break; + default: + break; + } + ErrorMsgStream(error_msg) << "Unsupported " << bit_width + << "-bit float literals"; + return EncodeNumberStatus::kUnsupported; +} + +EncodeNumberStatus ParseAndEncodeNumber(const char* text, + const NumberType& type, + std::function emit, + std::string* error_msg) { + if (IsUnknown(type)) { + ErrorMsgStream(error_msg) + << "The expected type is not a integer or float type"; + return EncodeNumberStatus::kInvalidUsage; + } + + // If we explicitly expect a floating-point number, we should handle that + // first. + if (IsFloating(type)) { + return ParseAndEncodeFloatingPointNumber(text, type, emit, error_msg); + } + + return ParseAndEncodeIntegerNumber(text, type, emit, error_msg); +} + +} // namespace spvutils diff --git a/source/util/parse_number.h b/source/util/parse_number.h new file mode 100644 index 000000000..6b9903aa6 --- /dev/null +++ b/source/util/parse_number.h @@ -0,0 +1,249 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_UTIL_PARSE_NUMBER_H_ +#define LIBSPIRV_UTIL_PARSE_NUMBER_H_ + +#include +#include +#include + +#include "spirv-tools/libspirv.h" +#include "util/hex_float.h" + +namespace spvutils { + +// A struct to hold the expected type information for the number in text to be +// parsed. +struct NumberType { + uint32_t bitwidth; + // SPV_NUMBER_NONE means the type is unknown and is invalid to be used with + // ParseAndEncode{|Integer|Floating}Number(). + spv_number_kind_t kind; +}; + +// Returns true if the type is a scalar integer type. +inline bool IsIntegral(const NumberType& type) { + return type.kind == SPV_NUMBER_UNSIGNED_INT || + type.kind == SPV_NUMBER_SIGNED_INT; +} + +// Returns true if the type is a scalar floating point type. +inline bool IsFloating(const NumberType& type) { + return type.kind == SPV_NUMBER_FLOATING; +} + +// Returns true if the type is a signed value. +inline bool IsSigned(const NumberType& type) { + return type.kind == SPV_NUMBER_FLOATING || type.kind == SPV_NUMBER_SIGNED_INT; +} + +// Returns true if the type is unknown. +inline bool IsUnknown(const NumberType& type) { + return type.kind == SPV_NUMBER_NONE; +} + +// Returns the number of bits in the type. This is only valid for integer and +// floating types. +inline int AssumedBitWidth(const NumberType& type) { + switch (type.kind) { + case SPV_NUMBER_SIGNED_INT: + case SPV_NUMBER_UNSIGNED_INT: + case SPV_NUMBER_FLOATING: + return type.bitwidth; + default: + break; + } + // We don't care about this case. + return 0; +} + +// A templated class with a static member function Clamp, where Clamp sets a +// referenced value of type T to 0 if T is an unsigned integer type, and +// returns true if it modified the referenced value. +template +class ClampToZeroIfUnsignedType { + public: + // The default specialization does not clamp the value. + static bool Clamp(T*) { return false; } +}; + +// The specialization of ClampToZeroIfUnsignedType for unsigned integer types. +template +class ClampToZeroIfUnsignedType< + T, typename std::enable_if::value>::type> { + public: + static bool Clamp(T* value_pointer) { + if (*value_pointer) { + *value_pointer = 0; + return true; + } + return false; + } +}; + +// Returns true if the given value fits within the target scalar integral type. +// The target type may have an unusual bit width. If the value was originally +// specified as a hexadecimal number, then the overflow bits should be zero. +// If it was hex and the target type is signed, then return the sign-extended +// value through the updated_value_for_hex pointer argument. On failure, +// returns false. +template +bool CheckRangeAndIfHexThenSignExtend(T value, const NumberType& type, + bool is_hex, T* updated_value_for_hex) { + // The encoded result has three regions of bits that are of interest, from + // least to most significant: + // - magnitude bits, where the magnitude of the number would be stored if + // we were using a signed-magnitude representation. + // - an optional sign bit + // - overflow bits, up to bit 63 of a 64-bit number + // For example: + // Type Overflow Sign Magnitude + // --------------- -------- ---- --------- + // unsigned 8 bit 8-63 n/a 0-7 + // signed 8 bit 8-63 7 0-6 + // unsigned 16 bit 16-63 n/a 0-15 + // signed 16 bit 16-63 15 0-14 + + // We'll use masks to define the three regions. + // At first we'll assume the number is unsigned. + const uint32_t bit_width = AssumedBitWidth(type); + uint64_t magnitude_mask = + (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1); + uint64_t sign_mask = 0; + uint64_t overflow_mask = ~magnitude_mask; + + if (value < 0 || IsSigned(type)) { + // Accommodate the sign bit. + magnitude_mask >>= 1; + sign_mask = magnitude_mask + 1; + } + + bool failed = false; + if (value < 0) { + // The top bits must all be 1 for a negative signed value. + failed = ((value & overflow_mask) != overflow_mask) || + ((value & sign_mask) != sign_mask); + } else { + if (is_hex) { + // Hex values are a bit special. They decode as unsigned values, but may + // represent a negative number. In this case, the overflow bits should + // be zero. + failed = (value & overflow_mask) != 0; + } else { + const uint64_t value_as_u64 = static_cast(value); + // Check overflow in the ordinary case. + failed = (value_as_u64 & magnitude_mask) != value_as_u64; + } + } + + if (failed) { + return false; + } + + // Sign extend hex the number. + if (is_hex && (value & sign_mask)) + *updated_value_for_hex = (value | overflow_mask); + + return true; +} + +// 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 true and populates the object referenced by +// value_pointer. On failure, returns false. +template +bool ParseNumber(const char* text, T* value_pointer) { + // 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; + + // We should have read something. + bool 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 && text[0] == '-') + ok = !ClampToZeroIfUnsignedType::Clamp(value_pointer); + + return ok; +} + +// Enum to indicate the parsing and encoding status. +enum class EncodeNumberStatus { + kSuccess = 0, + // Unsupported bit width etc. + kUnsupported, + // Expected type (NumberType) is not a scalar int or float, or putting a + // negative number in an unsigned literal. + kInvalidUsage, + // Number value does not fit the bit width of the expected type etc. + kInvalidText, +}; + +// Parses an integer value of a given |type| from the given |text| and encodes +// the number by the given |emit| function. On success, returns +// EncodeNumberStatus::kSuccess and the parsed number will be consumed by the +// given |emit| function word by word (least significant word first). On +// failure, this function returns the error code of the encoding status and +// |emit| function will not be called. If the string pointer |error_msg| is not +// a nullptr, it will be overwritten with error messages in case of failure. In +// case of success, |error_msg| will not be touched. Integers up to 64 bits are +// supported. +EncodeNumberStatus ParseAndEncodeIntegerNumber( + const char* text, const NumberType& type, + std::function emit, std::string* error_msg); + +// Parses a floating point value of a given |type| from the given |text| and +// encodes the number by the given |emit| funciton. On success, returns +// EncodeNumberStatus::kSuccess and the parsed number will be consumed by the +// given |emit| function word by word (least significant word first). On +// failure, this function returns the error code of the encoding status and +// |emit| function will not be called. If the string pointer |error_msg| is not +// a nullptr, it will be overwritten with error messages in case of failure. In +// case of success, |error_msg| will not be touched. Only 16, 32 and 64 bit +// floating point numbers are supported. +EncodeNumberStatus ParseAndEncodeFloatingPointNumber( + const char* text, const NumberType& type, + std::function emit, std::string* error_msg); + +// Parses an integer or floating point number of a given |type| from the given +// |text| and encodes the number by the given |emit| function. On success, +// returns EncodeNumberStatus::kSuccess and the parsed number will be consumed +// by the given |emit| function word by word (least significant word first). On +// failure, this function returns the error code of the encoding status and +// |emit| function will not be called. If the string pointer |error_msg| is not +// a nullptr, it will be overwritten with error messages in case of failure. In +// case of success, |error_msg| will not be touched. Integers up to 64 bits +// and 16/32/64 bit floating point values are supported. +EncodeNumberStatus ParseAndEncodeNumber(const char* text, + const NumberType& type, + std::function emit, + std::string* error_msg); + +} // namespace spvutils + +#endif // LIBSPIRV_UTIL_PARSE_NUMBER_H_ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 10012b4a6..b585a266d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -134,5 +134,10 @@ add_spvtools_unittest( SRCS cpp_interface.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS}) +add_spvtools_unittest( + TARGET parse_number + SRCS ${CMAKE_CURRENT_SOURCE_DIR}/ParseNumber.cpp + LIBS ${SPIRV_TOOLS}) + add_subdirectory(opt) add_subdirectory(val) diff --git a/test/ParseNumber.cpp b/test/ParseNumber.cpp new file mode 100644 index 000000000..42f3b2a8a --- /dev/null +++ b/test/ParseNumber.cpp @@ -0,0 +1,943 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "source/util/parse_number.h" +#include "spirv-tools/libspirv.h" + +namespace { +using spvutils::NumberType; +using spvutils::EncodeNumberStatus; +using spvutils::ParseNumber; +using spvutils::ParseAndEncodeIntegerNumber; +using spvutils::ParseAndEncodeFloatingPointNumber; +using spvutils::ParseAndEncodeNumber; +using testing::Eq; +using testing::IsNull; +using testing::NotNull; + +TEST(ParseNarrowSignedIntegers, Sample) { + int16_t i16; + + EXPECT_FALSE(ParseNumber("", &i16)); + EXPECT_FALSE(ParseNumber("0=", &i16)); + + EXPECT_TRUE(ParseNumber("0", &i16)); + EXPECT_EQ(0, i16); + EXPECT_TRUE(ParseNumber("32767", &i16)); + EXPECT_EQ(32767, i16); + EXPECT_TRUE(ParseNumber("-32768", &i16)); + EXPECT_EQ(-32768, i16); + EXPECT_TRUE(ParseNumber("-0", &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_FALSE(ParseNumber("32768", &i16)); + EXPECT_FALSE(ParseNumber("65535", &i16)); + + // Check hex parsing. + EXPECT_TRUE(ParseNumber("0x7fff", &i16)); + EXPECT_EQ(32767, i16); + // This is out of range. + EXPECT_FALSE(ParseNumber("0xffff", &i16)); +} + +TEST(ParseNarrowUnsignedIntegers, Sample) { + uint16_t u16; + + EXPECT_FALSE(ParseNumber("", &u16)); + EXPECT_FALSE(ParseNumber("0=", &u16)); + + EXPECT_TRUE(ParseNumber("0", &u16)); + EXPECT_EQ(0, u16); + EXPECT_TRUE(ParseNumber("65535", &u16)); + EXPECT_EQ(65535, u16); + EXPECT_FALSE(ParseNumber("65536", &u16)); + + // We don't care about -0 since it's rejected at a higher level. + EXPECT_FALSE(ParseNumber("-1", &u16)); + EXPECT_TRUE(ParseNumber("0xffff", &u16)); + EXPECT_EQ(0xffff, u16); + EXPECT_FALSE(ParseNumber("0x10000", &u16)); +} + +TEST(ParseSignedIntegers, Sample) { + int32_t i32; + + // Invalid parse. + EXPECT_FALSE(ParseNumber("", &i32)); + EXPECT_FALSE(ParseNumber("0=", &i32)); + + // Decimal values. + EXPECT_TRUE(ParseNumber("0", &i32)); + EXPECT_EQ(0, i32); + EXPECT_TRUE(ParseNumber("2147483647", &i32)); + EXPECT_EQ(std::numeric_limits::max(), i32); + EXPECT_FALSE(ParseNumber("2147483648", &i32)); + EXPECT_TRUE(ParseNumber("-0", &i32)); + EXPECT_EQ(0, i32); + EXPECT_TRUE(ParseNumber("-1", &i32)); + EXPECT_EQ(-1, i32); + EXPECT_TRUE(ParseNumber("-2147483648", &i32)); + EXPECT_EQ(std::numeric_limits::min(), i32); + + // Hex values. + EXPECT_TRUE(ParseNumber("0x7fffffff", &i32)); + EXPECT_EQ(std::numeric_limits::max(), i32); + EXPECT_FALSE(ParseNumber("0x80000000", &i32)); + EXPECT_TRUE(ParseNumber("-0x000", &i32)); + EXPECT_EQ(0, i32); + EXPECT_TRUE(ParseNumber("-0x001", &i32)); + EXPECT_EQ(-1, i32); + EXPECT_TRUE(ParseNumber("-0x80000000", &i32)); + EXPECT_EQ(std::numeric_limits::min(), i32); +} + +TEST(ParseUnsignedIntegers, Sample) { + uint32_t u32; + + // Invalid parse. + EXPECT_FALSE(ParseNumber("", &u32)); + EXPECT_FALSE(ParseNumber("0=", &u32)); + + // Valid values. + EXPECT_TRUE(ParseNumber("0", &u32)); + EXPECT_EQ(0u, u32); + EXPECT_TRUE(ParseNumber("4294967295", &u32)); + EXPECT_EQ(std::numeric_limits::max(), u32); + EXPECT_FALSE(ParseNumber("4294967296", &u32)); + + // Hex values. + EXPECT_TRUE(ParseNumber("0xffffffff", &u32)); + EXPECT_EQ(std::numeric_limits::max(), u32); + + // We don't care about -0 since it's rejected at a higher level. + EXPECT_FALSE(ParseNumber("-1", &u32)); +} + +TEST(ParseWideSignedIntegers, Sample) { + int64_t i64; + EXPECT_FALSE(ParseNumber("", &i64)); + EXPECT_FALSE(ParseNumber("0=", &i64)); + EXPECT_TRUE(ParseNumber("0", &i64)); + EXPECT_EQ(0, i64); + EXPECT_TRUE(ParseNumber("0x7fffffffffffffff", &i64)); + EXPECT_EQ(0x7fffffffffffffff, i64); + EXPECT_TRUE(ParseNumber("-0", &i64)); + EXPECT_EQ(0, i64); + EXPECT_TRUE(ParseNumber("-1", &i64)); + EXPECT_EQ(-1, i64); +} + +TEST(ParseWideUnsignedIntegers, Sample) { + uint64_t u64; + EXPECT_FALSE(ParseNumber("", &u64)); + EXPECT_FALSE(ParseNumber("0=", &u64)); + EXPECT_TRUE(ParseNumber("0", &u64)); + EXPECT_EQ(0u, u64); + EXPECT_TRUE(ParseNumber("0xffffffffffffffff", &u64)); + EXPECT_EQ(0xffffffffffffffffULL, u64); + + // We don't care about -0 since it's rejected at a higher level. + EXPECT_FALSE(ParseNumber("-1", &u64)); +} + +TEST(ParseFloat, Sample) { + float f; + + EXPECT_FALSE(ParseNumber("", &f)); + EXPECT_FALSE(ParseNumber("0=", &f)); + + // These values are exactly representatble. + EXPECT_TRUE(ParseNumber("0", &f)); + EXPECT_EQ(0.0f, f); + EXPECT_TRUE(ParseNumber("42", &f)); + EXPECT_EQ(42.0f, f); + EXPECT_TRUE(ParseNumber("2.5", &f)); + EXPECT_EQ(2.5f, f); + EXPECT_TRUE(ParseNumber("-32.5", &f)); + EXPECT_EQ(-32.5f, f); + EXPECT_TRUE(ParseNumber("1e38", &f)); + EXPECT_EQ(1e38f, f); + EXPECT_TRUE(ParseNumber("-1e38", &f)); + EXPECT_EQ(-1e38f, f); +} + +TEST(ParseFloat, Overflow) { + // The assembler parses using HexFloat>. Make + // sure that succeeds for in-range values, and fails for out of + // range values. When it does overflow, the value is set to the + // nearest finite value, matching C++11 behavior for operator>> + // on floating point. + spvutils::HexFloat> f(0.0f); + + EXPECT_TRUE(ParseNumber("1e38", &f)); + EXPECT_EQ(1e38f, f.value().getAsFloat()); + EXPECT_TRUE(ParseNumber("-1e38", &f)); + EXPECT_EQ(-1e38f, f.value().getAsFloat()); + EXPECT_FALSE(ParseNumber("1e40", &f)); + EXPECT_FALSE(ParseNumber("-1e40", &f)); + EXPECT_FALSE(ParseNumber("1e400", &f)); + EXPECT_FALSE(ParseNumber("-1e400", &f)); +} + +TEST(ParseDouble, Sample) { + double f; + + EXPECT_FALSE(ParseNumber("", &f)); + EXPECT_FALSE(ParseNumber("0=", &f)); + + // These values are exactly representatble. + EXPECT_TRUE(ParseNumber("0", &f)); + EXPECT_EQ(0.0, f); + EXPECT_TRUE(ParseNumber("42", &f)); + EXPECT_EQ(42.0, f); + EXPECT_TRUE(ParseNumber("2.5", &f)); + EXPECT_EQ(2.5, f); + EXPECT_TRUE(ParseNumber("-32.5", &f)); + EXPECT_EQ(-32.5, f); + EXPECT_TRUE(ParseNumber("1e38", &f)); + EXPECT_EQ(1e38, f); + EXPECT_TRUE(ParseNumber("-1e38", &f)); + EXPECT_EQ(-1e38, f); + // These are out of range for 32-bit float, but in range for 64-bit float. + EXPECT_TRUE(ParseNumber("1e40", &f)); + EXPECT_EQ(1e40, f); + EXPECT_TRUE(ParseNumber("-1e40", &f)); + EXPECT_EQ(-1e40, f); +} + +TEST(ParseDouble, Overflow) { + // The assembler parses using HexFloat>. Make + // sure that succeeds for in-range values, and fails for out of + // range values. When it does overflow, the value is set to the + // nearest finite value, matching C++11 behavior for operator>> + // on floating point. + spvutils::HexFloat> f(0.0); + + EXPECT_TRUE(ParseNumber("1e38", &f)); + EXPECT_EQ(1e38, f.value().getAsFloat()); + EXPECT_TRUE(ParseNumber("-1e38", &f)); + EXPECT_EQ(-1e38, f.value().getAsFloat()); + EXPECT_TRUE(ParseNumber("1e40", &f)); + EXPECT_EQ(1e40, f.value().getAsFloat()); + EXPECT_TRUE(ParseNumber("-1e40", &f)); + EXPECT_EQ(-1e40, f.value().getAsFloat()); + EXPECT_FALSE(ParseNumber("1e400", &f)); + EXPECT_FALSE(ParseNumber("-1e400", &f)); +} + +TEST(ParseFloat16, Overflow) { + // The assembler parses using HexFloat>. Make + // sure that succeeds for in-range values, and fails for out of + // range values. When it does overflow, the value is set to the + // nearest finite value, matching C++11 behavior for operator>> + // on floating point. + spvutils::HexFloat> f(0); + + EXPECT_TRUE(ParseNumber("-0.0", &f)); + EXPECT_EQ(uint16_t{0x8000}, f.value().getAsFloat().get_value()); + EXPECT_TRUE(ParseNumber("1.0", &f)); + EXPECT_EQ(uint16_t{0x3c00}, f.value().getAsFloat().get_value()); + + // Overflows 16-bit but not 32-bit + EXPECT_FALSE(ParseNumber("1e38", &f)); + EXPECT_FALSE(ParseNumber("-1e38", &f)); + + // Overflows 32-bit but not 64-bit + EXPECT_FALSE(ParseNumber("1e40", &f)); + EXPECT_FALSE(ParseNumber("-1e40", &f)); + + // Overflows 64-bit + EXPECT_FALSE(ParseNumber("1e400", &f)); + EXPECT_FALSE(ParseNumber("-1e400", &f)); +} + +void AssertEmitFunc(uint32_t) { + ASSERT_FALSE(true) + << "Should not call emit() function when the number can not be parsed."; + return; +} + +TEST(ParseAndEncodeNarrowSignedIntegers, Invalid) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {16, SPV_NUMBER_SIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: ", err_msg); + rc = ParseAndEncodeIntegerNumber("=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: =", err_msg); + rc = ParseAndEncodeIntegerNumber("-", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid signed integer literal: -", err_msg); + rc = ParseAndEncodeIntegerNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: 0=", err_msg); +} + +TEST(ParseAndEncodeNarrowSignedIntegers, Overflow) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {16, SPV_NUMBER_SIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber("32768", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Integer 32768 does not fit in a 16-bit signed integer", err_msg); + rc = ParseAndEncodeIntegerNumber("-32769", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Integer -32769 does not fit in a 16-bit signed integer", err_msg); +} + +TEST(ParseAndEncodeNarrowSignedIntegers, Success) { + // Don't care the error message in this case. + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + NumberType type = {16, SPV_NUMBER_SIGNED_INT}; + + // Zero, maximum, and minimum value + rc = ParseAndEncodeIntegerNumber( + "0", type, [](uint32_t word) { EXPECT_EQ(0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "-0", type, [](uint32_t word) { EXPECT_EQ(0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "32767", type, [](uint32_t word) { EXPECT_EQ(0x00007fffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "-32768", type, [](uint32_t word) { EXPECT_EQ(0xffff8000u, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Hex parsing + rc = ParseAndEncodeIntegerNumber( + "0x7fff", type, [](uint32_t word) { EXPECT_EQ(0x00007fffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "0xffff", type, [](uint32_t word) { EXPECT_EQ(0xffffffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); +} + +TEST(ParseAndEncodeNarrowUnsignedIntegers, Invalid) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {16, SPV_NUMBER_UNSIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: ", err_msg); + rc = ParseAndEncodeIntegerNumber("=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: =", err_msg); + rc = ParseAndEncodeIntegerNumber("-", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); + rc = ParseAndEncodeIntegerNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: 0=", err_msg); + rc = ParseAndEncodeIntegerNumber("-0", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); + rc = ParseAndEncodeIntegerNumber("-1", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); +} + +TEST(ParseAndEncodeNarrowUnsignedIntegers, Overflow) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg("random content"); + NumberType type = {16, SPV_NUMBER_UNSIGNED_INT}; + + // Overflow + rc = ParseAndEncodeIntegerNumber("65536", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Integer 65536 does not fit in a 16-bit unsigned integer", err_msg); +} + +TEST(ParseAndEncodeNarrowUnsignedIntegers, Success) { + // Don't care the error message in this case. + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + NumberType type = {16, SPV_NUMBER_UNSIGNED_INT}; + + // Zero, maximum, and minimum value + rc = ParseAndEncodeIntegerNumber( + "0", type, [](uint32_t word) { EXPECT_EQ(0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "65535", type, [](uint32_t word) { EXPECT_EQ(0x0000ffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Hex parsing + rc = ParseAndEncodeIntegerNumber( + "0xffff", type, [](uint32_t word) { EXPECT_EQ(0x0000ffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); +} + +TEST(ParseAndEncodeSignedIntegers, Invalid) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {32, SPV_NUMBER_SIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: ", err_msg); + rc = ParseAndEncodeIntegerNumber("=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: =", err_msg); + rc = ParseAndEncodeIntegerNumber("-", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid signed integer literal: -", err_msg); + rc = ParseAndEncodeIntegerNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: 0=", err_msg); +} + +TEST(ParseAndEncodeSignedIntegers, Overflow) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {32, SPV_NUMBER_SIGNED_INT}; + + rc = + ParseAndEncodeIntegerNumber("2147483648", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Integer 2147483648 does not fit in a 32-bit signed integer", + err_msg); + rc = ParseAndEncodeIntegerNumber("-2147483649", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Integer -2147483649 does not fit in a 32-bit signed integer", + err_msg); +} + +TEST(ParseAndEncodeSignedIntegers, Success) { + // Don't care the error message in this case. + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + NumberType type = {32, SPV_NUMBER_SIGNED_INT}; + + // Zero, maximum, and minimum value + rc = ParseAndEncodeIntegerNumber( + "0", type, [](uint32_t word) { EXPECT_EQ(0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "-0", type, [](uint32_t word) { EXPECT_EQ(0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "2147483647", type, [](uint32_t word) { EXPECT_EQ(0x7fffffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "-2147483648", type, [](uint32_t word) { EXPECT_EQ(0x80000000u, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Hex parsing + rc = ParseAndEncodeIntegerNumber( + "0x7fffffff", type, [](uint32_t word) { EXPECT_EQ(0x7fffffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "0xffffffff", type, [](uint32_t word) { EXPECT_EQ(0xffffffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); +} + +TEST(ParseAndEncodeUnsignedIntegers, Invalid) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {32, SPV_NUMBER_UNSIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: ", err_msg); + rc = ParseAndEncodeIntegerNumber("=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: =", err_msg); + rc = ParseAndEncodeIntegerNumber("-", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); + rc = ParseAndEncodeIntegerNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: 0=", err_msg); + rc = ParseAndEncodeIntegerNumber("-0", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); + rc = ParseAndEncodeIntegerNumber("-1", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); +} + +TEST(ParseAndEncodeUnsignedIntegers, Overflow) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg("random content"); + NumberType type = {32, SPV_NUMBER_UNSIGNED_INT}; + + // Overflow + rc = + ParseAndEncodeIntegerNumber("4294967296", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Integer 4294967296 does not fit in a 32-bit unsigned integer", + err_msg); +} + +TEST(ParseAndEncodeUnsignedIntegers, Success) { + // Don't care the error message in this case. + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + NumberType type = {32, SPV_NUMBER_UNSIGNED_INT}; + + // Zero, maximum, and minimum value + rc = ParseAndEncodeIntegerNumber( + "0", type, [](uint32_t word) { EXPECT_EQ(0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "4294967295", type, [](uint32_t word) { EXPECT_EQ(0xffffffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Hex parsing + rc = ParseAndEncodeIntegerNumber( + "0xffffffff", type, [](uint32_t word) { EXPECT_EQ(0xffffffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); +} + +TEST(ParseAndEncodeWideSignedIntegers, Invalid) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {64, SPV_NUMBER_SIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: ", err_msg); + rc = ParseAndEncodeIntegerNumber("=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: =", err_msg); + rc = ParseAndEncodeIntegerNumber("-", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid signed integer literal: -", err_msg); + rc = ParseAndEncodeIntegerNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: 0=", err_msg); +} + +TEST(ParseAndEncodeWideSignedIntegers, Overflow) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {64, SPV_NUMBER_SIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber("9223372036854775808", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ( + "Integer 9223372036854775808 does not fit in a 64-bit signed integer", + err_msg); + rc = ParseAndEncodeIntegerNumber("-9223372036854775809", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid signed integer literal: -9223372036854775809", err_msg); +} + +TEST(ParseAndEncodeWideSignedIntegers, Success) { + // Don't care the error message in this case. + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + NumberType type = {64, SPV_NUMBER_SIGNED_INT}; + std::vector word_buffer; + auto emit = [&word_buffer](uint32_t word) { + if (word_buffer.size() == 2) word_buffer.clear(); + word_buffer.push_back(word); + }; + + // Zero, maximum, and minimum value + rc = ParseAndEncodeIntegerNumber("0", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0u})); + rc = ParseAndEncodeIntegerNumber("-0", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0u})); + rc = ParseAndEncodeIntegerNumber("9223372036854775807", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xffffffffu, 0x7fffffffu})); + rc = ParseAndEncodeIntegerNumber("-9223372036854775808", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0x80000000u})); + rc = ParseAndEncodeIntegerNumber("-1", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xffffffffu, 0xffffffffu})); + + // Hex parsing + rc = ParseAndEncodeIntegerNumber("0x7fffffffffffffff", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xffffffffu, 0x7fffffffu})); + rc = ParseAndEncodeIntegerNumber("0xffffffffffffffff", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xffffffffu, 0xffffffffu})); +} + +TEST(ParseAndEncodeWideUnsignedIntegers, Invalid) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {64, SPV_NUMBER_UNSIGNED_INT}; + + // Invalid + rc = ParseAndEncodeIntegerNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: ", err_msg); + rc = ParseAndEncodeIntegerNumber("=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: =", err_msg); + rc = ParseAndEncodeIntegerNumber("-", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); + rc = ParseAndEncodeIntegerNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: 0=", err_msg); + rc = ParseAndEncodeIntegerNumber("-0", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); + rc = ParseAndEncodeIntegerNumber("-1", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); +} + +TEST(ParseAndEncodeWideUnsignedIntegers, Overflow) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {64, SPV_NUMBER_UNSIGNED_INT}; + + // Overflow + rc = ParseAndEncodeIntegerNumber("18446744073709551616", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: 18446744073709551616", err_msg); +} + +TEST(ParseAndEncodeWideUnsignedIntegers, Success) { + // Don't care the error message in this case. + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + NumberType type = {64, SPV_NUMBER_UNSIGNED_INT}; + std::vector word_buffer; + auto emit = [&word_buffer](uint32_t word) { + if (word_buffer.size() == 2) word_buffer.clear(); + word_buffer.push_back(word); + }; + + // Zero, maximum, and minimum value + rc = ParseAndEncodeIntegerNumber("0", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0u})); + rc = ParseAndEncodeIntegerNumber("18446744073709551615", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xffffffffu, 0xffffffffu})); + + // Hex parsing + rc = ParseAndEncodeIntegerNumber("0xffffffffffffffff", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xffffffffu, 0xffffffffu})); +} + +TEST(ParseAndEncodeIntegerNumber, TypeNone) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {32, SPV_NUMBER_NONE}; + + rc = ParseAndEncodeIntegerNumber( + "0.0", type, [](uint32_t word) { EXPECT_EQ(0x0u, word); }, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("The expected type is not a integer type", err_msg); +} + +TEST(ParseAndEncodeIntegerNumber, InvalidCaseWithoutErrorMessageString) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + NumberType type = {32, SPV_NUMBER_SIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber("invalid", type, AssertEmitFunc, nullptr); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); +} + +TEST(ParseAndEncodeIntegerNumber, DoNotTouchErrorMessageStringOnSuccess) { + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + std::string err_msg("random content"); + NumberType type = {32, SPV_NUMBER_SIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber( + "100", type, [](uint32_t word) { EXPECT_EQ(100u, word); }, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_EQ("random content", err_msg); +} + +TEST(ParseAndEncodeFloat, Sample) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {32, SPV_NUMBER_FLOATING}; + + // Invalid + rc = ParseAndEncodeFloatingPointNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 32-bit float literal: ", err_msg); + rc = ParseAndEncodeFloatingPointNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 32-bit float literal: 0=", err_msg); + + // Representative samples + rc = ParseAndEncodeFloatingPointNumber( + "0.0", type, [](uint32_t word) { EXPECT_EQ(0x0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "-0.0", type, [](uint32_t word) { EXPECT_EQ(0x80000000u, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "42", type, [](uint32_t word) { EXPECT_EQ(0x42280000u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "2.5", type, [](uint32_t word) { EXPECT_EQ(0x40200000u, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "-32.5", type, [](uint32_t word) { EXPECT_EQ(0xc2020000u, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "1e38", type, [](uint32_t word) { EXPECT_EQ(0x7e967699u, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "-1e38", type, [](uint32_t word) { EXPECT_EQ(0xfe967699u, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Overflow + rc = + ParseAndEncodeFloatingPointNumber("1e40", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 32-bit float literal: 1e40", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e40", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 32-bit float literal: -1e40", err_msg); + rc = ParseAndEncodeFloatingPointNumber("1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 32-bit float literal: 1e400", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 32-bit float literal: -1e400", err_msg); +} + +TEST(ParseAndEncodeDouble, Sample) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {64, SPV_NUMBER_FLOATING}; + std::vector word_buffer; + auto emit = [&word_buffer](uint32_t word) { + if (word_buffer.size() == 2) word_buffer.clear(); + word_buffer.push_back(word); + }; + + // Invalid + rc = ParseAndEncodeFloatingPointNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 64-bit float literal: ", err_msg); + rc = ParseAndEncodeFloatingPointNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 64-bit float literal: 0=", err_msg); + + // Representative samples + rc = ParseAndEncodeFloatingPointNumber("0.0", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0u})); + rc = ParseAndEncodeFloatingPointNumber("-0.0", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0x80000000u})); + rc = ParseAndEncodeFloatingPointNumber("42", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0x40450000u})); + rc = ParseAndEncodeFloatingPointNumber("2.5", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0x40040000u})); + rc = ParseAndEncodeFloatingPointNumber("32.5", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0x40404000u})); + rc = ParseAndEncodeFloatingPointNumber("1e38", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0x2a16a1b1u, 0x47d2ced3u})); + rc = ParseAndEncodeFloatingPointNumber("-1e38", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0x2a16a1b1u, 0xc7d2ced3u})); + rc = ParseAndEncodeFloatingPointNumber("1e40", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xf1c35ca5u, 0x483d6329u})); + rc = ParseAndEncodeFloatingPointNumber("-1e40", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xf1c35ca5u, 0xc83d6329u})); + + // Overflow + rc = ParseAndEncodeFloatingPointNumber("1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 64-bit float literal: 1e400", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 64-bit float literal: -1e400", err_msg); +} + +TEST(ParseAndEncodeFloat16, Sample) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {16, SPV_NUMBER_FLOATING}; + + // Invalid + rc = ParseAndEncodeFloatingPointNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: ", err_msg); + rc = ParseAndEncodeFloatingPointNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: 0=", err_msg); + + // Representative samples + rc = ParseAndEncodeFloatingPointNumber( + "0.0", type, [](uint32_t word) { EXPECT_EQ(0x0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "-0.0", type, [](uint32_t word) { EXPECT_EQ(0x8000u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "1.0", type, [](uint32_t word) { EXPECT_EQ(0x3c00u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "2.5", type, [](uint32_t word) { EXPECT_EQ(0x4100u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "32.5", type, [](uint32_t word) { EXPECT_EQ(0x5010u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Overflow + rc = + ParseAndEncodeFloatingPointNumber("1e38", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: 1e38", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e38", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: -1e38", err_msg); + rc = + ParseAndEncodeFloatingPointNumber("1e40", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: 1e40", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e40", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: -1e40", err_msg); + rc = ParseAndEncodeFloatingPointNumber("1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: 1e400", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: -1e400", err_msg); +} + +TEST(ParseAndEncodeFloatingPointNumber, TypeNone) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {32, SPV_NUMBER_NONE}; + + rc = ParseAndEncodeFloatingPointNumber( + "0.0", type, [](uint32_t word) { EXPECT_EQ(0x0u, word); }, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("The expected type is not a float type", err_msg); +} + +TEST(ParseAndEncodeFloatingPointNumber, InvalidCaseWithoutErrorMessageString) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + NumberType type = {32, SPV_NUMBER_FLOATING}; + + rc = ParseAndEncodeFloatingPointNumber("invalid", type, AssertEmitFunc, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); +} + +TEST(ParseAndEncodeFloatingPointNumber, DoNotTouchErrorMessageStringOnSuccess) { + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + std::string err_msg("random content"); + NumberType type = {32, SPV_NUMBER_FLOATING}; + + rc = ParseAndEncodeFloatingPointNumber( + "0.0", type, [](uint32_t word) { EXPECT_EQ(0x0u, word); }, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_EQ("random content", err_msg); +} + +TEST(ParseAndEncodeNumber, Sample) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {32, SPV_NUMBER_SIGNED_INT}; + + // Invalid with error message string + rc = ParseAndEncodeNumber("something wrong", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: something wrong", err_msg); + + // Invalid without error message string + rc = ParseAndEncodeNumber("something wrong", type, AssertEmitFunc, nullptr); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + + // Signed integer, should not touch the error message string. + err_msg = "random content"; + rc = ParseAndEncodeNumber("-1", type, + [](uint32_t word) { EXPECT_EQ(0xffffffffu, word); }, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_EQ("random content", err_msg); + + // Unsigned integer + type = {32, SPV_NUMBER_UNSIGNED_INT}; + rc = ParseAndEncodeNumber( + "1", type, [](uint32_t word) { EXPECT_EQ(1u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Float + type = {32, SPV_NUMBER_FLOATING}; + rc = ParseAndEncodeNumber("-1.0", type, + [](uint32_t word) { EXPECT_EQ(0xbf800000, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); +} + +} // anonymous namespace diff --git a/test/TextToBinary.cpp b/test/TextToBinary.cpp index 0e1cae399..0a1439acb 100644 --- a/test/TextToBinary.cpp +++ b/test/TextToBinary.cpp @@ -256,290 +256,6 @@ INSTANTIATE_TEST_CASE_P( {"0x1.804p4", 0x00004e01}, }), ); -TEST(AssemblyContextParseNarrowSignedIntegers, Sample) { - AssemblyContext context(AutoText(""), nullptr); - const spv_result_t ec = SPV_FAILED_MATCH; - int16_t i16; - - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("", ec, &i16, "")); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0=", ec, &i16, "")); - - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0", ec, &i16, "")); - EXPECT_EQ(0, i16); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("32767", ec, &i16, "")); - EXPECT_EQ(32767, i16); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-32768", ec, &i16, "")); - EXPECT_EQ(-32768, i16); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-0", ec, &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", ec, &i16, "")); - EXPECT_EQ(SPV_ERROR_INVALID_TEXT, - context.parseNumber("65535", SPV_ERROR_INVALID_TEXT, &i16, "")); - - // Check hex parsing. - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0x7fff", ec, &i16, "")); - EXPECT_EQ(32767, i16); - // This is out of range. - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0xffff", ec, &i16, "")); -} - -TEST(AssemblyContextParseNarrowUnsignedIntegers, Sample) { - AssemblyContext context(AutoText(""), nullptr); - const spv_result_t ec = SPV_FAILED_MATCH; - uint16_t u16; - - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("", ec, &u16, "")); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0=", ec, &u16, "")); - - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0", ec, &u16, "")); - EXPECT_EQ(0, u16); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("65535", ec, &u16, "")); - EXPECT_EQ(65535, u16); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("65536", ec, &u16, "")); - - // We don't care about -0 since it's rejected at a higher level. - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("-1", ec, &u16, "")); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0xffff", ec, &u16, "")); - EXPECT_EQ(0xffff, u16); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0x10000", ec, &u16, "")); -} - -TEST(AssemblyContextParseSignedIntegers, Sample) { - AssemblyContext context(AutoText(""), nullptr); - const spv_result_t ec = SPV_FAILED_MATCH; - int32_t i32; - - // Invalid parse. - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("", ec, &i32, "")); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0=", ec, &i32, "")); - - // Decimal values. - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0", ec, &i32, "")); - EXPECT_EQ(0, i32); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("2147483647", ec, &i32, "")); - EXPECT_EQ(std::numeric_limits::max(), i32); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("2147483648", ec, &i32, "")); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-0", ec, &i32, "")); - EXPECT_EQ(0, i32); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-1", ec, &i32, "")); - EXPECT_EQ(-1, i32); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-2147483648", ec, &i32, "")); - EXPECT_EQ(std::numeric_limits::min(), i32); - - // Hex values. - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0x7fffffff", ec, &i32, "")); - EXPECT_EQ(std::numeric_limits::max(), i32); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0x80000000", ec, &i32, "")); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-0x000", ec, &i32, "")); - EXPECT_EQ(0, i32); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-0x001", ec, &i32, "")); - EXPECT_EQ(-1, i32); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-0x80000000", ec, &i32, "")); - EXPECT_EQ(std::numeric_limits::min(), i32); -} - -TEST(AssemblyContextParseUnsignedIntegers, Sample) { - AssemblyContext context(AutoText(""), nullptr); - const spv_result_t ec = SPV_FAILED_MATCH; - uint32_t u32; - - // Invalid parse. - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("", ec, &u32, "")); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0=", ec, &u32, "")); - - // Valid values. - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0", ec, &u32, "")); - EXPECT_EQ(0u, u32); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("4294967295", ec, &u32, "")); - EXPECT_EQ(std::numeric_limits::max(), u32); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("4294967296", ec, &u32, "")); - - // Hex values. - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0xffffffff", ec, &u32, "")); - EXPECT_EQ(std::numeric_limits::max(), u32); - - // We don't care about -0 since it's rejected at a higher level. - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("-1", ec, &u32, "")); -} - -TEST(AssemblyContextParseWideSignedIntegers, Sample) { - AssemblyContext context(AutoText(""), nullptr); - const spv_result_t ec = SPV_FAILED_MATCH; - int64_t i64; - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("", ec, &i64, "")); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0=", ec, &i64, "")); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0", ec, &i64, "")); - EXPECT_EQ(0, i64); - EXPECT_EQ(SPV_SUCCESS, - context.parseNumber("0x7fffffffffffffff", ec, &i64, "")); - EXPECT_EQ(0x7fffffffffffffff, i64); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-0", ec, &i64, "")); - EXPECT_EQ(0, i64); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-1", ec, &i64, "")); - EXPECT_EQ(-1, i64); -} - -TEST(AssemblyContextParseWideUnsignedIntegers, Sample) { - AssemblyContext context(AutoText(""), nullptr); - const spv_result_t ec = SPV_FAILED_MATCH; - uint64_t u64; - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("", ec, &u64, "")); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0=", ec, &u64, "")); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0", ec, &u64, "")); - EXPECT_EQ(0u, u64); - EXPECT_EQ(SPV_SUCCESS, - context.parseNumber("0xffffffffffffffff", ec, &u64, "")); - EXPECT_EQ(0xffffffffffffffffULL, u64); - - // We don't care about -0 since it's rejected at a higher level. - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("-1", ec, &u64, "")); -} - -TEST(AssemblyContextParseFloat, Sample) { - AssemblyContext context(AutoText(""), nullptr); - const spv_result_t ec = SPV_FAILED_MATCH; - float f; - - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("", ec, &f, "")); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0=", ec, &f, "")); - - // These values are exactly representatble. - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0", ec, &f, "")); - EXPECT_EQ(0.0f, f); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("42", ec, &f, "")); - EXPECT_EQ(42.0f, f); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("2.5", ec, &f, "")); - EXPECT_EQ(2.5f, f); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-32.5", ec, &f, "")); - EXPECT_EQ(-32.5f, f); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("1e38", ec, &f, "")); - EXPECT_EQ(1e38f, f); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-1e38", ec, &f, "")); - EXPECT_EQ(-1e38f, f); -} - -TEST(AssemblyContextParseFloat, Overflow) { - // The assembler parses using HexFloat>. Make - // sure that succeeds for in-range values, and fails for out of - // range values. When it does overflow, the value is set to the - // nearest finite value, matching C++11 behavior for operator>> - // on floating point. - AssemblyContext context(AutoText(""), nullptr); - const spv_result_t ec = SPV_FAILED_MATCH; - spvutils::HexFloat> f(0.0f); - - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("1e38", ec, &f, "")); - EXPECT_EQ(1e38f, f.value().getAsFloat()); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-1e38", ec, &f, "")); - EXPECT_EQ(-1e38f, f.value().getAsFloat()); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("1e40", ec, &f, "")); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("-1e40", ec, &f, "")); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("1e400", ec, &f, "")); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("-1e400", ec, &f, "")); -} - -TEST(AssemblyContextParseDouble, Sample) { - AssemblyContext context(AutoText(""), nullptr); - const spv_result_t ec = SPV_FAILED_MATCH; - double f; - - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("", ec, &f, "")); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("0=", ec, &f, "")); - - // These values are exactly representatble. - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("0", ec, &f, "")); - EXPECT_EQ(0.0, f); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("42", ec, &f, "")); - EXPECT_EQ(42.0, f); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("2.5", ec, &f, "")); - EXPECT_EQ(2.5, f); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-32.5", ec, &f, "")); - EXPECT_EQ(-32.5, f); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("1e38", ec, &f, "")); - EXPECT_EQ(1e38, f); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-1e38", ec, &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", ec, &f, "")); - EXPECT_EQ(1e40, f); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-1e40", ec, &f, "")); - EXPECT_EQ(-1e40, f); -} - -TEST(AssemblyContextParseDouble, Overflow) { - // The assembler parses using HexFloat>. Make - // sure that succeeds for in-range values, and fails for out of - // range values. When it does overflow, the value is set to the - // nearest finite value, matching C++11 behavior for operator>> - // on floating point. - AssemblyContext context(AutoText(""), nullptr); - const spv_result_t ec = SPV_FAILED_MATCH; - spvutils::HexFloat> f(0.0); - - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("1e38", ec, &f, "")); - EXPECT_EQ(1e38, f.value().getAsFloat()); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-1e38", ec, &f, "")); - EXPECT_EQ(-1e38, f.value().getAsFloat()); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("1e40", ec, &f, "")); - EXPECT_EQ(1e40, f.value().getAsFloat()); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-1e40", ec, &f, "")); - EXPECT_EQ(-1e40, f.value().getAsFloat()); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("1e400", ec, &f, "")); - EXPECT_EQ(SPV_FAILED_MATCH, context.parseNumber("-1e400", ec, &f, "")); -} - -TEST(AssemblyContextParseFloat16, Overflow) { - // The assembler parses using HexFloat>. Make - // sure that succeeds for in-range values, and fails for out of - // range values. When it does overflow, the value is set to the - // nearest finite value, matching C++11 behavior for operator>> - // on floating point. - AssemblyContext context(AutoText(""), nullptr); - const spv_result_t ec = SPV_FAILED_MATCH; - spvutils::HexFloat> f(0); - - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("-0.0", ec, &f, "")); - EXPECT_EQ(uint16_t{0x8000}, f.value().getAsFloat().get_value()); - EXPECT_EQ(SPV_SUCCESS, context.parseNumber("1.0", ec, &f, "")); - EXPECT_EQ(uint16_t{0x3c00}, f.value().getAsFloat().get_value()); - - // Overflows 16-bit but not 32-bit - EXPECT_EQ(ec, context.parseNumber("1e38", ec, &f, "")); - EXPECT_EQ(ec, context.parseNumber("-1e38", ec, &f, "")); - - // Overflows 32-bit but not 64-bit - EXPECT_EQ(ec, context.parseNumber("1e40", ec, &f, "")); - EXPECT_EQ(ec, context.parseNumber("-1e40", ec, &f, "")); - - // Overflows 64-bit - EXPECT_EQ(ec, context.parseNumber("1e400", ec, &f, "")); - EXPECT_EQ(ec, context.parseNumber("-1e400", ec, &f, "")); -} - -TEST(AssemblyContextParseMessages, Errors) { - spv_diagnostic diag = nullptr; - const spv_result_t ec = SPV_FAILED_MATCH; - 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", ec, &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", SPV_ERROR_INVALID_TEXT, &i16, - "bad narrow int: ")); - ASSERT_NE(nullptr, diag); - EXPECT_EQ("bad narrow int: abc", std::string(diag->error)); - // Don't leak. - spvDiagnosticDestroy(diag); -} - TEST(CreateContext, InvalidEnvironment) { spv_target_env env; std::memset(&env, 99, sizeof(env));