123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- // 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| 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<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_
|