1 // Copyright (c) 2016 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef SOURCE_UTIL_PARSE_NUMBER_H_ 16 #define SOURCE_UTIL_PARSE_NUMBER_H_ 17 18 #include <functional> 19 #include <string> 20 #include <tuple> 21 22 #include "source/util/hex_float.h" 23 #include "spirv-tools/libspirv.h" 24 25 namespace spvtools { 26 namespace utils { 27 28 // A struct to hold the expected type information for the number in text to be 29 // parsed. 30 struct NumberType { 31 uint32_t bitwidth; 32 // SPV_NUMBER_NONE means the type is unknown and is invalid to be used with 33 // ParseAndEncode{|Integer|Floating}Number(). 34 spv_number_kind_t kind; 35 }; 36 37 // Returns true if the type is a scalar integer type. 38 inline bool IsIntegral(const NumberType& type) { 39 return type.kind == SPV_NUMBER_UNSIGNED_INT || 40 type.kind == SPV_NUMBER_SIGNED_INT; 41 } 42 43 // Returns true if the type is a scalar floating point type. 44 inline bool IsFloating(const NumberType& type) { 45 return type.kind == SPV_NUMBER_FLOATING; 46 } 47 48 // Returns true if the type is a signed value. 49 inline bool IsSigned(const NumberType& type) { 50 return type.kind == SPV_NUMBER_FLOATING || type.kind == SPV_NUMBER_SIGNED_INT; 51 } 52 53 // Returns true if the type is unknown. 54 inline bool IsUnknown(const NumberType& type) { 55 return type.kind == SPV_NUMBER_NONE; 56 } 57 58 // Returns the number of bits in the type. This is only valid for integer and 59 // floating types. 60 inline int AssumedBitWidth(const NumberType& type) { 61 switch (type.kind) { 62 case SPV_NUMBER_SIGNED_INT: 63 case SPV_NUMBER_UNSIGNED_INT: 64 case SPV_NUMBER_FLOATING: 65 return type.bitwidth; 66 default: 67 break; 68 } 69 // We don't care about this case. 70 return 0; 71 } 72 73 // A templated class with a static member function Clamp, where Clamp sets a 74 // referenced value of type T to 0 if T is an unsigned integer type, and 75 // returns true if it modified the referenced value. 76 template <typename T, typename = void> 77 class ClampToZeroIfUnsignedType { 78 public: 79 // The default specialization does not clamp the value. 80 static bool Clamp(T*) { return false; } 81 }; 82 83 // The specialization of ClampToZeroIfUnsignedType for unsigned integer types. 84 template <typename T> 85 class ClampToZeroIfUnsignedType< 86 T, typename std::enable_if<std::is_unsigned<T>::value>::type> { 87 public: 88 static bool Clamp(T* value_pointer) { 89 if (*value_pointer) { 90 *value_pointer = 0; 91 return true; 92 } 93 return false; 94 } 95 }; 96 97 // Returns true if the given value fits within the target scalar integral type. 98 // The target type may have an unusual bit width. If the value was originally 99 // specified as a hexadecimal number, then the overflow bits should be zero. 100 // If it was hex and the target type is signed, then return the sign-extended 101 // value through the updated_value_for_hex pointer argument. On failure, 102 // returns false. 103 template <typename T> 104 bool CheckRangeAndIfHexThenSignExtend(T value, const NumberType& type, 105 bool is_hex, T* updated_value_for_hex) { 106 // The encoded result has three regions of bits that are of interest, from 107 // least to most significant: 108 // - magnitude bits, where the magnitude of the number would be stored if 109 // we were using a signed-magnitude representation. 110 // - an optional sign bit 111 // - overflow bits, up to bit 63 of a 64-bit number 112 // For example: 113 // Type Overflow Sign Magnitude 114 // --------------- -------- ---- --------- 115 // unsigned 8 bit 8-63 n/a 0-7 116 // signed 8 bit 8-63 7 0-6 117 // unsigned 16 bit 16-63 n/a 0-15 118 // signed 16 bit 16-63 15 0-14 119 120 // We'll use masks to define the three regions. 121 // At first we'll assume the number is unsigned. 122 const uint32_t bit_width = AssumedBitWidth(type); 123 uint64_t magnitude_mask = 124 (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1); 125 uint64_t sign_mask = 0; 126 uint64_t overflow_mask = ~magnitude_mask; 127 128 if (value < 0 || IsSigned(type)) { 129 // Accommodate the sign bit. 130 magnitude_mask >>= 1; 131 sign_mask = magnitude_mask + 1; 132 } 133 134 bool failed = false; 135 if (value < 0) { 136 // The top bits must all be 1 for a negative signed value. 137 failed = ((value & overflow_mask) != overflow_mask) || 138 ((value & sign_mask) != sign_mask); 139 } else { 140 if (is_hex) { 141 // Hex values are a bit special. They decode as unsigned values, but may 142 // represent a negative number. In this case, the overflow bits should 143 // be zero. 144 failed = (value & overflow_mask) != 0; 145 } else { 146 const uint64_t value_as_u64 = static_cast<uint64_t>(value); 147 // Check overflow in the ordinary case. 148 failed = (value_as_u64 & magnitude_mask) != value_as_u64; 149 } 150 } 151 152 if (failed) { 153 return false; 154 } 155 156 // Sign extend hex the number. 157 if (is_hex && (value & sign_mask)) 158 *updated_value_for_hex = (value | overflow_mask); 159 160 return true; 161 } 162 163 // Parses a numeric value of a given type from the given text. The number 164 // should take up the entire string, and should be within bounds for the target 165 // type. On success, returns true and populates the object referenced by 166 // value_pointer. On failure, returns false. 167 template <typename T> 168 bool ParseNumber(const char* text, T* value_pointer) { 169 // C++11 doesn't define std::istringstream(int8_t&), so calling this method 170 // with a single-byte type leads to implementation-defined behaviour. 171 // Similarly for uint8_t. 172 static_assert(sizeof(T) > 1, 173 "Single-byte types are not supported in this parse method"); 174 175 if (!text) return false; 176 std::istringstream text_stream(text); 177 // Allow both decimal and hex input for integers. 178 // It also allows octal input, but we don't care about that case. 179 text_stream >> std::setbase(0); 180 text_stream >> *value_pointer; 181 182 // We should have read something. 183 bool ok = (text[0] != 0) && !text_stream.bad(); 184 // It should have been all the text. 185 ok = ok && text_stream.eof(); 186 // It should have been in range. 187 ok = ok && !text_stream.fail(); 188 189 // Work around a bug in the GNU C++11 library. It will happily parse 190 // "-1" for uint16_t as 65535. 191 if (ok && text[0] == '-') 192 ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); 193 194 return ok; 195 } 196 197 // Enum to indicate the parsing and encoding status. 198 enum class EncodeNumberStatus { 199 kSuccess = 0, 200 // Unsupported bit width etc. 201 kUnsupported, 202 // Expected type (NumberType) is not a scalar int or float, or putting a 203 // negative number in an unsigned literal. 204 kInvalidUsage, 205 // Number value does not fit the bit width of the expected type etc. 206 kInvalidText, 207 }; 208 209 // Parses an integer value of a given |type| from the given |text| and encodes 210 // the number by the given |emit| function. On success, returns 211 // EncodeNumberStatus::kSuccess and the parsed number will be consumed by the 212 // given |emit| function word by word (least significant word first). On 213 // failure, this function returns the error code of the encoding status and 214 // |emit| function will not be called. If the string pointer |error_msg| is not 215 // a nullptr, it will be overwritten with error messages in case of failure. In 216 // case of success, |error_msg| will not be touched. Integers up to 64 bits are 217 // supported. 218 EncodeNumberStatus ParseAndEncodeIntegerNumber( 219 const char* text, const NumberType& type, 220 std::function<void(uint32_t)> emit, std::string* error_msg); 221 222 // Parses a floating point value of a given |type| from the given |text| and 223 // encodes the number by the given |emit| funciton. On success, returns 224 // EncodeNumberStatus::kSuccess and the parsed number will be consumed by the 225 // given |emit| function word by word (least significant word first). On 226 // failure, this function returns the error code of the encoding status and 227 // |emit| function will not be called. If the string pointer |error_msg| is not 228 // a nullptr, it will be overwritten with error messages in case of failure. In 229 // case of success, |error_msg| will not be touched. Only 16, 32 and 64 bit 230 // floating point numbers are supported. 231 EncodeNumberStatus ParseAndEncodeFloatingPointNumber( 232 const char* text, const NumberType& type, 233 std::function<void(uint32_t)> emit, std::string* error_msg); 234 235 // Parses an integer or floating point number of a given |type| from the given 236 // |text| and encodes the number by the given |emit| function. On success, 237 // returns EncodeNumberStatus::kSuccess and the parsed number will be consumed 238 // by the given |emit| function word by word (least significant word first). On 239 // failure, this function returns the error code of the encoding status and 240 // |emit| function will not be called. If the string pointer |error_msg| is not 241 // a nullptr, it will be overwritten with error messages in case of failure. In 242 // case of success, |error_msg| will not be touched. Integers up to 64 bits 243 // and 16/32/64 bit floating point values are supported. 244 EncodeNumberStatus ParseAndEncodeNumber(const char* text, 245 const NumberType& type, 246 std::function<void(uint32_t)> emit, 247 std::string* error_msg); 248 249 } // namespace utils 250 } // namespace spvtools 251 252 #endif // SOURCE_UTIL_PARSE_NUMBER_H_ 253