• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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