/src/spirv-tools/source/util/parse_number.h
Line | Count | Source |
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 | | spv_fp_encoding_t encoding; |
36 | | }; |
37 | | |
38 | | // Returns true if the type is a scalar integer type. |
39 | 7.57M | inline bool IsIntegral(const NumberType& type) { |
40 | 7.57M | return type.kind == SPV_NUMBER_UNSIGNED_INT || |
41 | 12.5k | type.kind == SPV_NUMBER_SIGNED_INT; |
42 | 7.57M | } |
43 | | |
44 | | // Returns true if the type is a scalar floating point type. |
45 | 8.04M | inline bool IsFloating(const NumberType& type) { |
46 | 8.04M | return type.kind == SPV_NUMBER_FLOATING; |
47 | 8.04M | } |
48 | | |
49 | | // Returns true if the type is a signed value. |
50 | 14.8M | inline bool IsSigned(const NumberType& type) { |
51 | 14.8M | return type.kind == SPV_NUMBER_FLOATING || type.kind == SPV_NUMBER_SIGNED_INT; |
52 | 14.8M | } |
53 | | |
54 | | // Returns true if the type is unknown. |
55 | 7.80M | inline bool IsUnknown(const NumberType& type) { |
56 | 7.80M | return type.kind == SPV_NUMBER_NONE; |
57 | 7.80M | } |
58 | | |
59 | | // Returns the number of bits in the type. This is only valid for integer and |
60 | | // floating types. |
61 | 15.1M | inline int AssumedBitWidth(const NumberType& type) { |
62 | 15.1M | switch (type.kind) { |
63 | 24.8k | case SPV_NUMBER_SIGNED_INT: |
64 | 14.9M | case SPV_NUMBER_UNSIGNED_INT: |
65 | 15.1M | case SPV_NUMBER_FLOATING: |
66 | 15.1M | return type.bitwidth; |
67 | 0 | default: |
68 | 0 | break; |
69 | 15.1M | } |
70 | | // We don't care about this case. |
71 | 0 | return 0; |
72 | 15.1M | } |
73 | | |
74 | | // A templated class with a static member function Clamp, where Clamp sets a |
75 | | // referenced value of type T to 0 if T is an unsigned integer type, and |
76 | | // returns true if it modified the referenced value. |
77 | | template <typename T, typename = void> |
78 | | class ClampToZeroIfUnsignedType { |
79 | | public: |
80 | | // The default specialization does not clamp the value. |
81 | 34.2k | static bool Clamp(T*) { return false; }spvtools::utils::ClampToZeroIfUnsignedType<long, void>::Clamp(long*) Line | Count | Source | 81 | 8.64k | static bool Clamp(T*) { return false; } |
spvtools::utils::ClampToZeroIfUnsignedType<spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::Float8_E4M3>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::Float8_E4M3> > >, void>::Clamp(spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::Float8_E4M3>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::Float8_E4M3> > >*) Line | Count | Source | 81 | 4.83k | static bool Clamp(T*) { return false; } |
spvtools::utils::ClampToZeroIfUnsignedType<spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::Float8_E5M2>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::Float8_E5M2> > >, void>::Clamp(spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::Float8_E5M2>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::Float8_E5M2> > >*) Line | Count | Source | 81 | 4.12k | static bool Clamp(T*) { return false; } |
spvtools::utils::ClampToZeroIfUnsignedType<spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::BFloat16>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::BFloat16> > >, void>::Clamp(spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::BFloat16>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::BFloat16> > >*) Line | Count | Source | 81 | 6.76k | static bool Clamp(T*) { return false; } |
spvtools::utils::ClampToZeroIfUnsignedType<spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::Float16>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::Float16> > >, void>::Clamp(spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::Float16>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::Float16> > >*) Line | Count | Source | 81 | 4.84k | static bool Clamp(T*) { return false; } |
spvtools::utils::ClampToZeroIfUnsignedType<spvtools::utils::HexFloat<spvtools::utils::FloatProxy<float>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<float> > >, void>::Clamp(spvtools::utils::HexFloat<spvtools::utils::FloatProxy<float>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<float> > >*) Line | Count | Source | 81 | 3.06k | static bool Clamp(T*) { return false; } |
spvtools::utils::ClampToZeroIfUnsignedType<spvtools::utils::HexFloat<spvtools::utils::FloatProxy<double>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<double> > >, void>::Clamp(spvtools::utils::HexFloat<spvtools::utils::FloatProxy<double>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<double> > >*) Line | Count | Source | 81 | 1.97k | static bool Clamp(T*) { return false; } |
|
82 | | }; |
83 | | |
84 | | // The specialization of ClampToZeroIfUnsignedType for unsigned integer types. |
85 | | template <typename T> |
86 | | class ClampToZeroIfUnsignedType< |
87 | | T, typename std::enable_if<std::is_unsigned<T>::value>::type> { |
88 | | public: |
89 | 3.68k | static bool Clamp(T* value_pointer) { |
90 | 3.68k | if (*value_pointer) { |
91 | 116 | *value_pointer = 0; |
92 | 116 | return true; |
93 | 116 | } |
94 | 3.56k | return false; |
95 | 3.68k | } spvtools::utils::ClampToZeroIfUnsignedType<unsigned int, void>::Clamp(unsigned int*) Line | Count | Source | 89 | 1.34k | static bool Clamp(T* value_pointer) { | 90 | 1.34k | if (*value_pointer) { | 91 | 84 | *value_pointer = 0; | 92 | 84 | return true; | 93 | 84 | } | 94 | 1.26k | return false; | 95 | 1.34k | } |
spvtools::utils::ClampToZeroIfUnsignedType<unsigned short, void>::Clamp(unsigned short*) Line | Count | Source | 89 | 2.33k | static bool Clamp(T* value_pointer) { | 90 | 2.33k | if (*value_pointer) { | 91 | 32 | *value_pointer = 0; | 92 | 32 | return true; | 93 | 32 | } | 94 | 2.30k | return false; | 95 | 2.33k | } |
Unexecuted instantiation: spvtools::utils::ClampToZeroIfUnsignedType<unsigned long, void>::Clamp(unsigned long*) |
96 | | }; |
97 | | |
98 | | // Returns true if the given value fits within the target scalar integral type. |
99 | | // The target type may have an unusual bit width. If the value was originally |
100 | | // specified as a hexadecimal number, then the overflow bits should be zero. |
101 | | // If it was hex and the target type is signed, then return the sign-extended |
102 | | // value through the updated_value_for_hex pointer argument. On failure, |
103 | | // returns false. |
104 | | template <typename T> |
105 | | bool CheckRangeAndIfHexThenSignExtend(T value, const NumberType& type, |
106 | 7.32M | bool is_hex, T* updated_value_for_hex) { |
107 | | // The encoded result has three regions of bits that are of interest, from |
108 | | // least to most significant: |
109 | | // - magnitude bits, where the magnitude of the number would be stored if |
110 | | // we were using a signed-magnitude representation. |
111 | | // - an optional sign bit |
112 | | // - overflow bits, up to bit 63 of a 64-bit number |
113 | | // For example: |
114 | | // Type Overflow Sign Magnitude |
115 | | // --------------- -------- ---- --------- |
116 | | // unsigned 8 bit 8-63 n/a 0-7 |
117 | | // signed 8 bit 8-63 7 0-6 |
118 | | // unsigned 16 bit 16-63 n/a 0-15 |
119 | | // signed 16 bit 16-63 15 0-14 |
120 | | |
121 | | // We'll use masks to define the three regions. |
122 | | // At first we'll assume the number is unsigned. |
123 | 7.32M | const uint32_t bit_width = AssumedBitWidth(type); |
124 | 7.32M | uint64_t magnitude_mask = |
125 | 7.32M | (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1); |
126 | 7.32M | uint64_t sign_mask = 0; |
127 | 7.32M | uint64_t overflow_mask = ~magnitude_mask; |
128 | | |
129 | 7.32M | if (value < 0 || IsSigned(type)) { |
130 | | // Accommodate the sign bit. |
131 | 12.3k | magnitude_mask >>= 1; |
132 | 12.3k | sign_mask = magnitude_mask + 1; |
133 | 12.3k | } |
134 | | |
135 | 7.32M | bool failed = false; |
136 | 7.32M | if (value < 0) { |
137 | | // The top bits must all be 1 for a negative signed value. |
138 | 4.90k | failed = ((value & overflow_mask) != overflow_mask) || |
139 | 4.63k | ((value & sign_mask) != sign_mask); |
140 | 7.31M | } else { |
141 | 7.31M | if (is_hex) { |
142 | | // Hex values are a bit special. They decode as unsigned values, but may |
143 | | // represent a negative number. In this case, the overflow bits should |
144 | | // be zero. |
145 | 2.08k | failed = (value & overflow_mask) != 0; |
146 | 7.31M | } else { |
147 | 7.31M | const uint64_t value_as_u64 = static_cast<uint64_t>(value); |
148 | | // Check overflow in the ordinary case. |
149 | 7.31M | failed = (value_as_u64 & magnitude_mask) != value_as_u64; |
150 | 7.31M | } |
151 | 7.31M | } |
152 | | |
153 | 7.32M | if (failed) { |
154 | 762 | return false; |
155 | 762 | } |
156 | | |
157 | | // Sign extend hex the number. |
158 | 7.32M | if (is_hex && (value & sign_mask)) |
159 | 70 | *updated_value_for_hex = (value | overflow_mask); |
160 | | |
161 | 7.32M | return true; |
162 | 7.32M | } bool spvtools::utils::CheckRangeAndIfHexThenSignExtend<long>(long, spvtools::utils::NumberType const&, bool, long*) Line | Count | Source | 106 | 8.64k | bool is_hex, T* updated_value_for_hex) { | 107 | | // The encoded result has three regions of bits that are of interest, from | 108 | | // least to most significant: | 109 | | // - magnitude bits, where the magnitude of the number would be stored if | 110 | | // we were using a signed-magnitude representation. | 111 | | // - an optional sign bit | 112 | | // - overflow bits, up to bit 63 of a 64-bit number | 113 | | // For example: | 114 | | // Type Overflow Sign Magnitude | 115 | | // --------------- -------- ---- --------- | 116 | | // unsigned 8 bit 8-63 n/a 0-7 | 117 | | // signed 8 bit 8-63 7 0-6 | 118 | | // unsigned 16 bit 16-63 n/a 0-15 | 119 | | // signed 16 bit 16-63 15 0-14 | 120 | | | 121 | | // We'll use masks to define the three regions. | 122 | | // At first we'll assume the number is unsigned. | 123 | 8.64k | const uint32_t bit_width = AssumedBitWidth(type); | 124 | 8.64k | uint64_t magnitude_mask = | 125 | 8.64k | (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1); | 126 | 8.64k | uint64_t sign_mask = 0; | 127 | 8.64k | uint64_t overflow_mask = ~magnitude_mask; | 128 | | | 129 | 8.64k | if (value < 0 || IsSigned(type)) { | 130 | | // Accommodate the sign bit. | 131 | 8.64k | magnitude_mask >>= 1; | 132 | 8.64k | sign_mask = magnitude_mask + 1; | 133 | 8.64k | } | 134 | | | 135 | 8.64k | bool failed = false; | 136 | 8.64k | if (value < 0) { | 137 | | // The top bits must all be 1 for a negative signed value. | 138 | 4.90k | failed = ((value & overflow_mask) != overflow_mask) || | 139 | 4.63k | ((value & sign_mask) != sign_mask); | 140 | 4.90k | } else { | 141 | 3.73k | if (is_hex) { | 142 | | // Hex values are a bit special. They decode as unsigned values, but may | 143 | | // represent a negative number. In this case, the overflow bits should | 144 | | // be zero. | 145 | 0 | failed = (value & overflow_mask) != 0; | 146 | 3.73k | } else { | 147 | 3.73k | const uint64_t value_as_u64 = static_cast<uint64_t>(value); | 148 | | // Check overflow in the ordinary case. | 149 | 3.73k | failed = (value_as_u64 & magnitude_mask) != value_as_u64; | 150 | 3.73k | } | 151 | 3.73k | } | 152 | | | 153 | 8.64k | if (failed) { | 154 | 314 | return false; | 155 | 314 | } | 156 | | | 157 | | // Sign extend hex the number. | 158 | 8.32k | if (is_hex && (value & sign_mask)) | 159 | 0 | *updated_value_for_hex = (value | overflow_mask); | 160 | | | 161 | 8.32k | return true; | 162 | 8.64k | } |
bool spvtools::utils::CheckRangeAndIfHexThenSignExtend<unsigned long>(unsigned long, spvtools::utils::NumberType const&, bool, unsigned long*) Line | Count | Source | 106 | 7.31M | bool is_hex, T* updated_value_for_hex) { | 107 | | // The encoded result has three regions of bits that are of interest, from | 108 | | // least to most significant: | 109 | | // - magnitude bits, where the magnitude of the number would be stored if | 110 | | // we were using a signed-magnitude representation. | 111 | | // - an optional sign bit | 112 | | // - overflow bits, up to bit 63 of a 64-bit number | 113 | | // For example: | 114 | | // Type Overflow Sign Magnitude | 115 | | // --------------- -------- ---- --------- | 116 | | // unsigned 8 bit 8-63 n/a 0-7 | 117 | | // signed 8 bit 8-63 7 0-6 | 118 | | // unsigned 16 bit 16-63 n/a 0-15 | 119 | | // signed 16 bit 16-63 15 0-14 | 120 | | | 121 | | // We'll use masks to define the three regions. | 122 | | // At first we'll assume the number is unsigned. | 123 | 7.31M | const uint32_t bit_width = AssumedBitWidth(type); | 124 | 7.31M | uint64_t magnitude_mask = | 125 | 7.31M | (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1); | 126 | 7.31M | uint64_t sign_mask = 0; | 127 | 7.31M | uint64_t overflow_mask = ~magnitude_mask; | 128 | | | 129 | 7.31M | if (value < 0 || IsSigned(type)) { | 130 | | // Accommodate the sign bit. | 131 | 3.67k | magnitude_mask >>= 1; | 132 | 3.67k | sign_mask = magnitude_mask + 1; | 133 | 3.67k | } | 134 | | | 135 | 7.31M | bool failed = false; | 136 | 7.31M | if (value < 0) { | 137 | | // The top bits must all be 1 for a negative signed value. | 138 | 0 | failed = ((value & overflow_mask) != overflow_mask) || | 139 | 0 | ((value & sign_mask) != sign_mask); | 140 | 7.31M | } else { | 141 | 7.31M | if (is_hex) { | 142 | | // Hex values are a bit special. They decode as unsigned values, but may | 143 | | // represent a negative number. In this case, the overflow bits should | 144 | | // be zero. | 145 | 2.08k | failed = (value & overflow_mask) != 0; | 146 | 7.31M | } else { | 147 | 7.31M | const uint64_t value_as_u64 = static_cast<uint64_t>(value); | 148 | | // Check overflow in the ordinary case. | 149 | 7.31M | failed = (value_as_u64 & magnitude_mask) != value_as_u64; | 150 | 7.31M | } | 151 | 7.31M | } | 152 | | | 153 | 7.31M | if (failed) { | 154 | 448 | return false; | 155 | 448 | } | 156 | | | 157 | | // Sign extend hex the number. | 158 | 7.31M | if (is_hex && (value & sign_mask)) | 159 | 70 | *updated_value_for_hex = (value | overflow_mask); | 160 | | | 161 | 7.31M | return true; | 162 | 7.31M | } |
|
163 | | |
164 | | template <typename T> |
165 | | struct IsHexFloat { |
166 | | static const bool value = false; |
167 | | }; |
168 | | template <typename T> |
169 | | struct IsHexFloat<HexFloat<T>> { |
170 | | static const bool value = true; |
171 | | }; |
172 | | // Parses a numeric value of a given type from the given text. The number |
173 | | // should take up the entire string, and should be within bounds for the target |
174 | | // type. On success, returns true and populates the object referenced by |
175 | | // value_pointer. On failure, returns false. |
176 | | template <typename T> |
177 | 8.48M | bool ParseNumber(const char* text, T* value_pointer) { |
178 | | // C++11 doesn't define std::istringstream(int8_t&), so calling this method |
179 | | // with a single-byte type leads to implementation-defined behaviour. |
180 | | // Similarly for uint8_t. |
181 | | // HexFloat<T> overloads the operator |
182 | 8.48M | static_assert(sizeof(T) > 1 || IsHexFloat<T>::value, |
183 | 8.48M | "Single-byte types other than HexFloat<> are not supported in " |
184 | 8.48M | "this parse method"); |
185 | | |
186 | 8.48M | if (!text) return false; |
187 | 8.48M | std::istringstream text_stream(text); |
188 | | // Allow both decimal and hex input for integers. |
189 | | // It also allows octal input, but we don't care about that case. |
190 | 8.48M | text_stream >> std::setbase(0); |
191 | 8.48M | text_stream >> *value_pointer; |
192 | | |
193 | | // We should have read something. |
194 | 8.48M | bool ok = (text[0] != 0) && !text_stream.bad(); |
195 | | // It should have been all the text. |
196 | 8.48M | ok = ok && text_stream.eof(); |
197 | | // It should have been in range. |
198 | 8.48M | ok = ok && !text_stream.fail(); |
199 | | |
200 | | // Work around a bug in the GNU C++11 library. It will happily parse |
201 | | // "-1" for uint16_t as 65535. |
202 | 8.48M | if (ok && text[0] == '-') |
203 | 37.9k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); |
204 | | |
205 | 8.48M | return ok; |
206 | 8.48M | } bool spvtools::utils::ParseNumber<unsigned int>(char const*, unsigned int*) Line | Count | Source | 177 | 662k | bool ParseNumber(const char* text, T* value_pointer) { | 178 | | // C++11 doesn't define std::istringstream(int8_t&), so calling this method | 179 | | // with a single-byte type leads to implementation-defined behaviour. | 180 | | // Similarly for uint8_t. | 181 | | // HexFloat<T> overloads the operator | 182 | 662k | static_assert(sizeof(T) > 1 || IsHexFloat<T>::value, | 183 | 662k | "Single-byte types other than HexFloat<> are not supported in " | 184 | 662k | "this parse method"); | 185 | | | 186 | 662k | if (!text) return false; | 187 | 662k | std::istringstream text_stream(text); | 188 | | // Allow both decimal and hex input for integers. | 189 | | // It also allows octal input, but we don't care about that case. | 190 | 662k | text_stream >> std::setbase(0); | 191 | 662k | text_stream >> *value_pointer; | 192 | | | 193 | | // We should have read something. | 194 | 662k | bool ok = (text[0] != 0) && !text_stream.bad(); | 195 | | // It should have been all the text. | 196 | 662k | ok = ok && text_stream.eof(); | 197 | | // It should have been in range. | 198 | 662k | ok = ok && !text_stream.fail(); | 199 | | | 200 | | // Work around a bug in the GNU C++11 library. It will happily parse | 201 | | // "-1" for uint16_t as 65535. | 202 | 662k | if (ok && text[0] == '-') | 203 | 1.34k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 204 | | | 205 | 662k | return ok; | 206 | 662k | } |
bool spvtools::utils::ParseNumber<unsigned short>(char const*, unsigned short*) Line | Count | Source | 177 | 16.0k | bool ParseNumber(const char* text, T* value_pointer) { | 178 | | // C++11 doesn't define std::istringstream(int8_t&), so calling this method | 179 | | // with a single-byte type leads to implementation-defined behaviour. | 180 | | // Similarly for uint8_t. | 181 | | // HexFloat<T> overloads the operator | 182 | 16.0k | static_assert(sizeof(T) > 1 || IsHexFloat<T>::value, | 183 | 16.0k | "Single-byte types other than HexFloat<> are not supported in " | 184 | 16.0k | "this parse method"); | 185 | | | 186 | 16.0k | if (!text) return false; | 187 | 16.0k | std::istringstream text_stream(text); | 188 | | // Allow both decimal and hex input for integers. | 189 | | // It also allows octal input, but we don't care about that case. | 190 | 16.0k | text_stream >> std::setbase(0); | 191 | 16.0k | text_stream >> *value_pointer; | 192 | | | 193 | | // We should have read something. | 194 | 16.0k | bool ok = (text[0] != 0) && !text_stream.bad(); | 195 | | // It should have been all the text. | 196 | 16.0k | ok = ok && text_stream.eof(); | 197 | | // It should have been in range. | 198 | 16.0k | ok = ok && !text_stream.fail(); | 199 | | | 200 | | // Work around a bug in the GNU C++11 library. It will happily parse | 201 | | // "-1" for uint16_t as 65535. | 202 | 16.0k | if (ok && text[0] == '-') | 203 | 2.33k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 204 | | | 205 | 16.0k | return ok; | 206 | 16.0k | } |
bool spvtools::utils::ParseNumber<long>(char const*, long*) Line | Count | Source | 177 | 8.83k | bool ParseNumber(const char* text, T* value_pointer) { | 178 | | // C++11 doesn't define std::istringstream(int8_t&), so calling this method | 179 | | // with a single-byte type leads to implementation-defined behaviour. | 180 | | // Similarly for uint8_t. | 181 | | // HexFloat<T> overloads the operator | 182 | 8.83k | static_assert(sizeof(T) > 1 || IsHexFloat<T>::value, | 183 | 8.83k | "Single-byte types other than HexFloat<> are not supported in " | 184 | 8.83k | "this parse method"); | 185 | | | 186 | 8.83k | if (!text) return false; | 187 | 8.83k | std::istringstream text_stream(text); | 188 | | // Allow both decimal and hex input for integers. | 189 | | // It also allows octal input, but we don't care about that case. | 190 | 8.83k | text_stream >> std::setbase(0); | 191 | 8.83k | text_stream >> *value_pointer; | 192 | | | 193 | | // We should have read something. | 194 | 8.83k | bool ok = (text[0] != 0) && !text_stream.bad(); | 195 | | // It should have been all the text. | 196 | 8.83k | ok = ok && text_stream.eof(); | 197 | | // It should have been in range. | 198 | 8.83k | ok = ok && !text_stream.fail(); | 199 | | | 200 | | // Work around a bug in the GNU C++11 library. It will happily parse | 201 | | // "-1" for uint16_t as 65535. | 202 | 8.83k | if (ok && text[0] == '-') | 203 | 8.64k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 204 | | | 205 | 8.83k | return ok; | 206 | 8.83k | } |
bool spvtools::utils::ParseNumber<unsigned long>(char const*, unsigned long*) Line | Count | Source | 177 | 7.56M | bool ParseNumber(const char* text, T* value_pointer) { | 178 | | // C++11 doesn't define std::istringstream(int8_t&), so calling this method | 179 | | // with a single-byte type leads to implementation-defined behaviour. | 180 | | // Similarly for uint8_t. | 181 | | // HexFloat<T> overloads the operator | 182 | 7.56M | static_assert(sizeof(T) > 1 || IsHexFloat<T>::value, | 183 | 7.56M | "Single-byte types other than HexFloat<> are not supported in " | 184 | 7.56M | "this parse method"); | 185 | | | 186 | 7.56M | if (!text) return false; | 187 | 7.56M | std::istringstream text_stream(text); | 188 | | // Allow both decimal and hex input for integers. | 189 | | // It also allows octal input, but we don't care about that case. | 190 | 7.56M | text_stream >> std::setbase(0); | 191 | 7.56M | text_stream >> *value_pointer; | 192 | | | 193 | | // We should have read something. | 194 | 7.56M | bool ok = (text[0] != 0) && !text_stream.bad(); | 195 | | // It should have been all the text. | 196 | 7.56M | ok = ok && text_stream.eof(); | 197 | | // It should have been in range. | 198 | 7.56M | ok = ok && !text_stream.fail(); | 199 | | | 200 | | // Work around a bug in the GNU C++11 library. It will happily parse | 201 | | // "-1" for uint16_t as 65535. | 202 | 7.56M | if (ok && text[0] == '-') | 203 | 0 | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 204 | | | 205 | 7.56M | return ok; | 206 | 7.56M | } |
bool spvtools::utils::ParseNumber<spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::Float8_E4M3>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::Float8_E4M3> > > >(char const*, spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::Float8_E4M3>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::Float8_E4M3> > >*) Line | Count | Source | 177 | 41.2k | bool ParseNumber(const char* text, T* value_pointer) { | 178 | | // C++11 doesn't define std::istringstream(int8_t&), so calling this method | 179 | | // with a single-byte type leads to implementation-defined behaviour. | 180 | | // Similarly for uint8_t. | 181 | | // HexFloat<T> overloads the operator | 182 | 41.2k | static_assert(sizeof(T) > 1 || IsHexFloat<T>::value, | 183 | 41.2k | "Single-byte types other than HexFloat<> are not supported in " | 184 | 41.2k | "this parse method"); | 185 | | | 186 | 41.2k | if (!text) return false; | 187 | 41.2k | std::istringstream text_stream(text); | 188 | | // Allow both decimal and hex input for integers. | 189 | | // It also allows octal input, but we don't care about that case. | 190 | 41.2k | text_stream >> std::setbase(0); | 191 | 41.2k | text_stream >> *value_pointer; | 192 | | | 193 | | // We should have read something. | 194 | 41.2k | bool ok = (text[0] != 0) && !text_stream.bad(); | 195 | | // It should have been all the text. | 196 | 41.2k | ok = ok && text_stream.eof(); | 197 | | // It should have been in range. | 198 | 41.2k | ok = ok && !text_stream.fail(); | 199 | | | 200 | | // Work around a bug in the GNU C++11 library. It will happily parse | 201 | | // "-1" for uint16_t as 65535. | 202 | 41.2k | if (ok && text[0] == '-') | 203 | 4.83k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 204 | | | 205 | 41.2k | return ok; | 206 | 41.2k | } |
bool spvtools::utils::ParseNumber<spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::Float8_E5M2>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::Float8_E5M2> > > >(char const*, spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::Float8_E5M2>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::Float8_E5M2> > >*) Line | Count | Source | 177 | 60.1k | bool ParseNumber(const char* text, T* value_pointer) { | 178 | | // C++11 doesn't define std::istringstream(int8_t&), so calling this method | 179 | | // with a single-byte type leads to implementation-defined behaviour. | 180 | | // Similarly for uint8_t. | 181 | | // HexFloat<T> overloads the operator | 182 | 60.1k | static_assert(sizeof(T) > 1 || IsHexFloat<T>::value, | 183 | 60.1k | "Single-byte types other than HexFloat<> are not supported in " | 184 | 60.1k | "this parse method"); | 185 | | | 186 | 60.1k | if (!text) return false; | 187 | 60.1k | std::istringstream text_stream(text); | 188 | | // Allow both decimal and hex input for integers. | 189 | | // It also allows octal input, but we don't care about that case. | 190 | 60.1k | text_stream >> std::setbase(0); | 191 | 60.1k | text_stream >> *value_pointer; | 192 | | | 193 | | // We should have read something. | 194 | 60.1k | bool ok = (text[0] != 0) && !text_stream.bad(); | 195 | | // It should have been all the text. | 196 | 60.1k | ok = ok && text_stream.eof(); | 197 | | // It should have been in range. | 198 | 60.1k | ok = ok && !text_stream.fail(); | 199 | | | 200 | | // Work around a bug in the GNU C++11 library. It will happily parse | 201 | | // "-1" for uint16_t as 65535. | 202 | 60.1k | if (ok && text[0] == '-') | 203 | 4.12k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 204 | | | 205 | 60.1k | return ok; | 206 | 60.1k | } |
bool spvtools::utils::ParseNumber<spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::BFloat16>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::BFloat16> > > >(char const*, spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::BFloat16>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::BFloat16> > >*) Line | Count | Source | 177 | 39.8k | bool ParseNumber(const char* text, T* value_pointer) { | 178 | | // C++11 doesn't define std::istringstream(int8_t&), so calling this method | 179 | | // with a single-byte type leads to implementation-defined behaviour. | 180 | | // Similarly for uint8_t. | 181 | | // HexFloat<T> overloads the operator | 182 | 39.8k | static_assert(sizeof(T) > 1 || IsHexFloat<T>::value, | 183 | 39.8k | "Single-byte types other than HexFloat<> are not supported in " | 184 | 39.8k | "this parse method"); | 185 | | | 186 | 39.8k | if (!text) return false; | 187 | 39.8k | std::istringstream text_stream(text); | 188 | | // Allow both decimal and hex input for integers. | 189 | | // It also allows octal input, but we don't care about that case. | 190 | 39.8k | text_stream >> std::setbase(0); | 191 | 39.8k | text_stream >> *value_pointer; | 192 | | | 193 | | // We should have read something. | 194 | 39.8k | bool ok = (text[0] != 0) && !text_stream.bad(); | 195 | | // It should have been all the text. | 196 | 39.8k | ok = ok && text_stream.eof(); | 197 | | // It should have been in range. | 198 | 39.8k | ok = ok && !text_stream.fail(); | 199 | | | 200 | | // Work around a bug in the GNU C++11 library. It will happily parse | 201 | | // "-1" for uint16_t as 65535. | 202 | 39.8k | if (ok && text[0] == '-') | 203 | 6.76k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 204 | | | 205 | 39.8k | return ok; | 206 | 39.8k | } |
bool spvtools::utils::ParseNumber<spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::Float16>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::Float16> > > >(char const*, spvtools::utils::HexFloat<spvtools::utils::FloatProxy<spvtools::utils::Float16>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<spvtools::utils::Float16> > >*) Line | Count | Source | 177 | 37.1k | bool ParseNumber(const char* text, T* value_pointer) { | 178 | | // C++11 doesn't define std::istringstream(int8_t&), so calling this method | 179 | | // with a single-byte type leads to implementation-defined behaviour. | 180 | | // Similarly for uint8_t. | 181 | | // HexFloat<T> overloads the operator | 182 | 37.1k | static_assert(sizeof(T) > 1 || IsHexFloat<T>::value, | 183 | 37.1k | "Single-byte types other than HexFloat<> are not supported in " | 184 | 37.1k | "this parse method"); | 185 | | | 186 | 37.1k | if (!text) return false; | 187 | 37.1k | std::istringstream text_stream(text); | 188 | | // Allow both decimal and hex input for integers. | 189 | | // It also allows octal input, but we don't care about that case. | 190 | 37.1k | text_stream >> std::setbase(0); | 191 | 37.1k | text_stream >> *value_pointer; | 192 | | | 193 | | // We should have read something. | 194 | 37.1k | bool ok = (text[0] != 0) && !text_stream.bad(); | 195 | | // It should have been all the text. | 196 | 37.1k | ok = ok && text_stream.eof(); | 197 | | // It should have been in range. | 198 | 37.1k | ok = ok && !text_stream.fail(); | 199 | | | 200 | | // Work around a bug in the GNU C++11 library. It will happily parse | 201 | | // "-1" for uint16_t as 65535. | 202 | 37.1k | if (ok && text[0] == '-') | 203 | 4.84k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 204 | | | 205 | 37.1k | return ok; | 206 | 37.1k | } |
bool spvtools::utils::ParseNumber<spvtools::utils::HexFloat<spvtools::utils::FloatProxy<float>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<float> > > >(char const*, spvtools::utils::HexFloat<spvtools::utils::FloatProxy<float>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<float> > >*) Line | Count | Source | 177 | 27.5k | bool ParseNumber(const char* text, T* value_pointer) { | 178 | | // C++11 doesn't define std::istringstream(int8_t&), so calling this method | 179 | | // with a single-byte type leads to implementation-defined behaviour. | 180 | | // Similarly for uint8_t. | 181 | | // HexFloat<T> overloads the operator | 182 | 27.5k | static_assert(sizeof(T) > 1 || IsHexFloat<T>::value, | 183 | 27.5k | "Single-byte types other than HexFloat<> are not supported in " | 184 | 27.5k | "this parse method"); | 185 | | | 186 | 27.5k | if (!text) return false; | 187 | 27.5k | std::istringstream text_stream(text); | 188 | | // Allow both decimal and hex input for integers. | 189 | | // It also allows octal input, but we don't care about that case. | 190 | 27.5k | text_stream >> std::setbase(0); | 191 | 27.5k | text_stream >> *value_pointer; | 192 | | | 193 | | // We should have read something. | 194 | 27.5k | bool ok = (text[0] != 0) && !text_stream.bad(); | 195 | | // It should have been all the text. | 196 | 27.5k | ok = ok && text_stream.eof(); | 197 | | // It should have been in range. | 198 | 27.5k | ok = ok && !text_stream.fail(); | 199 | | | 200 | | // Work around a bug in the GNU C++11 library. It will happily parse | 201 | | // "-1" for uint16_t as 65535. | 202 | 27.5k | if (ok && text[0] == '-') | 203 | 3.06k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 204 | | | 205 | 27.5k | return ok; | 206 | 27.5k | } |
bool spvtools::utils::ParseNumber<spvtools::utils::HexFloat<spvtools::utils::FloatProxy<double>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<double> > > >(char const*, spvtools::utils::HexFloat<spvtools::utils::FloatProxy<double>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<double> > >*) Line | Count | Source | 177 | 26.1k | bool ParseNumber(const char* text, T* value_pointer) { | 178 | | // C++11 doesn't define std::istringstream(int8_t&), so calling this method | 179 | | // with a single-byte type leads to implementation-defined behaviour. | 180 | | // Similarly for uint8_t. | 181 | | // HexFloat<T> overloads the operator | 182 | 26.1k | static_assert(sizeof(T) > 1 || IsHexFloat<T>::value, | 183 | 26.1k | "Single-byte types other than HexFloat<> are not supported in " | 184 | 26.1k | "this parse method"); | 185 | | | 186 | 26.1k | if (!text) return false; | 187 | 26.1k | std::istringstream text_stream(text); | 188 | | // Allow both decimal and hex input for integers. | 189 | | // It also allows octal input, but we don't care about that case. | 190 | 26.1k | text_stream >> std::setbase(0); | 191 | 26.1k | text_stream >> *value_pointer; | 192 | | | 193 | | // We should have read something. | 194 | 26.1k | bool ok = (text[0] != 0) && !text_stream.bad(); | 195 | | // It should have been all the text. | 196 | 26.1k | ok = ok && text_stream.eof(); | 197 | | // It should have been in range. | 198 | 26.1k | ok = ok && !text_stream.fail(); | 199 | | | 200 | | // Work around a bug in the GNU C++11 library. It will happily parse | 201 | | // "-1" for uint16_t as 65535. | 202 | 26.1k | if (ok && text[0] == '-') | 203 | 1.97k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 204 | | | 205 | 26.1k | return ok; | 206 | 26.1k | } |
|
207 | | |
208 | | // Enum to indicate the parsing and encoding status. |
209 | | enum class EncodeNumberStatus { |
210 | | kSuccess = 0, |
211 | | // Unsupported bit width etc. |
212 | | kUnsupported, |
213 | | // Expected type (NumberType) is not a scalar int or float, or putting a |
214 | | // negative number in an unsigned literal. |
215 | | kInvalidUsage, |
216 | | // Number value does not fit the bit width of the expected type etc. |
217 | | kInvalidText, |
218 | | }; |
219 | | |
220 | | // Parses an integer value of a given |type| from the given |text| and encodes |
221 | | // the number by the given |emit| function. On success, returns |
222 | | // EncodeNumberStatus::kSuccess and the parsed number will be consumed by the |
223 | | // given |emit| function word by word (least significant word first). On |
224 | | // failure, this function returns the error code of the encoding status and |
225 | | // |emit| function will not be called. If the string pointer |error_msg| is not |
226 | | // a nullptr, it will be overwritten with error messages in case of failure. In |
227 | | // case of success, |error_msg| will not be touched. Integers up to 64 bits are |
228 | | // supported. |
229 | | EncodeNumberStatus ParseAndEncodeIntegerNumber( |
230 | | const char* text, const NumberType& type, |
231 | | std::function<void(uint32_t)> emit, std::string* error_msg); |
232 | | |
233 | | // Parses a floating point value of a given |type| from the given |text| and |
234 | | // encodes the number by the given |emit| function. On success, returns |
235 | | // EncodeNumberStatus::kSuccess and the parsed number will be consumed by the |
236 | | // given |emit| function word by word (least significant word first). On |
237 | | // failure, this function returns the error code of the encoding status and |
238 | | // |emit| function will not be called. If the string pointer |error_msg| is not |
239 | | // a nullptr, it will be overwritten with error messages in case of failure. In |
240 | | // case of success, |error_msg| will not be touched. Only 16, 32 and 64 bit |
241 | | // floating point numbers are supported. |
242 | | EncodeNumberStatus ParseAndEncodeFloatingPointNumber( |
243 | | const char* text, const NumberType& type, |
244 | | std::function<void(uint32_t)> emit, std::string* error_msg); |
245 | | |
246 | | // Parses an integer or floating point number of a given |type| from the given |
247 | | // |text| and encodes the number by the given |emit| function. On success, |
248 | | // returns EncodeNumberStatus::kSuccess and the parsed number will be consumed |
249 | | // by the given |emit| function word by word (least significant word first). On |
250 | | // failure, this function returns the error code of the encoding status and |
251 | | // |emit| function will not be called. If the string pointer |error_msg| is not |
252 | | // a nullptr, it will be overwritten with error messages in case of failure. In |
253 | | // case of success, |error_msg| will not be touched. Integers up to 64 bits |
254 | | // and 16/32/64 bit floating point values are supported. |
255 | | EncodeNumberStatus ParseAndEncodeNumber(const char* text, |
256 | | const NumberType& type, |
257 | | std::function<void(uint32_t)> emit, |
258 | | std::string* error_msg); |
259 | | |
260 | | } // namespace utils |
261 | | } // namespace spvtools |
262 | | |
263 | | #endif // SOURCE_UTIL_PARSE_NUMBER_H_ |