1 #pragma once
2
3 // Copyright (C) 2002-2007 Nikolaus Gebhardt
4 // This file is part of the "Irrlicht Engine" and the "irrXML" project.
5 // For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h
6
7 // ------------------------------------------------------------------------------------
8 // Original description: (Schrompf)
9 // Adapted to the ASSIMP library because the builtin atof indeed takes AGES to parse a
10 // float inside a large string. Before parsing, it does a strlen on the given point.
11 // Changes:
12 // 22nd October 08 (Aramis_acg): Added temporary cast to double, added strtoul10_64
13 // to ensure long numbers are handled correctly
14 // ------------------------------------------------------------------------------------
15
16 #pragma once
17 #ifndef FAST_A_TO_F_H_INCLUDED
18 #define FAST_A_TO_F_H_INCLUDED
19
20 #ifdef __GNUC__
21 # pragma GCC system_header
22 #endif
23
24 #include <cmath>
25 #include <limits>
26 #include <stdint.h>
27 #include <stdexcept>
28 #include <assimp/defs.h>
29
30 #include "StringComparison.h"
31 #include <assimp/DefaultLogger.hpp>
32
33 #ifdef _MSC_VER
34 # include <stdint.h>
35 #else
36 # include <assimp/Compiler/pstdint.h>
37 #endif
38
39 namespace Assimp {
40
41 const double fast_atof_table[16] = { // we write [16] here instead of [] to work around a swig bug
42 0.0,
43 0.1,
44 0.01,
45 0.001,
46 0.0001,
47 0.00001,
48 0.000001,
49 0.0000001,
50 0.00000001,
51 0.000000001,
52 0.0000000001,
53 0.00000000001,
54 0.000000000001,
55 0.0000000000001,
56 0.00000000000001,
57 0.000000000000001
58 };
59
60
61 // ------------------------------------------------------------------------------------
62 // Convert a string in decimal format to a number
63 // ------------------------------------------------------------------------------------
64 inline
65 unsigned int strtoul10( const char* in, const char** out=0) {
66 unsigned int value = 0;
67
68 for ( ;; ) {
69 if ( *in < '0' || *in > '9' ) {
70 break;
71 }
72
73 value = ( value * 10 ) + ( *in - '0' );
74 ++in;
75 }
76 if ( out ) {
77 *out = in;
78 }
79 return value;
80 }
81
82 // ------------------------------------------------------------------------------------
83 // Convert a string in octal format to a number
84 // ------------------------------------------------------------------------------------
85 inline
86 unsigned int strtoul8( const char* in, const char** out=0) {
87 unsigned int value( 0 );
88 for ( ;; ) {
89 if ( *in < '0' || *in > '7' ) {
90 break;
91 }
92
93 value = ( value << 3 ) + ( *in - '0' );
94 ++in;
95 }
96 if ( out ) {
97 *out = in;
98 }
99 return value;
100 }
101
102 // ------------------------------------------------------------------------------------
103 // Convert a string in hex format to a number
104 // ------------------------------------------------------------------------------------
105 inline
106 unsigned int strtoul16( const char* in, const char** out=0) {
107 unsigned int value( 0 );
108 for ( ;; ) {
109 if ( *in >= '0' && *in <= '9' ) {
110 value = ( value << 4u ) + ( *in - '0' );
111 } else if (*in >= 'A' && *in <= 'F') {
112 value = ( value << 4u ) + ( *in - 'A' ) + 10;
113 } else if (*in >= 'a' && *in <= 'f') {
114 value = ( value << 4u ) + ( *in - 'a' ) + 10;
115 } else {
116 break;
117 }
118 ++in;
119 }
120 if ( out ) {
121 *out = in;
122 }
123 return value;
124 }
125
126 // ------------------------------------------------------------------------------------
127 // Convert just one hex digit
128 // Return value is UINT_MAX if the input character is not a hex digit.
129 // ------------------------------------------------------------------------------------
130 inline
HexDigitToDecimal(char in)131 unsigned int HexDigitToDecimal(char in) {
132 unsigned int out( UINT_MAX );
133 if ( in >= '0' && in <= '9' ) {
134 out = in - '0';
135 } else if ( in >= 'a' && in <= 'f' ) {
136 out = 10u + in - 'a';
137 } else if ( in >= 'A' && in <= 'F' ) {
138 out = 10u + in - 'A';
139 }
140
141 // return value is UINT_MAX if the input is not a hex digit
142 return out;
143 }
144
145 // ------------------------------------------------------------------------------------
146 // Convert a hex-encoded octet (2 characters, i.e. df or 1a).
147 // ------------------------------------------------------------------------------------
148 inline
HexOctetToDecimal(const char * in)149 uint8_t HexOctetToDecimal(const char* in) {
150 return ((uint8_t)HexDigitToDecimal(in[0])<<4)+(uint8_t)HexDigitToDecimal(in[1]);
151 }
152
153 // ------------------------------------------------------------------------------------
154 // signed variant of strtoul10
155 // ------------------------------------------------------------------------------------
156 inline
157 int strtol10( const char* in, const char** out=0) {
158 bool inv = (*in=='-');
159 if ( inv || *in == '+' ) {
160 ++in;
161 }
162
163 int value = strtoul10(in,out);
164 if (inv) {
165 value = -value;
166 }
167 return value;
168 }
169
170 // ------------------------------------------------------------------------------------
171 // Parse a C++-like integer literal - hex and oct prefixes.
172 // 0xNNNN - hex
173 // 0NNN - oct
174 // NNN - dec
175 // ------------------------------------------------------------------------------------
176 inline
177 unsigned int strtoul_cppstyle( const char* in, const char** out=0) {
178 if ('0' == in[0]) {
179 return 'x' == in[1] ? strtoul16(in+2,out) : strtoul8(in+1,out);
180 }
181 return strtoul10(in, out);
182 }
183
184 // ------------------------------------------------------------------------------------
185 // Special version of the function, providing higher accuracy and safety
186 // It is mainly used by fast_atof to prevent ugly and unwanted integer overflows.
187 // ------------------------------------------------------------------------------------
188 inline
189 uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_inout=0) {
190 unsigned int cur = 0;
191 uint64_t value = 0;
192
193 if ( *in < '0' || *in > '9' ) {
194 throw std::invalid_argument( std::string( "The string \"" ) + in + "\" cannot be converted into a value." );
195 }
196
197 for ( ;; ) {
198 if ( *in < '0' || *in > '9' ) {
199 break;
200 }
201
202 const uint64_t new_value = ( value * (uint64_t) 10 ) + ( (uint64_t) ( *in - '0' ) );
203
204 // numeric overflow, we rely on you
205 if ( new_value < value ) {
206 ASSIMP_LOG_WARN_F( "Converting the string \"", in, "\" into a value resulted in overflow." );
207 return 0;
208 }
209
210 value = new_value;
211
212 ++in;
213 ++cur;
214
215 if (max_inout && *max_inout == cur) {
216 if (out) { /* skip to end */
217 while ( *in >= '0' && *in <= '9' ) {
218 ++in;
219 }
220 *out = in;
221 }
222
223 return value;
224 }
225 }
226 if ( out ) {
227 *out = in;
228 }
229
230 if ( max_inout ) {
231 *max_inout = cur;
232 }
233
234 return value;
235 }
236
237 // ------------------------------------------------------------------------------------
238 // signed variant of strtoul10_64
239 // ------------------------------------------------------------------------------------
240 inline
241 int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* max_inout = 0) {
242 bool inv = (*in == '-');
243 if ( inv || *in == '+' ) {
244 ++in;
245 }
246
247 int64_t value = strtoul10_64(in, out, max_inout);
248 if (inv) {
249 value = -value;
250 }
251 return value;
252 }
253
254 // Number of relevant decimals for floating-point parsing.
255 #define AI_FAST_ATOF_RELAVANT_DECIMALS 15
256
257 // ------------------------------------------------------------------------------------
258 //! Provides a fast function for converting a string into a float,
259 //! about 6 times faster than atof in win32.
260 // If you find any bugs, please send them to me, niko (at) irrlicht3d.org.
261 // ------------------------------------------------------------------------------------
262 template<typename Real>
263 inline
264 const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) {
265 Real f = 0;
266
267 bool inv = (*c == '-');
268 if (inv || *c == '+') {
269 ++c;
270 }
271
272 if ((c[0] == 'N' || c[0] == 'n') && ASSIMP_strincmp(c, "nan", 3) == 0) {
273 out = std::numeric_limits<Real>::quiet_NaN();
274 c += 3;
275 return c;
276 }
277
278 if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inf", 3) == 0) {
279 out = std::numeric_limits<Real>::infinity();
280 if (inv) {
281 out = -out;
282 }
283 c += 3;
284 if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inity", 5) == 0) {
285 c += 5;
286 }
287 return c;
288 }
289
290 if (!(c[0] >= '0' && c[0] <= '9') &&
291 !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) {
292 throw std::invalid_argument("Cannot parse string "
293 "as real number: does not start with digit "
294 "or decimal point followed by digit.");
295 }
296
297 if (*c != '.' && (! check_comma || c[0] != ',')) {
298 f = static_cast<Real>( strtoul10_64 ( c, &c) );
299 }
300
301 if ((*c == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9') {
302 ++c;
303
304 // NOTE: The original implementation is highly inaccurate here. The precision of a single
305 // IEEE 754 float is not high enough, everything behind the 6th digit tends to be more
306 // inaccurate than it would need to be. Casting to double seems to solve the problem.
307 // strtol_64 is used to prevent integer overflow.
308
309 // Another fix: this tends to become 0 for long numbers if we don't limit the maximum
310 // number of digits to be read. AI_FAST_ATOF_RELAVANT_DECIMALS can be a value between
311 // 1 and 15.
312 unsigned int diff = AI_FAST_ATOF_RELAVANT_DECIMALS;
313 double pl = static_cast<double>( strtoul10_64 ( c, &c, &diff ));
314
315 pl *= fast_atof_table[diff];
316 f += static_cast<Real>( pl );
317 }
318 // For backwards compatibility: eat trailing dots, but not trailing commas.
319 else if (*c == '.') {
320 ++c;
321 }
322
323 // A major 'E' must be allowed. Necessary for proper reading of some DXF files.
324 // Thanks to Zhao Lei to point out that this if() must be outside the if (*c == '.' ..)
325 if (*c == 'e' || *c == 'E') {
326 ++c;
327 const bool einv = (*c=='-');
328 if (einv || *c=='+') {
329 ++c;
330 }
331
332 // The reason float constants are used here is that we've seen cases where compilers
333 // would perform such casts on compile-time constants at runtime, which would be
334 // bad considering how frequently fast_atoreal_move<float> is called in Assimp.
335 Real exp = static_cast<Real>( strtoul10_64(c, &c) );
336 if (einv) {
337 exp = -exp;
338 }
339 f *= std::pow(static_cast<Real>(10.0), exp);
340 }
341
342 if (inv) {
343 f = -f;
344 }
345 out = f;
346 return c;
347 }
348
349 // ------------------------------------------------------------------------------------
350 // The same but more human.
351 inline
fast_atof(const char * c)352 ai_real fast_atof(const char* c) {
353 ai_real ret(0.0);
354 fast_atoreal_move<ai_real>(c, ret);
355
356 return ret;
357 }
358
359 inline
fast_atof(const char * c,const char ** cout)360 ai_real fast_atof( const char* c, const char** cout) {
361 ai_real ret(0.0);
362 *cout = fast_atoreal_move<ai_real>(c, ret);
363
364 return ret;
365 }
366
367 inline
fast_atof(const char ** inout)368 ai_real fast_atof( const char** inout) {
369 ai_real ret(0.0);
370 *inout = fast_atoreal_move<ai_real>(*inout, ret);
371
372 return ret;
373 }
374
375 } //! namespace Assimp
376
377 #endif // FAST_A_TO_F_H_INCLUDED
378