/src/spirv-tools/source/util/parse_number.h
Line | Count | Source (jump to first uncovered line) |
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 | 7.98M | inline bool IsIntegral(const NumberType& type) { |
39 | 7.98M | return type.kind == SPV_NUMBER_UNSIGNED_INT || |
40 | 7.98M | type.kind == SPV_NUMBER_SIGNED_INT; |
41 | 7.98M | } |
42 | | |
43 | | // Returns true if the type is a scalar floating point type. |
44 | 8.16M | inline bool IsFloating(const NumberType& type) { |
45 | 8.16M | return type.kind == SPV_NUMBER_FLOATING; |
46 | 8.16M | } |
47 | | |
48 | | // Returns true if the type is a signed value. |
49 | 15.7M | inline bool IsSigned(const NumberType& type) { |
50 | 15.7M | return type.kind == SPV_NUMBER_FLOATING || type.kind == SPV_NUMBER_SIGNED_INT; |
51 | 15.7M | } |
52 | | |
53 | | // Returns true if the type is unknown. |
54 | 8.07M | inline bool IsUnknown(const NumberType& type) { |
55 | 8.07M | return type.kind == SPV_NUMBER_NONE; |
56 | 8.07M | } |
57 | | |
58 | | // Returns the number of bits in the type. This is only valid for integer and |
59 | | // floating types. |
60 | 15.8M | inline int AssumedBitWidth(const NumberType& type) { |
61 | 15.8M | switch (type.kind) { |
62 | 29.3k | case SPV_NUMBER_SIGNED_INT: |
63 | 15.7M | case SPV_NUMBER_UNSIGNED_INT: |
64 | 15.8M | case SPV_NUMBER_FLOATING: |
65 | 15.8M | return type.bitwidth; |
66 | 0 | default: |
67 | 0 | break; |
68 | 15.8M | } |
69 | | // We don't care about this case. |
70 | 0 | return 0; |
71 | 15.8M | } |
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 | 19.3k | static bool Clamp(T*) { return false; } spvtools::utils::ClampToZeroIfUnsignedType<long, void>::Clamp(long*) Line | Count | Source | 80 | 7.24k | 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 | 80 | 7.38k | 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 | 80 | 1.99k | 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 | 80 | 2.68k | 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 | 819 | static bool Clamp(T* value_pointer) { |
89 | 819 | if (*value_pointer) { |
90 | 100 | *value_pointer = 0; |
91 | 100 | return true; |
92 | 100 | } |
93 | 719 | return false; |
94 | 819 | } spvtools::utils::ClampToZeroIfUnsignedType<unsigned int, void>::Clamp(unsigned int*) Line | Count | Source | 88 | 819 | static bool Clamp(T* value_pointer) { | 89 | 819 | if (*value_pointer) { | 90 | 100 | *value_pointer = 0; | 91 | 100 | return true; | 92 | 100 | } | 93 | 719 | return false; | 94 | 819 | } |
Unexecuted instantiation: spvtools::utils::ClampToZeroIfUnsignedType<unsigned long, void>::Clamp(unsigned long*) |
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 | 7.78M | 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 | 7.78M | const uint32_t bit_width = AssumedBitWidth(type); |
123 | 7.78M | uint64_t magnitude_mask = |
124 | 7.78M | (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1); |
125 | 7.78M | uint64_t sign_mask = 0; |
126 | 7.78M | uint64_t overflow_mask = ~magnitude_mask; |
127 | | |
128 | 7.78M | if (value < 0 || IsSigned(type)) { |
129 | | // Accommodate the sign bit. |
130 | 14.5k | magnitude_mask >>= 1; |
131 | 14.5k | sign_mask = magnitude_mask + 1; |
132 | 14.5k | } |
133 | | |
134 | 7.78M | bool failed = false; |
135 | 7.78M | if (value < 0) { |
136 | | // The top bits must all be 1 for a negative signed value. |
137 | 5.87k | failed = ((value & overflow_mask) != overflow_mask) || |
138 | 5.87k | ((value & sign_mask) != sign_mask); |
139 | 7.78M | } else { |
140 | 7.78M | 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 | 1.81k | failed = (value & overflow_mask) != 0; |
145 | 7.78M | } else { |
146 | 7.78M | const uint64_t value_as_u64 = static_cast<uint64_t>(value); |
147 | | // Check overflow in the ordinary case. |
148 | 7.78M | failed = (value_as_u64 & magnitude_mask) != value_as_u64; |
149 | 7.78M | } |
150 | 7.78M | } |
151 | | |
152 | 7.78M | if (failed) { |
153 | 786 | return false; |
154 | 786 | } |
155 | | |
156 | | // Sign extend hex the number. |
157 | 7.78M | if (is_hex && (value & sign_mask)) |
158 | 199 | *updated_value_for_hex = (value | overflow_mask); |
159 | | |
160 | 7.78M | return true; |
161 | 7.78M | } bool spvtools::utils::CheckRangeAndIfHexThenSignExtend<long>(long, spvtools::utils::NumberType const&, bool, long*) Line | Count | Source | 105 | 7.24k | 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 | 7.24k | const uint32_t bit_width = AssumedBitWidth(type); | 123 | 7.24k | uint64_t magnitude_mask = | 124 | 7.24k | (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1); | 125 | 7.24k | uint64_t sign_mask = 0; | 126 | 7.24k | uint64_t overflow_mask = ~magnitude_mask; | 127 | | | 128 | 7.24k | if (value < 0 || IsSigned(type)) { | 129 | | // Accommodate the sign bit. | 130 | 7.24k | magnitude_mask >>= 1; | 131 | 7.24k | sign_mask = magnitude_mask + 1; | 132 | 7.24k | } | 133 | | | 134 | 7.24k | bool failed = false; | 135 | 7.24k | if (value < 0) { | 136 | | // The top bits must all be 1 for a negative signed value. | 137 | 5.87k | failed = ((value & overflow_mask) != overflow_mask) || | 138 | 5.87k | ((value & sign_mask) != sign_mask); | 139 | 5.87k | } else { | 140 | 1.37k | 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 | 0 | failed = (value & overflow_mask) != 0; | 145 | 1.37k | } else { | 146 | 1.37k | const uint64_t value_as_u64 = static_cast<uint64_t>(value); | 147 | | // Check overflow in the ordinary case. | 148 | 1.37k | failed = (value_as_u64 & magnitude_mask) != value_as_u64; | 149 | 1.37k | } | 150 | 1.37k | } | 151 | | | 152 | 7.24k | if (failed) { | 153 | 239 | return false; | 154 | 239 | } | 155 | | | 156 | | // Sign extend hex the number. | 157 | 7.01k | if (is_hex && (value & sign_mask)) | 158 | 0 | *updated_value_for_hex = (value | overflow_mask); | 159 | | | 160 | 7.01k | return true; | 161 | 7.24k | } |
bool spvtools::utils::CheckRangeAndIfHexThenSignExtend<unsigned long>(unsigned long, spvtools::utils::NumberType const&, bool, unsigned long*) Line | Count | Source | 105 | 7.78M | 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 | 7.78M | const uint32_t bit_width = AssumedBitWidth(type); | 123 | 7.78M | uint64_t magnitude_mask = | 124 | 7.78M | (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1); | 125 | 7.78M | uint64_t sign_mask = 0; | 126 | 7.78M | uint64_t overflow_mask = ~magnitude_mask; | 127 | | | 128 | 7.78M | if (value < 0 || IsSigned(type)) { | 129 | | // Accommodate the sign bit. | 130 | 7.31k | magnitude_mask >>= 1; | 131 | 7.31k | sign_mask = magnitude_mask + 1; | 132 | 7.31k | } | 133 | | | 134 | 7.78M | bool failed = false; | 135 | 7.78M | if (value < 0) { | 136 | | // The top bits must all be 1 for a negative signed value. | 137 | 0 | failed = ((value & overflow_mask) != overflow_mask) || | 138 | 0 | ((value & sign_mask) != sign_mask); | 139 | 7.78M | } else { | 140 | 7.78M | 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 | 1.81k | failed = (value & overflow_mask) != 0; | 145 | 7.78M | } else { | 146 | 7.78M | const uint64_t value_as_u64 = static_cast<uint64_t>(value); | 147 | | // Check overflow in the ordinary case. | 148 | 7.78M | failed = (value_as_u64 & magnitude_mask) != value_as_u64; | 149 | 7.78M | } | 150 | 7.78M | } | 151 | | | 152 | 7.78M | if (failed) { | 153 | 547 | return false; | 154 | 547 | } | 155 | | | 156 | | // Sign extend hex the number. | 157 | 7.78M | if (is_hex && (value & sign_mask)) | 158 | 199 | *updated_value_for_hex = (value | overflow_mask); | 159 | | | 160 | 7.78M | return true; | 161 | 7.78M | } |
|
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 | 8.65M | 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 | 8.65M | static_assert(sizeof(T) > 1, |
173 | 8.65M | "Single-byte types are not supported in this parse method"); |
174 | | |
175 | 8.65M | if (!text) return false; |
176 | 8.65M | 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 | 8.65M | text_stream >> std::setbase(0); |
180 | 8.65M | text_stream >> *value_pointer; |
181 | | |
182 | | // We should have read something. |
183 | 8.65M | bool ok = (text[0] != 0) && !text_stream.bad(); |
184 | | // It should have been all the text. |
185 | 8.65M | ok = ok && text_stream.eof(); |
186 | | // It should have been in range. |
187 | 8.65M | 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 | 8.65M | if (ok && text[0] == '-') |
192 | 20.1k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); |
193 | | |
194 | 8.65M | return ok; |
195 | 8.65M | } bool spvtools::utils::ParseNumber<unsigned int>(char const*, unsigned int*) Line | Count | Source | 168 | 576k | 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 | 576k | static_assert(sizeof(T) > 1, | 173 | 576k | "Single-byte types are not supported in this parse method"); | 174 | | | 175 | 576k | if (!text) return false; | 176 | 576k | 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 | 576k | text_stream >> std::setbase(0); | 180 | 576k | text_stream >> *value_pointer; | 181 | | | 182 | | // We should have read something. | 183 | 576k | bool ok = (text[0] != 0) && !text_stream.bad(); | 184 | | // It should have been all the text. | 185 | 576k | ok = ok && text_stream.eof(); | 186 | | // It should have been in range. | 187 | 576k | 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 | 576k | if (ok && text[0] == '-') | 192 | 819 | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 193 | | | 194 | 576k | return ok; | 195 | 576k | } |
bool spvtools::utils::ParseNumber<long>(char const*, long*) Line | Count | Source | 168 | 7.42k | 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 | 7.42k | static_assert(sizeof(T) > 1, | 173 | 7.42k | "Single-byte types are not supported in this parse method"); | 174 | | | 175 | 7.42k | if (!text) return false; | 176 | 7.42k | 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 | 7.42k | text_stream >> std::setbase(0); | 180 | 7.42k | text_stream >> *value_pointer; | 181 | | | 182 | | // We should have read something. | 183 | 7.42k | bool ok = (text[0] != 0) && !text_stream.bad(); | 184 | | // It should have been all the text. | 185 | 7.42k | ok = ok && text_stream.eof(); | 186 | | // It should have been in range. | 187 | 7.42k | 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 | 7.42k | if (ok && text[0] == '-') | 192 | 7.24k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 193 | | | 194 | 7.42k | return ok; | 195 | 7.42k | } |
bool spvtools::utils::ParseNumber<unsigned long>(char const*, unsigned long*) Line | Count | Source | 168 | 7.98M | 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 | 7.98M | static_assert(sizeof(T) > 1, | 173 | 7.98M | "Single-byte types are not supported in this parse method"); | 174 | | | 175 | 7.98M | if (!text) return false; | 176 | 7.98M | 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 | 7.98M | text_stream >> std::setbase(0); | 180 | 7.98M | text_stream >> *value_pointer; | 181 | | | 182 | | // We should have read something. | 183 | 7.98M | bool ok = (text[0] != 0) && !text_stream.bad(); | 184 | | // It should have been all the text. | 185 | 7.98M | ok = ok && text_stream.eof(); | 186 | | // It should have been in range. | 187 | 7.98M | 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 | 7.98M | if (ok && text[0] == '-') | 192 | 0 | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 193 | | | 194 | 7.98M | return ok; | 195 | 7.98M | } |
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 | 168 | 34.2k | 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 | 34.2k | static_assert(sizeof(T) > 1, | 173 | 34.2k | "Single-byte types are not supported in this parse method"); | 174 | | | 175 | 34.2k | if (!text) return false; | 176 | 34.2k | 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 | 34.2k | text_stream >> std::setbase(0); | 180 | 34.2k | text_stream >> *value_pointer; | 181 | | | 182 | | // We should have read something. | 183 | 34.2k | bool ok = (text[0] != 0) && !text_stream.bad(); | 184 | | // It should have been all the text. | 185 | 34.2k | ok = ok && text_stream.eof(); | 186 | | // It should have been in range. | 187 | 34.2k | 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 | 34.2k | if (ok && text[0] == '-') | 192 | 7.38k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 193 | | | 194 | 34.2k | return ok; | 195 | 34.2k | } |
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 | 168 | 34.8k | 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 | 34.8k | static_assert(sizeof(T) > 1, | 173 | 34.8k | "Single-byte types are not supported in this parse method"); | 174 | | | 175 | 34.8k | if (!text) return false; | 176 | 34.8k | 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 | 34.8k | text_stream >> std::setbase(0); | 180 | 34.8k | text_stream >> *value_pointer; | 181 | | | 182 | | // We should have read something. | 183 | 34.8k | bool ok = (text[0] != 0) && !text_stream.bad(); | 184 | | // It should have been all the text. | 185 | 34.8k | ok = ok && text_stream.eof(); | 186 | | // It should have been in range. | 187 | 34.8k | 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 | 34.8k | if (ok && text[0] == '-') | 192 | 1.99k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 193 | | | 194 | 34.8k | return ok; | 195 | 34.8k | } |
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 | 168 | 21.2k | 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 | 21.2k | static_assert(sizeof(T) > 1, | 173 | 21.2k | "Single-byte types are not supported in this parse method"); | 174 | | | 175 | 21.2k | if (!text) return false; | 176 | 21.2k | 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 | 21.2k | text_stream >> std::setbase(0); | 180 | 21.2k | text_stream >> *value_pointer; | 181 | | | 182 | | // We should have read something. | 183 | 21.2k | bool ok = (text[0] != 0) && !text_stream.bad(); | 184 | | // It should have been all the text. | 185 | 21.2k | ok = ok && text_stream.eof(); | 186 | | // It should have been in range. | 187 | 21.2k | 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 | 21.2k | if (ok && text[0] == '-') | 192 | 2.68k | ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer); | 193 | | | 194 | 21.2k | return ok; | 195 | 21.2k | } |
|
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| function. 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_ |