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 #include "util/parse_number.h"
16
17 #include <functional>
18 #include <iomanip>
19 #include <memory>
20 #include <sstream>
21 #include <string>
22 #include <tuple>
23
24 #include "util/hex_float.h"
25
26 namespace spvutils {
27
28 namespace {
29 // A helper class that temporarily stores error messages and dump the messages
30 // to a string which given as as pointer when it is destructed. If the given
31 // pointer is a nullptr, this class does not store error message.
32 class ErrorMsgStream {
33 public:
ErrorMsgStream(std::string * error_msg_sink)34 explicit ErrorMsgStream(std::string* error_msg_sink)
35 : error_msg_sink_(error_msg_sink) {
36 if (error_msg_sink_) stream_.reset(new std::ostringstream());
37 }
~ErrorMsgStream()38 ~ErrorMsgStream() {
39 if (error_msg_sink_ && stream_) *error_msg_sink_ = stream_->str();
40 }
41 template <typename T>
operator <<(T val)42 ErrorMsgStream& operator<<(T val) {
43 if (stream_) *stream_ << val;
44 return *this;
45 }
46
47 private:
48 std::unique_ptr<std::ostringstream> stream_;
49 // The destination string to which this class dump the error message when
50 // destructor is called.
51 std::string* error_msg_sink_;
52 };
53 }
54
ParseAndEncodeIntegerNumber(const char * text,const NumberType & type,std::function<void (uint32_t)> emit,std::string * error_msg)55 EncodeNumberStatus ParseAndEncodeIntegerNumber(
56 const char* text, const NumberType& type,
57 std::function<void(uint32_t)> emit, std::string* error_msg) {
58 if (!text) {
59 ErrorMsgStream(error_msg) << "The given text is a nullptr";
60 return EncodeNumberStatus::kInvalidText;
61 }
62
63 if (!IsIntegral(type)) {
64 ErrorMsgStream(error_msg) << "The expected type is not a integer type";
65 return EncodeNumberStatus::kInvalidUsage;
66 }
67
68 const uint32_t bit_width = AssumedBitWidth(type);
69
70 if (bit_width > 64) {
71 ErrorMsgStream(error_msg) << "Unsupported " << bit_width
72 << "-bit integer literals";
73 return EncodeNumberStatus::kUnsupported;
74 }
75
76 // Either we are expecting anything or integer.
77 bool is_negative = text[0] == '-';
78 bool can_be_signed = IsSigned(type);
79
80 if (is_negative && !can_be_signed) {
81 ErrorMsgStream(error_msg)
82 << "Cannot put a negative number in an unsigned literal";
83 return EncodeNumberStatus::kInvalidUsage;
84 }
85
86 const bool is_hex = text[0] == '0' && (text[1] == 'x' || text[1] == 'X');
87
88 uint64_t decoded_bits;
89 if (is_negative) {
90 int64_t decoded_signed = 0;
91
92 if (!ParseNumber(text, &decoded_signed)) {
93 ErrorMsgStream(error_msg) << "Invalid signed integer literal: " << text;
94 return EncodeNumberStatus::kInvalidText;
95 }
96
97 if (!CheckRangeAndIfHexThenSignExtend(decoded_signed, type, is_hex,
98 &decoded_signed)) {
99 ErrorMsgStream(error_msg)
100 << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase
101 << decoded_signed << " does not fit in a " << std::dec << bit_width
102 << "-bit " << (IsSigned(type) ? "signed" : "unsigned") << " integer";
103 return EncodeNumberStatus::kInvalidText;
104 }
105 decoded_bits = decoded_signed;
106 } else {
107 // There's no leading minus sign, so parse it as an unsigned integer.
108 if (!ParseNumber(text, &decoded_bits)) {
109 ErrorMsgStream(error_msg) << "Invalid unsigned integer literal: " << text;
110 return EncodeNumberStatus::kInvalidText;
111 }
112 if (!CheckRangeAndIfHexThenSignExtend(decoded_bits, type, is_hex,
113 &decoded_bits)) {
114 ErrorMsgStream(error_msg)
115 << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase
116 << decoded_bits << " does not fit in a " << std::dec << bit_width
117 << "-bit " << (IsSigned(type) ? "signed" : "unsigned") << " integer";
118 return EncodeNumberStatus::kInvalidText;
119 }
120 }
121 if (bit_width > 32) {
122 uint32_t low = uint32_t(0x00000000ffffffff & decoded_bits);
123 uint32_t high = uint32_t((0xffffffff00000000 & decoded_bits) >> 32);
124 emit(low);
125 emit(high);
126 } else {
127 emit(uint32_t(decoded_bits));
128 }
129 return EncodeNumberStatus::kSuccess;
130 }
131
ParseAndEncodeFloatingPointNumber(const char * text,const NumberType & type,std::function<void (uint32_t)> emit,std::string * error_msg)132 EncodeNumberStatus ParseAndEncodeFloatingPointNumber(
133 const char* text, const NumberType& type,
134 std::function<void(uint32_t)> emit, std::string* error_msg) {
135 if (!text) {
136 ErrorMsgStream(error_msg) << "The given text is a nullptr";
137 return EncodeNumberStatus::kInvalidText;
138 }
139
140 if (!IsFloating(type)) {
141 ErrorMsgStream(error_msg) << "The expected type is not a float type";
142 return EncodeNumberStatus::kInvalidUsage;
143 }
144
145 const auto bit_width = AssumedBitWidth(type);
146 switch (bit_width) {
147 case 16: {
148 HexFloat<FloatProxy<spvutils::Float16>> hVal(0);
149 if (!ParseNumber(text, &hVal)) {
150 ErrorMsgStream(error_msg) << "Invalid 16-bit float literal: " << text;
151 return EncodeNumberStatus::kInvalidText;
152 }
153 // getAsFloat will return the spvutils::Float16 value, and get_value
154 // will return a uint16_t representing the bits of the float.
155 // The encoding is therefore correct from the perspective of the SPIR-V
156 // spec since the top 16 bits will be 0.
157 emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value()));
158 return EncodeNumberStatus::kSuccess;
159 } break;
160 case 32: {
161 HexFloat<FloatProxy<float>> fVal(0.0f);
162 if (!ParseNumber(text, &fVal)) {
163 ErrorMsgStream(error_msg) << "Invalid 32-bit float literal: " << text;
164 return EncodeNumberStatus::kInvalidText;
165 }
166 emit(BitwiseCast<uint32_t>(fVal));
167 return EncodeNumberStatus::kSuccess;
168 } break;
169 case 64: {
170 HexFloat<FloatProxy<double>> dVal(0.0);
171 if (!ParseNumber(text, &dVal)) {
172 ErrorMsgStream(error_msg) << "Invalid 64-bit float literal: " << text;
173 return EncodeNumberStatus::kInvalidText;
174 }
175 uint64_t decoded_val = BitwiseCast<uint64_t>(dVal);
176 uint32_t low = uint32_t(0x00000000ffffffff & decoded_val);
177 uint32_t high = uint32_t((0xffffffff00000000 & decoded_val) >> 32);
178 emit(low);
179 emit(high);
180 return EncodeNumberStatus::kSuccess;
181 } break;
182 default:
183 break;
184 }
185 ErrorMsgStream(error_msg) << "Unsupported " << bit_width
186 << "-bit float literals";
187 return EncodeNumberStatus::kUnsupported;
188 }
189
ParseAndEncodeNumber(const char * text,const NumberType & type,std::function<void (uint32_t)> emit,std::string * error_msg)190 EncodeNumberStatus ParseAndEncodeNumber(const char* text,
191 const NumberType& type,
192 std::function<void(uint32_t)> emit,
193 std::string* error_msg) {
194 if (!text) {
195 ErrorMsgStream(error_msg) << "The given text is a nullptr";
196 return EncodeNumberStatus::kInvalidText;
197 }
198
199 if (IsUnknown(type)) {
200 ErrorMsgStream(error_msg)
201 << "The expected type is not a integer or float type";
202 return EncodeNumberStatus::kInvalidUsage;
203 }
204
205 // If we explicitly expect a floating-point number, we should handle that
206 // first.
207 if (IsFloating(type)) {
208 return ParseAndEncodeFloatingPointNumber(text, type, emit, error_msg);
209 }
210
211 return ParseAndEncodeIntegerNumber(text, type, emit, error_msg);
212 }
213
214 } // namespace spvutils
215