| // 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 SOURCE_UTIL_PARSE_NUMBER_H_ | |
| #define SOURCE_UTIL_PARSE_NUMBER_H_ | |
| #include <functional> | |
| #include <string> | |
| #include <tuple> | |
| #include "source/util/hex_float.h" | |
| #include "spirv-tools/libspirv.h" | |
| namespace spvtools { | |
| namespace utils { | |
| // 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 <typename T, typename = void> | |
| 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 <typename T> | |
| class ClampToZeroIfUnsignedType< | |
| T, typename std::enable_if<std::is_unsigned<T>::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 <typename T> | |
| 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<uint64_t>(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 <typename T> | |
| 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, | |
| "Single-byte types are not supported in this parse method"); | |
| if (!text) return false; | |
| 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<T>::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<void(uint32_t)> 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| 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. Only 16, 32 and 64 bit | |
| // floating point numbers are supported. | |
| EncodeNumberStatus ParseAndEncodeFloatingPointNumber( | |
| const char* text, const NumberType& type, | |
| std::function<void(uint32_t)> 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<void(uint32_t)> emit, | |
| std::string* error_msg); | |
| } // namespace utils | |
| } // namespace spvtools | |
| #endif // SOURCE_UTIL_PARSE_NUMBER_H_ |