/src/tinyobjloader/tiny_obj_loader.h
Line | Count | Source |
1 | | /* |
2 | | The MIT License (MIT) |
3 | | |
4 | | Copyright (c) 2012-Present, Syoyo Fujita and many contributors. |
5 | | |
6 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | | of this software and associated documentation files (the "Software"), to deal |
8 | | in the Software without restriction, including without limitation the rights |
9 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
10 | | copies of the Software, and to permit persons to whom the Software is |
11 | | furnished to do so, subject to the following conditions: |
12 | | |
13 | | The above copyright notice and this permission notice shall be included in |
14 | | all copies or substantial portions of the Software. |
15 | | |
16 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
22 | | THE SOFTWARE. |
23 | | */ |
24 | | |
25 | | // |
26 | | // version 2.0.0 : Add new object oriented API. 1.x API is still provided. |
27 | | // * Add python binding. |
28 | | // * Support line primitive. |
29 | | // * Support points primitive. |
30 | | // * Support multiple search path for .mtl(v1 API). |
31 | | // * Support vertex skinning weight `vw`(as an tinyobj |
32 | | // extension). Note that this differs vertex weight([w] |
33 | | // component in `v` line) |
34 | | // * Support escaped whitespece in mtllib |
35 | | // * Add robust triangulation using Mapbox |
36 | | // earcut(TINYOBJLOADER_USE_MAPBOX_EARCUT). |
37 | | // version 1.4.0 : Modifed ParseTextureNameAndOption API |
38 | | // version 1.3.1 : Make ParseTextureNameAndOption API public |
39 | | // version 1.3.0 : Separate warning and error message(breaking API of LoadObj) |
40 | | // version 1.2.3 : Added color space extension('-colorspace') to tex opts. |
41 | | // version 1.2.2 : Parse multiple group names. |
42 | | // version 1.2.1 : Added initial support for line('l') primitive(PR #178) |
43 | | // version 1.2.0 : Hardened implementation(#175) |
44 | | // version 1.1.1 : Support smoothing groups(#162) |
45 | | // version 1.1.0 : Support parsing vertex color(#144) |
46 | | // version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138) |
47 | | // version 1.0.7 : Support multiple tex options(#126) |
48 | | // version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124) |
49 | | // version 1.0.5 : Ignore `Tr` when `d` exists in MTL(#43) |
50 | | // version 1.0.4 : Support multiple filenames for 'mtllib'(#112) |
51 | | // version 1.0.3 : Support parsing texture options(#85) |
52 | | // version 1.0.2 : Improve parsing speed by about a factor of 2 for large |
53 | | // files(#105) |
54 | | // version 1.0.1 : Fixes a shape is lost if obj ends with a 'usemtl'(#104) |
55 | | // version 1.0.0 : Change data structure. Change license from BSD to MIT. |
56 | | // |
57 | | |
58 | | // |
59 | | // Use this in *one* .cc |
60 | | // #define TINYOBJLOADER_IMPLEMENTATION |
61 | | // #include "tiny_obj_loader.h" |
62 | | // |
63 | | |
64 | | #ifndef TINY_OBJ_LOADER_H_ |
65 | | #define TINY_OBJ_LOADER_H_ |
66 | | |
67 | | #include <map> |
68 | | #include <string> |
69 | | #include <vector> |
70 | | |
71 | | namespace tinyobj { |
72 | | |
73 | | // C++11 is now the minimum required standard. |
74 | | #if __cplusplus < 201103L && (!defined(_MSVC_LANG) || _MSVC_LANG < 201103L) |
75 | | #error "tinyobjloader requires C++11 or later. Compile with -std=c++11 or higher." |
76 | | #endif |
77 | | #define TINYOBJ_OVERRIDE override |
78 | | |
79 | | #ifdef __clang__ |
80 | | #pragma clang diagnostic push |
81 | | #if __has_warning("-Wzero-as-null-pointer-constant") |
82 | | #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" |
83 | | #endif |
84 | | |
85 | | #pragma clang diagnostic ignored "-Wpadded" |
86 | | |
87 | | #endif |
88 | | |
89 | | // https://en.wikipedia.org/wiki/Wavefront_.obj_file says ... |
90 | | // |
91 | | // -blendu on | off # set horizontal texture blending |
92 | | // (default on) |
93 | | // -blendv on | off # set vertical texture blending |
94 | | // (default on) |
95 | | // -boost real_value # boost mip-map sharpness |
96 | | // -mm base_value gain_value # modify texture map values (default |
97 | | // 0 1) |
98 | | // # base_value = brightness, |
99 | | // gain_value = contrast |
100 | | // -o u [v [w]] # Origin offset (default |
101 | | // 0 0 0) |
102 | | // -s u [v [w]] # Scale (default |
103 | | // 1 1 1) |
104 | | // -t u [v [w]] # Turbulence (default |
105 | | // 0 0 0) |
106 | | // -texres resolution # texture resolution to create |
107 | | // -clamp on | off # only render texels in the clamped |
108 | | // 0-1 range (default off) |
109 | | // # When unclamped, textures are |
110 | | // repeated across a surface, |
111 | | // # when clamped, only texels which |
112 | | // fall within the 0-1 |
113 | | // # range are rendered. |
114 | | // -bm mult_value # bump multiplier (for bump maps |
115 | | // only) |
116 | | // |
117 | | // -imfchan r | g | b | m | l | z # specifies which channel of the file |
118 | | // is used to |
119 | | // # create a scalar or bump texture. |
120 | | // r:red, g:green, |
121 | | // # b:blue, m:matte, l:luminance, |
122 | | // z:z-depth.. |
123 | | // # (the default for bump is 'l' and |
124 | | // for decal is 'm') |
125 | | // bump -imfchan r bumpmap.tga # says to use the red channel of |
126 | | // bumpmap.tga as the bumpmap |
127 | | // |
128 | | // For reflection maps... |
129 | | // |
130 | | // -type sphere # specifies a sphere for a "refl" |
131 | | // reflection map |
132 | | // -type cube_top | cube_bottom | # when using a cube map, the texture |
133 | | // file for each |
134 | | // cube_front | cube_back | # side of the cube is specified |
135 | | // separately |
136 | | // cube_left | cube_right |
137 | | // |
138 | | // TinyObjLoader extension. |
139 | | // |
140 | | // -colorspace SPACE # Color space of the texture. e.g. |
141 | | // 'sRGB` or 'linear' |
142 | | // |
143 | | |
144 | | #ifdef TINYOBJLOADER_USE_DOUBLE |
145 | | //#pragma message "using double" |
146 | | typedef double real_t; |
147 | | #else |
148 | | //#pragma message "using float" |
149 | | typedef float real_t; |
150 | | #endif |
151 | | |
152 | | typedef enum { |
153 | | TEXTURE_TYPE_NONE, // default |
154 | | TEXTURE_TYPE_SPHERE, |
155 | | TEXTURE_TYPE_CUBE_TOP, |
156 | | TEXTURE_TYPE_CUBE_BOTTOM, |
157 | | TEXTURE_TYPE_CUBE_FRONT, |
158 | | TEXTURE_TYPE_CUBE_BACK, |
159 | | TEXTURE_TYPE_CUBE_LEFT, |
160 | | TEXTURE_TYPE_CUBE_RIGHT |
161 | | } texture_type_t; |
162 | | |
163 | | struct texture_option_t { |
164 | | texture_type_t type; // -type (default TEXTURE_TYPE_NONE) |
165 | | real_t sharpness; // -boost (default 1.0?) |
166 | | real_t brightness; // base_value in -mm option (default 0) |
167 | | real_t contrast; // gain_value in -mm option (default 1) |
168 | | real_t origin_offset[3]; // -o u [v [w]] (default 0 0 0) |
169 | | real_t scale[3]; // -s u [v [w]] (default 1 1 1) |
170 | | real_t turbulence[3]; // -t u [v [w]] (default 0 0 0) |
171 | | int texture_resolution; // -texres resolution (No default value in the spec. |
172 | | // We'll use -1) |
173 | | bool clamp; // -clamp (default false) |
174 | | char imfchan; // -imfchan (the default for bump is 'l' and for decal is 'm') |
175 | | bool blendu; // -blendu (default on) |
176 | | bool blendv; // -blendv (default on) |
177 | | real_t bump_multiplier; // -bm (for bump maps only, default 1.0) |
178 | | |
179 | | // extension |
180 | | std::string colorspace; // Explicitly specify color space of stored texel |
181 | | // value. Usually `sRGB` or `linear` (default empty). |
182 | | }; |
183 | | |
184 | | struct material_t { |
185 | | std::string name; |
186 | | |
187 | | real_t ambient[3]; |
188 | | real_t diffuse[3]; |
189 | | real_t specular[3]; |
190 | | real_t transmittance[3]; |
191 | | real_t emission[3]; |
192 | | real_t shininess; |
193 | | real_t ior; // index of refraction |
194 | | real_t dissolve; // 1 == opaque; 0 == fully transparent |
195 | | // illumination model (see http://www.fileformat.info/format/material/) |
196 | | int illum; |
197 | | |
198 | | int dummy; // Suppress padding warning. |
199 | | |
200 | | std::string ambient_texname; // map_Ka. For ambient or ambient occlusion. |
201 | | std::string diffuse_texname; // map_Kd |
202 | | std::string specular_texname; // map_Ks |
203 | | std::string specular_highlight_texname; // map_Ns |
204 | | std::string bump_texname; // map_bump, map_Bump, bump |
205 | | std::string displacement_texname; // disp |
206 | | std::string alpha_texname; // map_d |
207 | | std::string reflection_texname; // refl |
208 | | |
209 | | texture_option_t ambient_texopt; |
210 | | texture_option_t diffuse_texopt; |
211 | | texture_option_t specular_texopt; |
212 | | texture_option_t specular_highlight_texopt; |
213 | | texture_option_t bump_texopt; |
214 | | texture_option_t displacement_texopt; |
215 | | texture_option_t alpha_texopt; |
216 | | texture_option_t reflection_texopt; |
217 | | |
218 | | // PBR extension |
219 | | // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr |
220 | | real_t roughness; // [0, 1] default 0 |
221 | | real_t metallic; // [0, 1] default 0 |
222 | | real_t sheen; // [0, 1] default 0 |
223 | | real_t clearcoat_thickness; // [0, 1] default 0 |
224 | | real_t clearcoat_roughness; // [0, 1] default 0 |
225 | | real_t anisotropy; // aniso. [0, 1] default 0 |
226 | | real_t anisotropy_rotation; // anisor. [0, 1] default 0 |
227 | | real_t pad0; |
228 | | std::string roughness_texname; // map_Pr |
229 | | std::string metallic_texname; // map_Pm |
230 | | std::string sheen_texname; // map_Ps |
231 | | std::string emissive_texname; // map_Ke |
232 | | std::string normal_texname; // norm. For normal mapping. |
233 | | |
234 | | texture_option_t roughness_texopt; |
235 | | texture_option_t metallic_texopt; |
236 | | texture_option_t sheen_texopt; |
237 | | texture_option_t emissive_texopt; |
238 | | texture_option_t normal_texopt; |
239 | | |
240 | | int pad2; |
241 | | |
242 | | std::map<std::string, std::string> unknown_parameter; |
243 | | |
244 | | #ifdef TINY_OBJ_LOADER_PYTHON_BINDING |
245 | | // For pybind11 |
246 | | std::array<double, 3> GetDiffuse() { |
247 | | std::array<double, 3> values; |
248 | | values[0] = double(diffuse[0]); |
249 | | values[1] = double(diffuse[1]); |
250 | | values[2] = double(diffuse[2]); |
251 | | |
252 | | return values; |
253 | | } |
254 | | |
255 | | std::array<double, 3> GetSpecular() { |
256 | | std::array<double, 3> values; |
257 | | values[0] = double(specular[0]); |
258 | | values[1] = double(specular[1]); |
259 | | values[2] = double(specular[2]); |
260 | | |
261 | | return values; |
262 | | } |
263 | | |
264 | | std::array<double, 3> GetTransmittance() { |
265 | | std::array<double, 3> values; |
266 | | values[0] = double(transmittance[0]); |
267 | | values[1] = double(transmittance[1]); |
268 | | values[2] = double(transmittance[2]); |
269 | | |
270 | | return values; |
271 | | } |
272 | | |
273 | | std::array<double, 3> GetEmission() { |
274 | | std::array<double, 3> values; |
275 | | values[0] = double(emission[0]); |
276 | | values[1] = double(emission[1]); |
277 | | values[2] = double(emission[2]); |
278 | | |
279 | | return values; |
280 | | } |
281 | | |
282 | | std::array<double, 3> GetAmbient() { |
283 | | std::array<double, 3> values; |
284 | | values[0] = double(ambient[0]); |
285 | | values[1] = double(ambient[1]); |
286 | | values[2] = double(ambient[2]); |
287 | | |
288 | | return values; |
289 | | } |
290 | | |
291 | | void SetDiffuse(std::array<double, 3> &a) { |
292 | | diffuse[0] = real_t(a[0]); |
293 | | diffuse[1] = real_t(a[1]); |
294 | | diffuse[2] = real_t(a[2]); |
295 | | } |
296 | | |
297 | | void SetAmbient(std::array<double, 3> &a) { |
298 | | ambient[0] = real_t(a[0]); |
299 | | ambient[1] = real_t(a[1]); |
300 | | ambient[2] = real_t(a[2]); |
301 | | } |
302 | | |
303 | | void SetSpecular(std::array<double, 3> &a) { |
304 | | specular[0] = real_t(a[0]); |
305 | | specular[1] = real_t(a[1]); |
306 | | specular[2] = real_t(a[2]); |
307 | | } |
308 | | |
309 | | void SetTransmittance(std::array<double, 3> &a) { |
310 | | transmittance[0] = real_t(a[0]); |
311 | | transmittance[1] = real_t(a[1]); |
312 | | transmittance[2] = real_t(a[2]); |
313 | | } |
314 | | |
315 | | std::string GetCustomParameter(const std::string &key) { |
316 | | std::map<std::string, std::string>::const_iterator it = |
317 | | unknown_parameter.find(key); |
318 | | |
319 | | if (it != unknown_parameter.end()) { |
320 | | return it->second; |
321 | | } |
322 | | return std::string(); |
323 | | } |
324 | | |
325 | | #endif |
326 | | }; |
327 | | |
328 | | struct tag_t { |
329 | | std::string name; |
330 | | |
331 | | std::vector<int> intValues; |
332 | | std::vector<real_t> floatValues; |
333 | | std::vector<std::string> stringValues; |
334 | | }; |
335 | | |
336 | | struct joint_and_weight_t { |
337 | | int joint_id; |
338 | | real_t weight; |
339 | | }; |
340 | | |
341 | | struct skin_weight_t { |
342 | | int vertex_id; // Corresponding vertex index in `attrib_t::vertices`. |
343 | | // Compared to `index_t`, this index must be positive and |
344 | | // start with 0(does not allow relative indexing) |
345 | | std::vector<joint_and_weight_t> weightValues; |
346 | | }; |
347 | | |
348 | | // Index struct to support different indices for vtx/normal/texcoord. |
349 | | // -1 means not used. |
350 | | struct index_t { |
351 | | int vertex_index; |
352 | | int normal_index; |
353 | | int texcoord_index; |
354 | | }; |
355 | | |
356 | | struct mesh_t { |
357 | | std::vector<index_t> indices; |
358 | | std::vector<unsigned int> |
359 | | num_face_vertices; // The number of vertices per |
360 | | // face. 3 = triangle, 4 = quad, ... |
361 | | std::vector<int> material_ids; // per-face material ID |
362 | | std::vector<unsigned int> smoothing_group_ids; // per-face smoothing group |
363 | | // ID(0 = off. positive value |
364 | | // = group id) |
365 | | std::vector<tag_t> tags; // SubD tag |
366 | | }; |
367 | | |
368 | | // struct path_t { |
369 | | // std::vector<int> indices; // pairs of indices for lines |
370 | | //}; |
371 | | |
372 | | struct lines_t { |
373 | | // Linear flattened indices. |
374 | | std::vector<index_t> indices; // indices for vertices(poly lines) |
375 | | std::vector<int> num_line_vertices; // The number of vertices per line. |
376 | | }; |
377 | | |
378 | | struct points_t { |
379 | | std::vector<index_t> indices; // indices for points |
380 | | }; |
381 | | |
382 | | struct shape_t { |
383 | | std::string name; |
384 | | mesh_t mesh; |
385 | | lines_t lines; |
386 | | points_t points; |
387 | | }; |
388 | | |
389 | | // Vertex attributes |
390 | | struct attrib_t { |
391 | | std::vector<real_t> vertices; // 'v'(xyz) |
392 | | |
393 | | // For backward compatibility, we store vertex weight in separate array. |
394 | | std::vector<real_t> vertex_weights; // 'v'(w) |
395 | | std::vector<real_t> normals; // 'vn' |
396 | | std::vector<real_t> texcoords; // 'vt'(uv) |
397 | | |
398 | | // For backward compatibility, we store texture coordinate 'w' in separate |
399 | | // array. |
400 | | std::vector<real_t> texcoord_ws; // 'vt'(w) |
401 | | std::vector<real_t> colors; // extension: vertex colors |
402 | | |
403 | | // |
404 | | // TinyObj extension. |
405 | | // |
406 | | |
407 | | // NOTE(syoyo): array index is based on the appearance order. |
408 | | // To get a corresponding skin weight for a specific vertex id `vid`, |
409 | | // Need to reconstruct a look up table: `skin_weight_t::vertex_id` == `vid` |
410 | | // (e.g. using std::map, std::unordered_map) |
411 | | std::vector<skin_weight_t> skin_weights; |
412 | | |
413 | 8.56k | attrib_t() {} |
414 | | |
415 | | // |
416 | | // For pybind11 |
417 | | // |
418 | 0 | const std::vector<real_t> &GetVertices() const { return vertices; } |
419 | | |
420 | 0 | const std::vector<real_t> &GetVertexWeights() const { return vertex_weights; } |
421 | | }; |
422 | | |
423 | | struct callback_t { |
424 | | // W is optional and set to 1 if there is no `w` item in `v` line |
425 | | void (*vertex_cb)(void *user_data, real_t x, real_t y, real_t z, real_t w); |
426 | | void (*vertex_color_cb)(void *user_data, real_t x, real_t y, real_t z, |
427 | | real_t r, real_t g, real_t b, bool has_color); |
428 | | void (*normal_cb)(void *user_data, real_t x, real_t y, real_t z); |
429 | | |
430 | | // y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in |
431 | | // `vt` line. |
432 | | void (*texcoord_cb)(void *user_data, real_t x, real_t y, real_t z); |
433 | | |
434 | | // called per 'f' line. num_indices is the number of face indices(e.g. 3 for |
435 | | // triangle, 4 for quad) |
436 | | // 0 will be passed for undefined index in index_t members. |
437 | | void (*index_cb)(void *user_data, index_t *indices, int num_indices); |
438 | | // `name` material name, `material_id` = the array index of material_t[]. -1 |
439 | | // if |
440 | | // a material not found in .mtl |
441 | | void (*usemtl_cb)(void *user_data, const char *name, int material_id); |
442 | | // `materials` = parsed material data. |
443 | | void (*mtllib_cb)(void *user_data, const material_t *materials, |
444 | | int num_materials); |
445 | | // There may be multiple group names |
446 | | void (*group_cb)(void *user_data, const char **names, int num_names); |
447 | | void (*object_cb)(void *user_data, const char *name); |
448 | | |
449 | | callback_t() |
450 | | : vertex_cb(NULL), |
451 | | vertex_color_cb(NULL), |
452 | | normal_cb(NULL), |
453 | | texcoord_cb(NULL), |
454 | | index_cb(NULL), |
455 | | usemtl_cb(NULL), |
456 | | mtllib_cb(NULL), |
457 | | group_cb(NULL), |
458 | 0 | object_cb(NULL) {} |
459 | | }; |
460 | | |
461 | | class MaterialReader { |
462 | | public: |
463 | 8.54k | MaterialReader() {} |
464 | | virtual ~MaterialReader(); |
465 | | |
466 | | virtual bool operator()(const std::string &matId, |
467 | | std::vector<material_t> *materials, |
468 | | std::map<std::string, int> *matMap, std::string *warn, |
469 | | std::string *err) = 0; |
470 | | }; |
471 | | |
472 | | /// |
473 | | /// Read .mtl from a file. |
474 | | /// |
475 | | class MaterialFileReader : public MaterialReader { |
476 | | public: |
477 | | // Path could contain separator(';' in Windows, ':' in Posix) |
478 | | explicit MaterialFileReader(const std::string &mtl_basedir) |
479 | 0 | : m_mtlBaseDir(mtl_basedir) {} |
480 | 0 | virtual ~MaterialFileReader() TINYOBJ_OVERRIDE {} |
481 | | virtual bool operator()(const std::string &matId, |
482 | | std::vector<material_t> *materials, |
483 | | std::map<std::string, int> *matMap, std::string *warn, |
484 | | std::string *err) TINYOBJ_OVERRIDE; |
485 | | |
486 | | private: |
487 | | std::string m_mtlBaseDir; |
488 | | }; |
489 | | |
490 | | /// |
491 | | /// Read .mtl from a stream. |
492 | | /// |
493 | | class MaterialStreamReader : public MaterialReader { |
494 | | public: |
495 | | explicit MaterialStreamReader(std::istream &inStream) |
496 | 8.54k | : m_inStream(inStream) {} |
497 | 0 | virtual ~MaterialStreamReader() TINYOBJ_OVERRIDE {} |
498 | | virtual bool operator()(const std::string &matId, |
499 | | std::vector<material_t> *materials, |
500 | | std::map<std::string, int> *matMap, std::string *warn, |
501 | | std::string *err) TINYOBJ_OVERRIDE; |
502 | | |
503 | | private: |
504 | | std::istream &m_inStream; |
505 | | }; |
506 | | |
507 | | // v2 API |
508 | | struct ObjReaderConfig { |
509 | | bool triangulate; // triangulate polygon? |
510 | | |
511 | | // Currently not used. |
512 | | // "simple" or empty: Create triangle fan |
513 | | // "earcut": Use the algorithm based on Ear clipping |
514 | | std::string triangulation_method; |
515 | | |
516 | | /// Parse vertex color. |
517 | | /// If vertex color is not present, its filled with default value. |
518 | | /// false = no vertex color |
519 | | /// This will increase memory of parsed .obj |
520 | | bool vertex_color; |
521 | | |
522 | | /// |
523 | | /// Search path to .mtl file. |
524 | | /// Default = "" = search from the same directory of .obj file. |
525 | | /// Valid only when loading .obj from a file. |
526 | | /// |
527 | | std::string mtl_search_path; |
528 | | |
529 | | ObjReaderConfig() |
530 | 8.56k | : triangulate(true), triangulation_method("simple"), vertex_color(true) {} |
531 | | }; |
532 | | |
533 | | /// |
534 | | /// Wavefront .obj reader class(v2 API) |
535 | | /// |
536 | | class ObjReader { |
537 | | public: |
538 | 8.56k | ObjReader() : valid_(false) {} |
539 | | |
540 | | /// |
541 | | /// Load .obj and .mtl from a file. |
542 | | /// |
543 | | /// @param[in] filename wavefront .obj filename |
544 | | /// @param[in] config Reader configuration |
545 | | /// |
546 | | bool ParseFromFile(const std::string &filename, |
547 | | const ObjReaderConfig &config = ObjReaderConfig()); |
548 | | |
549 | | /// |
550 | | /// Parse .obj from a text string. |
551 | | /// Need to supply .mtl text string by `mtl_text`. |
552 | | /// This function ignores `mtllib` line in .obj text. |
553 | | /// |
554 | | /// @param[in] obj_text wavefront .obj filename |
555 | | /// @param[in] mtl_text wavefront .mtl filename |
556 | | /// @param[in] config Reader configuration |
557 | | /// |
558 | | bool ParseFromString(const std::string &obj_text, const std::string &mtl_text, |
559 | | const ObjReaderConfig &config = ObjReaderConfig()); |
560 | | |
561 | | /// |
562 | | /// .obj was loaded or parsed correctly. |
563 | | /// |
564 | 0 | bool Valid() const { return valid_; } |
565 | | |
566 | 0 | const attrib_t &GetAttrib() const { return attrib_; } |
567 | | |
568 | 0 | const std::vector<shape_t> &GetShapes() const { return shapes_; } |
569 | | |
570 | 0 | const std::vector<material_t> &GetMaterials() const { return materials_; } |
571 | | |
572 | | /// |
573 | | /// Warning message(may be filled after `Load` or `Parse`) |
574 | | /// |
575 | 0 | const std::string &Warning() const { return warning_; } |
576 | | |
577 | | /// |
578 | | /// Error message(filled when `Load` or `Parse` failed) |
579 | | /// |
580 | 0 | const std::string &Error() const { return error_; } |
581 | | |
582 | | private: |
583 | | bool valid_; |
584 | | |
585 | | attrib_t attrib_; |
586 | | std::vector<shape_t> shapes_; |
587 | | std::vector<material_t> materials_; |
588 | | |
589 | | std::string warning_; |
590 | | std::string error_; |
591 | | }; |
592 | | |
593 | | /// ==>>========= Legacy v1 API ============================================= |
594 | | |
595 | | /// Loads .obj from a file. |
596 | | /// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data |
597 | | /// 'shapes' will be filled with parsed shape data |
598 | | /// Returns true when loading .obj become success. |
599 | | /// Returns warning message into `warn`, and error message into `err` |
600 | | /// 'mtl_basedir' is optional, and used for base directory for .mtl file. |
601 | | /// In default(`NULL'), .mtl file is searched from an application's working |
602 | | /// directory. |
603 | | /// 'triangulate' is optional, and used whether triangulate polygon face in .obj |
604 | | /// or not. |
605 | | /// Option 'default_vcols_fallback' specifies whether vertex colors should |
606 | | /// always be defined, even if no colors are given (fallback to white). |
607 | | bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes, |
608 | | std::vector<material_t> *materials, std::string *warn, |
609 | | std::string *err, const char *filename, |
610 | | const char *mtl_basedir = NULL, bool triangulate = true, |
611 | | bool default_vcols_fallback = true); |
612 | | |
613 | | /// Loads .obj from a file with custom user callback. |
614 | | /// .mtl is loaded as usual and parsed material_t data will be passed to |
615 | | /// `callback.mtllib_cb`. |
616 | | /// Returns true when loading .obj/.mtl become success. |
617 | | /// Returns warning message into `warn`, and error message into `err` |
618 | | /// See `examples/callback_api/` for how to use this function. |
619 | | bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, |
620 | | void *user_data = NULL, |
621 | | MaterialReader *readMatFn = NULL, |
622 | | std::string *warn = NULL, std::string *err = NULL); |
623 | | |
624 | | /// Loads object from a std::istream, uses `readMatFn` to retrieve |
625 | | /// std::istream for materials. |
626 | | /// Returns true when loading .obj become success. |
627 | | /// Returns warning and error message into `err` |
628 | | bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes, |
629 | | std::vector<material_t> *materials, std::string *warn, |
630 | | std::string *err, std::istream *inStream, |
631 | | MaterialReader *readMatFn = NULL, bool triangulate = true, |
632 | | bool default_vcols_fallback = true); |
633 | | |
634 | | /// Loads materials into std::map |
635 | | void LoadMtl(std::map<std::string, int> *material_map, |
636 | | std::vector<material_t> *materials, std::istream *inStream, |
637 | | std::string *warning, std::string *err); |
638 | | |
639 | | /// |
640 | | /// Parse texture name and texture option for custom texture parameter through |
641 | | /// material::unknown_parameter |
642 | | /// |
643 | | /// @param[out] texname Parsed texture name |
644 | | /// @param[out] texopt Parsed texopt |
645 | | /// @param[in] linebuf Input string |
646 | | /// |
647 | | bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt, |
648 | | const char *linebuf); |
649 | | |
650 | | /// =<<========== Legacy v1 API ============================================= |
651 | | |
652 | | } // namespace tinyobj |
653 | | |
654 | | #endif // TINY_OBJ_LOADER_H_ |
655 | | |
656 | | #ifdef TINYOBJLOADER_IMPLEMENTATION |
657 | | #include <cassert> |
658 | | #include <cctype> |
659 | | #include <climits> |
660 | | #include <cmath> |
661 | | #include <cstddef> |
662 | | #include <cstdint> |
663 | | #include <cerrno> |
664 | | #include <cstdlib> |
665 | | #include <cstring> |
666 | | #include <fstream> |
667 | | #include <limits> |
668 | | |
669 | | #ifdef _WIN32 |
670 | | #ifndef WIN32_LEAN_AND_MEAN |
671 | | #define WIN32_LEAN_AND_MEAN |
672 | | #endif |
673 | | #ifndef NOMINMAX |
674 | | #define NOMINMAX |
675 | | #endif |
676 | | #include <windows.h> |
677 | | #endif |
678 | | |
679 | | #ifdef TINYOBJLOADER_USE_MMAP |
680 | | #if !defined(_WIN32) |
681 | | // POSIX headers for mmap |
682 | | #include <fcntl.h> |
683 | | #include <sys/mman.h> |
684 | | #include <sys/stat.h> |
685 | | #include <unistd.h> |
686 | | #endif |
687 | | #endif // TINYOBJLOADER_USE_MMAP |
688 | | #include <set> |
689 | | #include <sstream> |
690 | | #include <utility> |
691 | | |
692 | | #ifdef TINYOBJLOADER_USE_MAPBOX_EARCUT |
693 | | |
694 | | #ifdef TINYOBJLOADER_DONOT_INCLUDE_MAPBOX_EARCUT |
695 | | // Assume earcut.hpp is included outside of tiny_obj_loader.h |
696 | | #else |
697 | | |
698 | | #ifdef __clang__ |
699 | | #pragma clang diagnostic push |
700 | | #pragma clang diagnostic ignored "-Weverything" |
701 | | #endif |
702 | | |
703 | | #include <array> |
704 | | |
705 | | #include "mapbox/earcut.hpp" |
706 | | |
707 | | #ifdef __clang__ |
708 | | #pragma clang diagnostic pop |
709 | | #endif |
710 | | |
711 | | #endif |
712 | | |
713 | | #endif // TINYOBJLOADER_USE_MAPBOX_EARCUT |
714 | | |
715 | | #ifdef _WIN32 |
716 | | // Converts a UTF-8 encoded string to a UTF-16 wide string for use with |
717 | | // Windows file APIs that support Unicode paths (including paths longer than |
718 | | // MAX_PATH when combined with the extended-length path prefix). |
719 | | static std::wstring UTF8ToWchar(const std::string &str) { |
720 | | if (str.empty()) return std::wstring(); |
721 | | int size_needed = |
722 | | MultiByteToWideChar(CP_UTF8, 0, str.c_str(), |
723 | | static_cast<int>(str.size()), NULL, 0); |
724 | | if (size_needed == 0) return std::wstring(); |
725 | | std::wstring wstr(static_cast<size_t>(size_needed), L'\0'); |
726 | | int result = |
727 | | MultiByteToWideChar(CP_UTF8, 0, str.c_str(), |
728 | | static_cast<int>(str.size()), &wstr[0], size_needed); |
729 | | if (result == 0) return std::wstring(); |
730 | | return wstr; |
731 | | } |
732 | | |
733 | | // Prepends the Windows extended-length path prefix ("\\?\") to an absolute |
734 | | // path when the path length meets or exceeds MAX_PATH (260 characters). |
735 | | // This allows Windows APIs to handle paths up to 32767 characters long. |
736 | | // UNC paths (starting with "\\") are converted to "\\?\UNC\" form. |
737 | | static std::wstring LongPathW(const std::wstring &wpath) { |
738 | | const std::wstring kLongPathPrefix = L"\\\\?\\"; |
739 | | const std::wstring kUNCPrefix = L"\\\\"; |
740 | | const std::wstring kLongUNCPathPrefix = L"\\\\?\\UNC\\"; |
741 | | |
742 | | // Already has the extended-length prefix; return as-is. |
743 | | if (wpath.size() >= kLongPathPrefix.size() && |
744 | | wpath.substr(0, kLongPathPrefix.size()) == kLongPathPrefix) { |
745 | | return wpath; |
746 | | } |
747 | | |
748 | | // Only add the prefix when the path is long enough to require it. |
749 | | if (wpath.size() < MAX_PATH) { |
750 | | return wpath; |
751 | | } |
752 | | |
753 | | // Normalize forward slashes to backslashes: the extended-length "\\?\" |
754 | | // prefix requires backslash separators only. |
755 | | std::wstring normalized = wpath; |
756 | | for (std::wstring::size_type i = 0; i < normalized.size(); ++i) { |
757 | | if (normalized[i] == L'/') normalized[i] = L'\\'; |
758 | | } |
759 | | |
760 | | // UNC path: "\\server\share\..." -> "\\?\UNC\server\share\..." |
761 | | if (normalized.size() >= kUNCPrefix.size() && |
762 | | normalized.substr(0, kUNCPrefix.size()) == kUNCPrefix) { |
763 | | return kLongUNCPathPrefix + normalized.substr(kUNCPrefix.size()); |
764 | | } |
765 | | |
766 | | // Absolute path with drive letter: "C:\..." -> "\\?\C:\..." |
767 | | if (normalized.size() >= 2 && normalized[1] == L':') { |
768 | | return kLongPathPrefix + normalized; |
769 | | } |
770 | | |
771 | | return normalized; |
772 | | } |
773 | | #endif // _WIN32 |
774 | | |
775 | | // -------------------------------------------------------------------------- |
776 | | // Embedded fast_float v8.0.2 for high-performance, bit-exact float parsing. |
777 | | // Disable by defining TINYOBJLOADER_DISABLE_FAST_FLOAT before including |
778 | | // this file with TINYOBJLOADER_IMPLEMENTATION. |
779 | | // -------------------------------------------------------------------------- |
780 | | #ifndef TINYOBJLOADER_DISABLE_FAST_FLOAT |
781 | | |
782 | | // Standard headers needed by the embedded fast_float. |
783 | | #include <cfloat> |
784 | | #include <cstdint> |
785 | | |
786 | | namespace tinyobj_ff { |
787 | | |
788 | | // --- integral_constant, true_type, false_type --- |
789 | | template <typename T, T V> |
790 | | struct integral_constant { |
791 | | static const T value = V; |
792 | | typedef T value_type; |
793 | | typedef integral_constant type; |
794 | | operator value_type() const { return value; } |
795 | | }; |
796 | | typedef integral_constant<bool, true> true_type; |
797 | | typedef integral_constant<bool, false> false_type; |
798 | | |
799 | | // --- is_same --- |
800 | | template <typename T, typename U> struct is_same : false_type {}; |
801 | | template <typename T> struct is_same<T, T> : true_type {}; |
802 | | |
803 | | // --- enable_if --- |
804 | | template <bool B, typename T = void> struct enable_if {}; |
805 | | template <typename T> struct enable_if<true, T> { typedef T type; }; |
806 | | |
807 | | // --- conditional --- |
808 | | template <bool B, typename T, typename F> struct conditional { typedef T type; }; |
809 | | template <typename T, typename F> struct conditional<false, T, F> { typedef F type; }; |
810 | | |
811 | | // --- is_integral --- |
812 | | template <typename T> struct is_integral : false_type {}; |
813 | | template <> struct is_integral<bool> : true_type {}; |
814 | | template <> struct is_integral<char> : true_type {}; |
815 | | template <> struct is_integral<signed char> : true_type {}; |
816 | | template <> struct is_integral<unsigned char> : true_type {}; |
817 | | template <> struct is_integral<short> : true_type {}; |
818 | | template <> struct is_integral<unsigned short> : true_type {}; |
819 | | template <> struct is_integral<int> : true_type {}; |
820 | | template <> struct is_integral<unsigned int> : true_type {}; |
821 | | template <> struct is_integral<long> : true_type {}; |
822 | | template <> struct is_integral<unsigned long> : true_type {}; |
823 | | template <> struct is_integral<long long> : true_type {}; |
824 | | template <> struct is_integral<unsigned long long> : true_type {}; |
825 | | template <> struct is_integral<wchar_t> : true_type {}; |
826 | | template <> struct is_integral<char16_t> : true_type {}; |
827 | | template <> struct is_integral<char32_t> : true_type {}; |
828 | | |
829 | | // --- is_signed --- |
830 | | template <typename T> struct is_signed : integral_constant<bool, T(-1) < T(0)> {}; |
831 | | |
832 | | // --- underlying_type (uses compiler builtin) --- |
833 | | template <typename T> struct underlying_type { |
834 | | typedef __underlying_type(T) type; |
835 | | }; |
836 | | |
837 | | // --- ff_errc (replaces std::errc, our own enum - no system_error needed) --- |
838 | | enum class ff_errc { ok = 0, invalid_argument = 22, result_out_of_range = 34 }; |
839 | | |
840 | | // --- min_val (replaces std::min, avoids Windows min/max macro conflicts) --- |
841 | | template <typename T> |
842 | 0 | inline T min_val(T a, T b) { return (b < a) ? b : a; } |
843 | | |
844 | | // --- copy_n --- |
845 | | template <typename InputIt, typename Size, typename OutputIt> |
846 | 0 | inline OutputIt copy_n(InputIt first, Size count, OutputIt result) { |
847 | 0 | for (Size i = 0; i < count; ++i) *result++ = *first++; |
848 | 0 | return result; |
849 | 0 | } |
850 | | |
851 | | // --- copy_backward --- |
852 | | template <typename BidirIt1, typename BidirIt2> |
853 | 0 | inline BidirIt2 copy_backward(BidirIt1 first, BidirIt1 last, BidirIt2 d_last) { |
854 | 0 | while (first != last) *(--d_last) = *(--last); |
855 | 0 | return d_last; |
856 | 0 | } |
857 | | |
858 | | // --- fill --- |
859 | | template <typename ForwardIt, typename T> |
860 | 0 | inline void fill(ForwardIt first, ForwardIt last, const T &value) { |
861 | 0 | for (; first != last; ++first) *first = value; |
862 | 0 | } Unexecuted instantiation: void tinyobj_ff::fill<unsigned long*, unsigned long>(unsigned long*, unsigned long*, unsigned long const&) Unexecuted instantiation: void tinyobj_ff::fill<unsigned long*, int>(unsigned long*, unsigned long*, int const&) |
863 | | |
864 | | // --- distance --- |
865 | | template <typename It> |
866 | | inline typename conditional<true, long long, It>::type |
867 | 602 | distance(It first, It last) { |
868 | 602 | return last - first; |
869 | 602 | } |
870 | | |
871 | | } // namespace tinyobj_ff |
872 | | |
873 | | // --- Begin embedded fast_float v8.0.2 (MIT / Apache-2.0 / BSL-1.0) --- |
874 | | // https://github.com/fastfloat/fast_float |
875 | | // fast_float by Daniel Lemire |
876 | | // fast_float by João Paulo Magalhaes |
877 | | // |
878 | | // |
879 | | // with contributions from Eugene Golushkov |
880 | | // with contributions from Maksim Kita |
881 | | // with contributions from Marcin Wojdyr |
882 | | // with contributions from Neal Richardson |
883 | | // with contributions from Tim Paine |
884 | | // with contributions from Fabio Pellacini |
885 | | // with contributions from Lénárd Szolnoki |
886 | | // with contributions from Jan Pharago |
887 | | // with contributions from Maya Warrier |
888 | | // with contributions from Taha Khokhar |
889 | | // with contributions from Anders Dalvander |
890 | | // |
891 | | // |
892 | | // Licensed under the Apache License, Version 2.0, or the |
893 | | // MIT License or the Boost License. This file may not be copied, |
894 | | // modified, or distributed except according to those terms. |
895 | | // |
896 | | // MIT License Notice |
897 | | // |
898 | | // MIT License |
899 | | // |
900 | | // Copyright (c) 2021 The fast_float authors |
901 | | // |
902 | | // Permission is hereby granted, free of charge, to any |
903 | | // person obtaining a copy of this software and associated |
904 | | // documentation files (the "Software"), to deal in the |
905 | | // Software without restriction, including without |
906 | | // limitation the rights to use, copy, modify, merge, |
907 | | // publish, distribute, sublicense, and/or sell copies of |
908 | | // the Software, and to permit persons to whom the Software |
909 | | // is furnished to do so, subject to the following |
910 | | // conditions: |
911 | | // |
912 | | // The above copyright notice and this permission notice |
913 | | // shall be included in all copies or substantial portions |
914 | | // of the Software. |
915 | | // |
916 | | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF |
917 | | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED |
918 | | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A |
919 | | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
920 | | // SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
921 | | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
922 | | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR |
923 | | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
924 | | // DEALINGS IN THE SOFTWARE. |
925 | | // |
926 | | // Apache License (Version 2.0) Notice |
927 | | // |
928 | | // Copyright 2021 The fast_float authors |
929 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
930 | | // you may not use this file except in compliance with the License. |
931 | | // You may obtain a copy of the License at |
932 | | // |
933 | | // http://www.apache.org/licenses/LICENSE-2.0 |
934 | | // |
935 | | // Unless required by applicable law or agreed to in writing, software |
936 | | // distributed under the License is distributed on an "AS IS" BASIS, |
937 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
938 | | // See the License for the specific language governing permissions and |
939 | | // |
940 | | // BOOST License Notice |
941 | | // |
942 | | // Boost Software License - Version 1.0 - August 17th, 2003 |
943 | | // |
944 | | // Permission is hereby granted, free of charge, to any person or organization |
945 | | // obtaining a copy of the software and accompanying documentation covered by |
946 | | // this license (the "Software") to use, reproduce, display, distribute, |
947 | | // execute, and transmit the Software, and to prepare derivative works of the |
948 | | // Software, and to permit third-parties to whom the Software is furnished to |
949 | | // do so, all subject to the following: |
950 | | // |
951 | | // The copyright notices in the Software and this entire statement, including |
952 | | // the above license grant, this restriction and the following disclaimer, |
953 | | // must be included in all copies of the Software, in whole or in part, and |
954 | | // all derivative works of the Software, unless such copies or derivative |
955 | | // works are solely in the form of machine-executable object code generated by |
956 | | // a source language processor. |
957 | | // |
958 | | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
959 | | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
960 | | // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT |
961 | | // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE |
962 | | // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, |
963 | | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
964 | | // DEALINGS IN THE SOFTWARE. |
965 | | // |
966 | | |
967 | | #ifndef FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H |
968 | | #define FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H |
969 | | |
970 | | #ifdef __has_include |
971 | | #if __has_include(<version>) |
972 | | #include <version> |
973 | | #endif |
974 | | #endif |
975 | | |
976 | | // Testing for https://wg21.link/N3652, adopted in C++14 |
977 | | #if defined(__cpp_constexpr) && __cpp_constexpr >= 201304 |
978 | | #define FASTFLOAT_CONSTEXPR14 constexpr |
979 | | #else |
980 | | #define FASTFLOAT_CONSTEXPR14 |
981 | | #endif |
982 | | |
983 | | #if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L |
984 | | #define FASTFLOAT_HAS_BIT_CAST 1 |
985 | | #else |
986 | | #define FASTFLOAT_HAS_BIT_CAST 0 |
987 | | #endif |
988 | | |
989 | | #if defined(__cpp_lib_is_constant_evaluated) && \ |
990 | | __cpp_lib_is_constant_evaluated >= 201811L |
991 | | #define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1 |
992 | | #else |
993 | | #define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0 |
994 | | #endif |
995 | | |
996 | | #if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L |
997 | | #define FASTFLOAT_IF_CONSTEXPR17(x) if constexpr (x) |
998 | | #else |
999 | 9.50k | #define FASTFLOAT_IF_CONSTEXPR17(x) if (x) |
1000 | | #endif |
1001 | | |
1002 | | // Testing for relevant C++20 constexpr library features |
1003 | | #if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST && \ |
1004 | | defined(__cpp_lib_constexpr_algorithms) && \ |
1005 | | __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/ |
1006 | | #define FASTFLOAT_CONSTEXPR20 constexpr |
1007 | | #define FASTFLOAT_IS_CONSTEXPR 1 |
1008 | | #else |
1009 | | #define FASTFLOAT_CONSTEXPR20 |
1010 | | #define FASTFLOAT_IS_CONSTEXPR 0 |
1011 | | #endif |
1012 | | |
1013 | | #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) |
1014 | | #define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 0 |
1015 | | #else |
1016 | | #define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 1 |
1017 | | #endif |
1018 | | |
1019 | | #endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H |
1020 | | |
1021 | | #ifndef FASTFLOAT_FLOAT_COMMON_H |
1022 | | #define FASTFLOAT_FLOAT_COMMON_H |
1023 | | |
1024 | | #include <cassert> |
1025 | | #include <cstring> |
1026 | | #include <limits> |
1027 | | #ifdef __has_include |
1028 | | #if __has_include(<stdfloat>) && (__cplusplus > 202002L || (defined(_MSVC_LANG) && (_MSVC_LANG > 202002L))) |
1029 | | #include <stdfloat> |
1030 | | #endif |
1031 | | #endif |
1032 | | |
1033 | | #define FASTFLOAT_VERSION_MAJOR 8 |
1034 | | #define FASTFLOAT_VERSION_MINOR 0 |
1035 | | #define FASTFLOAT_VERSION_PATCH 2 |
1036 | | |
1037 | | #define FASTFLOAT_STRINGIZE_IMPL(x) #x |
1038 | | #define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x) |
1039 | | |
1040 | | #define FASTFLOAT_VERSION_STR \ |
1041 | | FASTFLOAT_STRINGIZE(FASTFLOAT_VERSION_MAJOR) \ |
1042 | | "." FASTFLOAT_STRINGIZE(FASTFLOAT_VERSION_MINOR) "." FASTFLOAT_STRINGIZE( \ |
1043 | | FASTFLOAT_VERSION_PATCH) |
1044 | | |
1045 | | #define FASTFLOAT_VERSION \ |
1046 | | (FASTFLOAT_VERSION_MAJOR * 10000 + FASTFLOAT_VERSION_MINOR * 100 + \ |
1047 | | FASTFLOAT_VERSION_PATCH) |
1048 | | |
1049 | | namespace fast_float { |
1050 | | |
1051 | | enum class chars_format : uint64_t; |
1052 | | |
1053 | | namespace detail { |
1054 | | constexpr chars_format basic_json_fmt = chars_format(1 << 5); |
1055 | | constexpr chars_format basic_fortran_fmt = chars_format(1 << 6); |
1056 | | } // namespace detail |
1057 | | |
1058 | | enum class chars_format : uint64_t { |
1059 | | scientific = 1 << 0, |
1060 | | fixed = 1 << 2, |
1061 | | hex = 1 << 3, |
1062 | | no_infnan = 1 << 4, |
1063 | | // RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6 |
1064 | | json = uint64_t(detail::basic_json_fmt) | fixed | scientific | no_infnan, |
1065 | | // Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed. |
1066 | | json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific, |
1067 | | fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific, |
1068 | | general = fixed | scientific, |
1069 | | allow_leading_plus = 1 << 7, |
1070 | | skip_white_space = 1 << 8, |
1071 | | }; |
1072 | | |
1073 | | template <typename UC> struct from_chars_result_t { |
1074 | | UC const *ptr; |
1075 | | tinyobj_ff::ff_errc ec; |
1076 | | }; |
1077 | | |
1078 | | using from_chars_result = from_chars_result_t<char>; |
1079 | | |
1080 | | template <typename UC> struct parse_options_t { |
1081 | | constexpr explicit parse_options_t(chars_format fmt = chars_format::general, |
1082 | | UC dot = UC('.'), int b = 10) |
1083 | 5.14k | : format(fmt), decimal_point(dot), base(b) {} |
1084 | | |
1085 | | /** Which number formats are accepted */ |
1086 | | chars_format format; |
1087 | | /** The character used as decimal point */ |
1088 | | UC decimal_point; |
1089 | | /** The base used for integers */ |
1090 | | int base; |
1091 | | }; |
1092 | | |
1093 | | using parse_options = parse_options_t<char>; |
1094 | | |
1095 | | } // namespace fast_float |
1096 | | |
1097 | | #if FASTFLOAT_HAS_BIT_CAST |
1098 | | #include <bit> |
1099 | | #endif |
1100 | | |
1101 | | #if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ |
1102 | | defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) || \ |
1103 | | defined(__MINGW64__) || defined(__s390x__) || \ |
1104 | | (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \ |
1105 | | defined(__PPC64LE__)) || \ |
1106 | | defined(__loongarch64)) |
1107 | | #define FASTFLOAT_64BIT 1 |
1108 | | #elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) || \ |
1109 | | defined(__arm__) || defined(_M_ARM) || defined(__ppc__) || \ |
1110 | | defined(__MINGW32__) || defined(__EMSCRIPTEN__)) |
1111 | | #define FASTFLOAT_32BIT 1 |
1112 | | #else |
1113 | | // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow. |
1114 | | // We can never tell the register width, but the SIZE_MAX is a good |
1115 | | // approximation. UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max |
1116 | | // portability. |
1117 | | #if SIZE_MAX == 0xffff |
1118 | | #error Unknown platform (16-bit, unsupported) |
1119 | | #elif SIZE_MAX == 0xffffffff |
1120 | | #define FASTFLOAT_32BIT 1 |
1121 | | #elif SIZE_MAX == 0xffffffffffffffff |
1122 | | #define FASTFLOAT_64BIT 1 |
1123 | | #else |
1124 | | #error Unknown platform (not 32-bit, not 64-bit?) |
1125 | | #endif |
1126 | | #endif |
1127 | | |
1128 | | #if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) || \ |
1129 | | (defined(_M_ARM64) && !defined(__MINGW32__)) |
1130 | | #include <intrin.h> |
1131 | | #endif |
1132 | | |
1133 | | #if defined(_MSC_VER) && !defined(__clang__) |
1134 | | #define FASTFLOAT_VISUAL_STUDIO 1 |
1135 | | #endif |
1136 | | |
1137 | | #if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ |
1138 | | #define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) |
1139 | | #elif defined _WIN32 |
1140 | | #define FASTFLOAT_IS_BIG_ENDIAN 0 |
1141 | | #else |
1142 | | #if defined(__APPLE__) || defined(__FreeBSD__) |
1143 | | #include <machine/endian.h> |
1144 | | #elif defined(sun) || defined(__sun) |
1145 | | #include <sys/byteorder.h> |
1146 | | #elif defined(__MVS__) |
1147 | | #include <sys/endian.h> |
1148 | | #else |
1149 | | #ifdef __has_include |
1150 | | #if __has_include(<endian.h>) |
1151 | | #include <endian.h> |
1152 | | #endif //__has_include(<endian.h>) |
1153 | | #endif //__has_include |
1154 | | #endif |
1155 | | # |
1156 | | #ifndef __BYTE_ORDER__ |
1157 | | // safe choice |
1158 | | #define FASTFLOAT_IS_BIG_ENDIAN 0 |
1159 | | #endif |
1160 | | # |
1161 | | #ifndef __ORDER_LITTLE_ENDIAN__ |
1162 | | // safe choice |
1163 | | #define FASTFLOAT_IS_BIG_ENDIAN 0 |
1164 | | #endif |
1165 | | # |
1166 | | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
1167 | | #define FASTFLOAT_IS_BIG_ENDIAN 0 |
1168 | | #else |
1169 | | #define FASTFLOAT_IS_BIG_ENDIAN 1 |
1170 | | #endif |
1171 | | #endif |
1172 | | |
1173 | | #if defined(__SSE2__) || (defined(FASTFLOAT_VISUAL_STUDIO) && \ |
1174 | | (defined(_M_AMD64) || defined(_M_X64) || \ |
1175 | | (defined(_M_IX86_FP) && _M_IX86_FP == 2))) |
1176 | | #define FASTFLOAT_SSE2 1 |
1177 | | #endif |
1178 | | |
1179 | | #if defined(__aarch64__) || defined(_M_ARM64) |
1180 | | #define FASTFLOAT_NEON 1 |
1181 | | #endif |
1182 | | |
1183 | | #if defined(FASTFLOAT_SSE2) || defined(FASTFLOAT_NEON) |
1184 | | #define FASTFLOAT_HAS_SIMD 1 |
1185 | | #endif |
1186 | | |
1187 | | #if defined(__GNUC__) |
1188 | | // disable -Wcast-align=strict (GCC only) |
1189 | | #define FASTFLOAT_SIMD_DISABLE_WARNINGS \ |
1190 | | _Pragma("GCC diagnostic push") \ |
1191 | | _Pragma("GCC diagnostic ignored \"-Wcast-align\"") |
1192 | | #else |
1193 | | #define FASTFLOAT_SIMD_DISABLE_WARNINGS |
1194 | | #endif |
1195 | | |
1196 | | #if defined(__GNUC__) |
1197 | | #define FASTFLOAT_SIMD_RESTORE_WARNINGS _Pragma("GCC diagnostic pop") |
1198 | | #else |
1199 | | #define FASTFLOAT_SIMD_RESTORE_WARNINGS |
1200 | | #endif |
1201 | | |
1202 | | #ifdef FASTFLOAT_VISUAL_STUDIO |
1203 | | #define fastfloat_really_inline __forceinline |
1204 | | #else |
1205 | | #define fastfloat_really_inline inline __attribute__((always_inline)) |
1206 | | #endif |
1207 | | |
1208 | | #ifndef FASTFLOAT_ASSERT |
1209 | | #define FASTFLOAT_ASSERT(x) \ |
1210 | 0 | { ((void)(x)); } |
1211 | | #endif |
1212 | | |
1213 | | #ifndef FASTFLOAT_DEBUG_ASSERT |
1214 | | #define FASTFLOAT_DEBUG_ASSERT(x) \ |
1215 | 0 | { ((void)(x)); } |
1216 | | #endif |
1217 | | |
1218 | | // rust style `try!()` macro, or `?` operator |
1219 | | #define FASTFLOAT_TRY(x) \ |
1220 | 0 | { \ |
1221 | 0 | if (!(x)) \ |
1222 | 0 | return false; \ |
1223 | 0 | } |
1224 | | |
1225 | | #define FASTFLOAT_ENABLE_IF(...) \ |
1226 | | typename tinyobj_ff::enable_if<(__VA_ARGS__), int>::type |
1227 | | |
1228 | | namespace fast_float { |
1229 | | |
1230 | 0 | fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() { |
1231 | 0 | #if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED |
1232 | 0 | return std::is_constant_evaluated(); |
1233 | 0 | #else |
1234 | 0 | return false; |
1235 | 0 | #endif |
1236 | 0 | } |
1237 | | |
1238 | | template <typename T> |
1239 | | struct is_supported_float_type |
1240 | | : tinyobj_ff::integral_constant< |
1241 | | bool, tinyobj_ff::is_same<T, double>::value || tinyobj_ff::is_same<T, float>::value |
1242 | | #ifdef __STDCPP_FLOAT64_T__ |
1243 | | || tinyobj_ff::is_same<T, std::float64_t>::value |
1244 | | #endif |
1245 | | #ifdef __STDCPP_FLOAT32_T__ |
1246 | | || tinyobj_ff::is_same<T, std::float32_t>::value |
1247 | | #endif |
1248 | | #ifdef __STDCPP_FLOAT16_T__ |
1249 | | || tinyobj_ff::is_same<T, std::float16_t>::value |
1250 | | #endif |
1251 | | #ifdef __STDCPP_BFLOAT16_T__ |
1252 | | || tinyobj_ff::is_same<T, std::bfloat16_t>::value |
1253 | | #endif |
1254 | | > { |
1255 | | }; |
1256 | | |
1257 | | template <typename T> |
1258 | | using equiv_uint_t = typename tinyobj_ff::conditional< |
1259 | | sizeof(T) == 1, uint8_t, |
1260 | | typename tinyobj_ff::conditional< |
1261 | | sizeof(T) == 2, uint16_t, |
1262 | | typename tinyobj_ff::conditional<sizeof(T) == 4, uint32_t, |
1263 | | uint64_t>::type>::type>::type; |
1264 | | |
1265 | | template <typename T> struct is_supported_integer_type : tinyobj_ff::is_integral<T> {}; |
1266 | | |
1267 | | template <typename UC> |
1268 | | struct is_supported_char_type |
1269 | | : tinyobj_ff::integral_constant<bool, tinyobj_ff::is_same<UC, char>::value || |
1270 | | tinyobj_ff::is_same<UC, wchar_t>::value || |
1271 | | tinyobj_ff::is_same<UC, char16_t>::value || |
1272 | | tinyobj_ff::is_same<UC, char32_t>::value |
1273 | | #ifdef __cpp_char8_t |
1274 | | || tinyobj_ff::is_same<UC, char8_t>::value |
1275 | | #endif |
1276 | | > { |
1277 | | }; |
1278 | | |
1279 | | // Compares two ASCII strings in a case insensitive manner. |
1280 | | template <typename UC> |
1281 | | inline FASTFLOAT_CONSTEXPR14 bool |
1282 | | fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase, |
1283 | 622 | size_t length) { |
1284 | 653 | for (size_t i = 0; i < length; ++i) { |
1285 | 653 | UC const actual = actual_mixedcase[i]; |
1286 | 653 | if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) { |
1287 | 622 | return false; |
1288 | 622 | } |
1289 | 653 | } |
1290 | 0 | return true; |
1291 | 622 | } |
1292 | | |
1293 | | #ifndef FLT_EVAL_METHOD |
1294 | | #error "FLT_EVAL_METHOD should be defined, please include cfloat." |
1295 | | #endif |
1296 | | |
1297 | | // a pointer and a length to a contiguous block of memory |
1298 | | template <typename T> struct span { |
1299 | | T const *ptr; |
1300 | | size_t length; |
1301 | | |
1302 | 5.10k | constexpr span(T const *_ptr, size_t _length) : ptr(_ptr), length(_length) {}fast_float::span<char const>::span(char const*, unsigned long) Line | Count | Source | 1302 | 5.10k | constexpr span(T const *_ptr, size_t _length) : ptr(_ptr), length(_length) {} |
Unexecuted instantiation: fast_float::span<unsigned long>::span(unsigned long const*, unsigned long) |
1303 | | |
1304 | 14.8k | constexpr span() : ptr(nullptr), length(0) {} |
1305 | | |
1306 | 15 | constexpr size_t len() const noexcept { return length; }fast_float::span<char const>::len() const Line | Count | Source | 1306 | 15 | constexpr size_t len() const noexcept { return length; } |
Unexecuted instantiation: fast_float::span<unsigned long>::len() const |
1307 | | |
1308 | 0 | FASTFLOAT_CONSTEXPR14 const T &operator[](size_t index) const noexcept { |
1309 | 0 | FASTFLOAT_DEBUG_ASSERT(index < length); |
1310 | 0 | return ptr[index]; |
1311 | 0 | } |
1312 | | }; |
1313 | | |
1314 | | struct value128 { |
1315 | | uint64_t low; |
1316 | | uint64_t high; |
1317 | | |
1318 | 0 | constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {} |
1319 | | |
1320 | 26 | constexpr value128() : low(0), high(0) {} |
1321 | | }; |
1322 | | |
1323 | | /* Helper C++14 constexpr generic implementation of leading_zeroes */ |
1324 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int |
1325 | 0 | leading_zeroes_generic(uint64_t input_num, int last_bit = 0) { |
1326 | 0 | if (input_num & uint64_t(0xffffffff00000000)) { |
1327 | 0 | input_num >>= 32; |
1328 | 0 | last_bit |= 32; |
1329 | 0 | } |
1330 | 0 | if (input_num & uint64_t(0xffff0000)) { |
1331 | 0 | input_num >>= 16; |
1332 | 0 | last_bit |= 16; |
1333 | 0 | } |
1334 | 0 | if (input_num & uint64_t(0xff00)) { |
1335 | 0 | input_num >>= 8; |
1336 | 0 | last_bit |= 8; |
1337 | 0 | } |
1338 | 0 | if (input_num & uint64_t(0xf0)) { |
1339 | 0 | input_num >>= 4; |
1340 | 0 | last_bit |= 4; |
1341 | 0 | } |
1342 | 0 | if (input_num & uint64_t(0xc)) { |
1343 | 0 | input_num >>= 2; |
1344 | 0 | last_bit |= 2; |
1345 | 0 | } |
1346 | 0 | if (input_num & uint64_t(0x2)) { /* input_num >>= 1; */ |
1347 | 0 | last_bit |= 1; |
1348 | 0 | } |
1349 | 0 | return 63 - last_bit; |
1350 | 0 | } |
1351 | | |
1352 | | /* result might be undefined when input_num is zero */ |
1353 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 int |
1354 | 25 | leading_zeroes(uint64_t input_num) { |
1355 | 25 | assert(input_num > 0); |
1356 | 25 | if (cpp20_and_in_constexpr()) { |
1357 | 0 | return leading_zeroes_generic(input_num); |
1358 | 0 | } |
1359 | | #ifdef FASTFLOAT_VISUAL_STUDIO |
1360 | | #if defined(_M_X64) || defined(_M_ARM64) |
1361 | | unsigned long leading_zero = 0; |
1362 | | // Search the mask data from most significant bit (MSB) |
1363 | | // to least significant bit (LSB) for a set bit (1). |
1364 | | _BitScanReverse64(&leading_zero, input_num); |
1365 | | return (int)(63 - leading_zero); |
1366 | | #else |
1367 | | return leading_zeroes_generic(input_num); |
1368 | | #endif |
1369 | | #else |
1370 | 25 | return __builtin_clzll(input_num); |
1371 | 25 | #endif |
1372 | 25 | } |
1373 | | |
1374 | | // slow emulation routine for 32-bit |
1375 | 0 | fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) { |
1376 | 0 | return x * (uint64_t)y; |
1377 | 0 | } |
1378 | | |
1379 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t |
1380 | 0 | umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) { |
1381 | 0 | uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd); |
1382 | 0 | uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd); |
1383 | 0 | uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32)); |
1384 | 0 | uint64_t adbc_carry = (uint64_t)(adbc < ad); |
1385 | 0 | uint64_t lo = bd + (adbc << 32); |
1386 | 0 | *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + |
1387 | 0 | (adbc_carry << 32) + (uint64_t)(lo < bd); |
1388 | 0 | return lo; |
1389 | 0 | } |
1390 | | |
1391 | | #ifdef FASTFLOAT_32BIT |
1392 | | |
1393 | | // slow emulation routine for 32-bit |
1394 | | #if !defined(__MINGW64__) |
1395 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t _umul128(uint64_t ab, |
1396 | | uint64_t cd, |
1397 | | uint64_t *hi) { |
1398 | | return umul128_generic(ab, cd, hi); |
1399 | | } |
1400 | | #endif // !__MINGW64__ |
1401 | | |
1402 | | #endif // FASTFLOAT_32BIT |
1403 | | |
1404 | | // compute 64-bit a*b |
1405 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128 |
1406 | 26 | full_multiplication(uint64_t a, uint64_t b) { |
1407 | 26 | if (cpp20_and_in_constexpr()) { |
1408 | 0 | value128 answer; |
1409 | 0 | answer.low = umul128_generic(a, b, &answer.high); |
1410 | 0 | return answer; |
1411 | 0 | } |
1412 | 26 | value128 answer; |
1413 | | #if defined(_M_ARM64) && !defined(__MINGW32__) |
1414 | | // ARM64 has native support for 64-bit multiplications, no need to emulate |
1415 | | // But MinGW on ARM64 doesn't have native support for 64-bit multiplications |
1416 | | answer.high = __umulh(a, b); |
1417 | | answer.low = a * b; |
1418 | | #elif defined(FASTFLOAT_32BIT) || \ |
1419 | | (defined(_WIN64) && !defined(__clang__) && !defined(_M_ARM64)) |
1420 | | answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64 |
1421 | | #elif defined(FASTFLOAT_64BIT) && defined(__SIZEOF_INT128__) |
1422 | | __uint128_t r = ((__uint128_t)a) * b; |
1423 | 26 | answer.low = uint64_t(r); |
1424 | 26 | answer.high = uint64_t(r >> 64); |
1425 | | #else |
1426 | | answer.low = umul128_generic(a, b, &answer.high); |
1427 | | #endif |
1428 | 26 | return answer; |
1429 | 26 | } |
1430 | | |
1431 | | struct adjusted_mantissa { |
1432 | | uint64_t mantissa{0}; |
1433 | | int32_t power2{0}; // a negative value indicates an invalid result |
1434 | 60 | adjusted_mantissa() = default; |
1435 | | |
1436 | 0 | constexpr bool operator==(adjusted_mantissa const &o) const { |
1437 | 0 | return mantissa == o.mantissa && power2 == o.power2; |
1438 | 0 | } |
1439 | | |
1440 | 10 | constexpr bool operator!=(adjusted_mantissa const &o) const { |
1441 | 10 | return mantissa != o.mantissa || power2 != o.power2; |
1442 | 10 | } |
1443 | | }; |
1444 | | |
1445 | | // Bias so we can get the real exponent with an invalid adjusted_mantissa. |
1446 | | constexpr static int32_t invalid_am_bias = -0x8000; |
1447 | | |
1448 | | // used for binary_format_lookup_tables<T>::max_mantissa |
1449 | | constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5; |
1450 | | |
1451 | | template <typename T, typename U = void> struct binary_format_lookup_tables; |
1452 | | |
1453 | | template <typename T> struct binary_format : binary_format_lookup_tables<T> { |
1454 | | using equiv_uint = equiv_uint_t<T>; |
1455 | | |
1456 | | static constexpr int mantissa_explicit_bits(); |
1457 | | static constexpr int minimum_exponent(); |
1458 | | static constexpr int infinite_power(); |
1459 | | static constexpr int sign_index(); |
1460 | | static constexpr int |
1461 | | min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST |
1462 | | static constexpr int max_exponent_fast_path(); |
1463 | | static constexpr int max_exponent_round_to_even(); |
1464 | | static constexpr int min_exponent_round_to_even(); |
1465 | | static constexpr uint64_t max_mantissa_fast_path(int64_t power); |
1466 | | static constexpr uint64_t |
1467 | | max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST |
1468 | | static constexpr int largest_power_of_ten(); |
1469 | | static constexpr int smallest_power_of_ten(); |
1470 | | static constexpr T exact_power_of_ten(int64_t power); |
1471 | | static constexpr size_t max_digits(); |
1472 | | static constexpr equiv_uint exponent_mask(); |
1473 | | static constexpr equiv_uint mantissa_mask(); |
1474 | | static constexpr equiv_uint hidden_bit_mask(); |
1475 | | }; |
1476 | | |
1477 | | template <typename U> struct binary_format_lookup_tables<double, U> { |
1478 | | static constexpr double powers_of_ten[] = { |
1479 | | 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, |
1480 | | 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; |
1481 | | |
1482 | | // Largest integer value v so that (5**index * v) <= 1<<53. |
1483 | | // 0x20000000000000 == 1 << 53 |
1484 | | static constexpr uint64_t max_mantissa[] = { |
1485 | | 0x20000000000000, |
1486 | | 0x20000000000000 / 5, |
1487 | | 0x20000000000000 / (5 * 5), |
1488 | | 0x20000000000000 / (5 * 5 * 5), |
1489 | | 0x20000000000000 / (5 * 5 * 5 * 5), |
1490 | | 0x20000000000000 / (constant_55555), |
1491 | | 0x20000000000000 / (constant_55555 * 5), |
1492 | | 0x20000000000000 / (constant_55555 * 5 * 5), |
1493 | | 0x20000000000000 / (constant_55555 * 5 * 5 * 5), |
1494 | | 0x20000000000000 / (constant_55555 * 5 * 5 * 5 * 5), |
1495 | | 0x20000000000000 / (constant_55555 * constant_55555), |
1496 | | 0x20000000000000 / (constant_55555 * constant_55555 * 5), |
1497 | | 0x20000000000000 / (constant_55555 * constant_55555 * 5 * 5), |
1498 | | 0x20000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5), |
1499 | | 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555), |
1500 | | 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5), |
1501 | | 0x20000000000000 / |
1502 | | (constant_55555 * constant_55555 * constant_55555 * 5 * 5), |
1503 | | 0x20000000000000 / |
1504 | | (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5), |
1505 | | 0x20000000000000 / |
1506 | | (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5), |
1507 | | 0x20000000000000 / |
1508 | | (constant_55555 * constant_55555 * constant_55555 * constant_55555), |
1509 | | 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * |
1510 | | constant_55555 * 5), |
1511 | | 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * |
1512 | | constant_55555 * 5 * 5), |
1513 | | 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * |
1514 | | constant_55555 * 5 * 5 * 5), |
1515 | | 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * |
1516 | | constant_55555 * 5 * 5 * 5 * 5)}; |
1517 | | }; |
1518 | | |
1519 | | #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE |
1520 | | |
1521 | | template <typename U> |
1522 | | constexpr double binary_format_lookup_tables<double, U>::powers_of_ten[]; |
1523 | | |
1524 | | template <typename U> |
1525 | | constexpr uint64_t binary_format_lookup_tables<double, U>::max_mantissa[]; |
1526 | | |
1527 | | #endif |
1528 | | |
1529 | | template <typename U> struct binary_format_lookup_tables<float, U> { |
1530 | | static constexpr float powers_of_ten[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f, |
1531 | | 1e6f, 1e7f, 1e8f, 1e9f, 1e10f}; |
1532 | | |
1533 | | // Largest integer value v so that (5**index * v) <= 1<<24. |
1534 | | // 0x1000000 == 1<<24 |
1535 | | static constexpr uint64_t max_mantissa[] = { |
1536 | | 0x1000000, |
1537 | | 0x1000000 / 5, |
1538 | | 0x1000000 / (5 * 5), |
1539 | | 0x1000000 / (5 * 5 * 5), |
1540 | | 0x1000000 / (5 * 5 * 5 * 5), |
1541 | | 0x1000000 / (constant_55555), |
1542 | | 0x1000000 / (constant_55555 * 5), |
1543 | | 0x1000000 / (constant_55555 * 5 * 5), |
1544 | | 0x1000000 / (constant_55555 * 5 * 5 * 5), |
1545 | | 0x1000000 / (constant_55555 * 5 * 5 * 5 * 5), |
1546 | | 0x1000000 / (constant_55555 * constant_55555), |
1547 | | 0x1000000 / (constant_55555 * constant_55555 * 5)}; |
1548 | | }; |
1549 | | |
1550 | | #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE |
1551 | | |
1552 | | template <typename U> |
1553 | | constexpr float binary_format_lookup_tables<float, U>::powers_of_ten[]; |
1554 | | |
1555 | | template <typename U> |
1556 | | constexpr uint64_t binary_format_lookup_tables<float, U>::max_mantissa[]; |
1557 | | |
1558 | | #endif |
1559 | | |
1560 | | template <> |
1561 | 2.84k | inline constexpr int binary_format<double>::min_exponent_fast_path() { |
1562 | | #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) |
1563 | | return 0; |
1564 | | #else |
1565 | 2.84k | return -22; |
1566 | 2.84k | #endif |
1567 | 2.84k | } |
1568 | | |
1569 | | template <> |
1570 | 0 | inline constexpr int binary_format<float>::min_exponent_fast_path() { |
1571 | 0 | #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) |
1572 | 0 | return 0; |
1573 | 0 | #else |
1574 | 0 | return -10; |
1575 | 0 | #endif |
1576 | 0 | } |
1577 | | |
1578 | | template <> |
1579 | 2.92k | inline constexpr int binary_format<double>::mantissa_explicit_bits() { |
1580 | 2.92k | return 52; |
1581 | 2.92k | } |
1582 | | |
1583 | | template <> |
1584 | 0 | inline constexpr int binary_format<float>::mantissa_explicit_bits() { |
1585 | 0 | return 23; |
1586 | 0 | } |
1587 | | |
1588 | | template <> |
1589 | 0 | inline constexpr int binary_format<double>::max_exponent_round_to_even() { |
1590 | 0 | return 23; |
1591 | 0 | } |
1592 | | |
1593 | | template <> |
1594 | 0 | inline constexpr int binary_format<float>::max_exponent_round_to_even() { |
1595 | 0 | return 10; |
1596 | 0 | } |
1597 | | |
1598 | | template <> |
1599 | 3 | inline constexpr int binary_format<double>::min_exponent_round_to_even() { |
1600 | 3 | return -4; |
1601 | 3 | } |
1602 | | |
1603 | | template <> |
1604 | 0 | inline constexpr int binary_format<float>::min_exponent_round_to_even() { |
1605 | 0 | return -17; |
1606 | 0 | } |
1607 | | |
1608 | 25 | template <> inline constexpr int binary_format<double>::minimum_exponent() { |
1609 | 25 | return -1023; |
1610 | 25 | } |
1611 | | |
1612 | 0 | template <> inline constexpr int binary_format<float>::minimum_exponent() { |
1613 | 0 | return -127; |
1614 | 0 | } |
1615 | | |
1616 | 67 | template <> inline constexpr int binary_format<double>::infinite_power() { |
1617 | 67 | return 0x7FF; |
1618 | 67 | } |
1619 | | |
1620 | 0 | template <> inline constexpr int binary_format<float>::infinite_power() { |
1621 | 0 | return 0xFF; |
1622 | 0 | } |
1623 | | |
1624 | 50 | template <> inline constexpr int binary_format<double>::sign_index() { |
1625 | 50 | return 63; |
1626 | 50 | } |
1627 | | |
1628 | 0 | template <> inline constexpr int binary_format<float>::sign_index() { |
1629 | 0 | return 31; |
1630 | 0 | } |
1631 | | |
1632 | | template <> |
1633 | 2.82k | inline constexpr int binary_format<double>::max_exponent_fast_path() { |
1634 | 2.82k | return 22; |
1635 | 2.82k | } |
1636 | | |
1637 | | template <> |
1638 | 0 | inline constexpr int binary_format<float>::max_exponent_fast_path() { |
1639 | 0 | return 10; |
1640 | 0 | } |
1641 | | |
1642 | | template <> |
1643 | 2.79k | inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() { |
1644 | 2.79k | return uint64_t(2) << mantissa_explicit_bits(); |
1645 | 2.79k | } |
1646 | | |
1647 | | template <> |
1648 | 0 | inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() { |
1649 | 0 | return uint64_t(2) << mantissa_explicit_bits(); |
1650 | 0 | } |
1651 | | |
1652 | | // credit: Jakub JelÃnek |
1653 | | #ifdef __STDCPP_FLOAT16_T__ |
1654 | | template <typename U> struct binary_format_lookup_tables<std::float16_t, U> { |
1655 | | static constexpr std::float16_t powers_of_ten[] = {1e0f16, 1e1f16, 1e2f16, |
1656 | | 1e3f16, 1e4f16}; |
1657 | | |
1658 | | // Largest integer value v so that (5**index * v) <= 1<<11. |
1659 | | // 0x800 == 1<<11 |
1660 | | static constexpr uint64_t max_mantissa[] = {0x800, |
1661 | | 0x800 / 5, |
1662 | | 0x800 / (5 * 5), |
1663 | | 0x800 / (5 * 5 * 5), |
1664 | | 0x800 / (5 * 5 * 5 * 5), |
1665 | | 0x800 / (constant_55555)}; |
1666 | | }; |
1667 | | |
1668 | | #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE |
1669 | | |
1670 | | template <typename U> |
1671 | | constexpr std::float16_t |
1672 | | binary_format_lookup_tables<std::float16_t, U>::powers_of_ten[]; |
1673 | | |
1674 | | template <typename U> |
1675 | | constexpr uint64_t |
1676 | | binary_format_lookup_tables<std::float16_t, U>::max_mantissa[]; |
1677 | | |
1678 | | #endif |
1679 | | |
1680 | | template <> |
1681 | | inline constexpr std::float16_t |
1682 | | binary_format<std::float16_t>::exact_power_of_ten(int64_t power) { |
1683 | | // Work around clang bug https://godbolt.org/z/zedh7rrhc |
1684 | | return (void)powers_of_ten[0], powers_of_ten[power]; |
1685 | | } |
1686 | | |
1687 | | template <> |
1688 | | inline constexpr binary_format<std::float16_t>::equiv_uint |
1689 | | binary_format<std::float16_t>::exponent_mask() { |
1690 | | return 0x7C00; |
1691 | | } |
1692 | | |
1693 | | template <> |
1694 | | inline constexpr binary_format<std::float16_t>::equiv_uint |
1695 | | binary_format<std::float16_t>::mantissa_mask() { |
1696 | | return 0x03FF; |
1697 | | } |
1698 | | |
1699 | | template <> |
1700 | | inline constexpr binary_format<std::float16_t>::equiv_uint |
1701 | | binary_format<std::float16_t>::hidden_bit_mask() { |
1702 | | return 0x0400; |
1703 | | } |
1704 | | |
1705 | | template <> |
1706 | | inline constexpr int binary_format<std::float16_t>::max_exponent_fast_path() { |
1707 | | return 4; |
1708 | | } |
1709 | | |
1710 | | template <> |
1711 | | inline constexpr int binary_format<std::float16_t>::mantissa_explicit_bits() { |
1712 | | return 10; |
1713 | | } |
1714 | | |
1715 | | template <> |
1716 | | inline constexpr uint64_t |
1717 | | binary_format<std::float16_t>::max_mantissa_fast_path() { |
1718 | | return uint64_t(2) << mantissa_explicit_bits(); |
1719 | | } |
1720 | | |
1721 | | template <> |
1722 | | inline constexpr uint64_t |
1723 | | binary_format<std::float16_t>::max_mantissa_fast_path(int64_t power) { |
1724 | | // caller is responsible to ensure that |
1725 | | // power >= 0 && power <= 4 |
1726 | | // |
1727 | | // Work around clang bug https://godbolt.org/z/zedh7rrhc |
1728 | | return (void)max_mantissa[0], max_mantissa[power]; |
1729 | | } |
1730 | | |
1731 | | template <> |
1732 | | inline constexpr int binary_format<std::float16_t>::min_exponent_fast_path() { |
1733 | | return 0; |
1734 | | } |
1735 | | |
1736 | | template <> |
1737 | | inline constexpr int |
1738 | | binary_format<std::float16_t>::max_exponent_round_to_even() { |
1739 | | return 5; |
1740 | | } |
1741 | | |
1742 | | template <> |
1743 | | inline constexpr int |
1744 | | binary_format<std::float16_t>::min_exponent_round_to_even() { |
1745 | | return -22; |
1746 | | } |
1747 | | |
1748 | | template <> |
1749 | | inline constexpr int binary_format<std::float16_t>::minimum_exponent() { |
1750 | | return -15; |
1751 | | } |
1752 | | |
1753 | | template <> |
1754 | | inline constexpr int binary_format<std::float16_t>::infinite_power() { |
1755 | | return 0x1F; |
1756 | | } |
1757 | | |
1758 | | template <> inline constexpr int binary_format<std::float16_t>::sign_index() { |
1759 | | return 15; |
1760 | | } |
1761 | | |
1762 | | template <> |
1763 | | inline constexpr int binary_format<std::float16_t>::largest_power_of_ten() { |
1764 | | return 4; |
1765 | | } |
1766 | | |
1767 | | template <> |
1768 | | inline constexpr int binary_format<std::float16_t>::smallest_power_of_ten() { |
1769 | | return -27; |
1770 | | } |
1771 | | |
1772 | | template <> |
1773 | | inline constexpr size_t binary_format<std::float16_t>::max_digits() { |
1774 | | return 22; |
1775 | | } |
1776 | | #endif // __STDCPP_FLOAT16_T__ |
1777 | | |
1778 | | // credit: Jakub JelÃnek |
1779 | | #ifdef __STDCPP_BFLOAT16_T__ |
1780 | | template <typename U> struct binary_format_lookup_tables<std::bfloat16_t, U> { |
1781 | | static constexpr std::bfloat16_t powers_of_ten[] = {1e0bf16, 1e1bf16, 1e2bf16, |
1782 | | 1e3bf16}; |
1783 | | |
1784 | | // Largest integer value v so that (5**index * v) <= 1<<8. |
1785 | | // 0x100 == 1<<8 |
1786 | | static constexpr uint64_t max_mantissa[] = {0x100, 0x100 / 5, 0x100 / (5 * 5), |
1787 | | 0x100 / (5 * 5 * 5), |
1788 | | 0x100 / (5 * 5 * 5 * 5)}; |
1789 | | }; |
1790 | | |
1791 | | #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE |
1792 | | |
1793 | | template <typename U> |
1794 | | constexpr std::bfloat16_t |
1795 | | binary_format_lookup_tables<std::bfloat16_t, U>::powers_of_ten[]; |
1796 | | |
1797 | | template <typename U> |
1798 | | constexpr uint64_t |
1799 | | binary_format_lookup_tables<std::bfloat16_t, U>::max_mantissa[]; |
1800 | | |
1801 | | #endif |
1802 | | |
1803 | | template <> |
1804 | | inline constexpr std::bfloat16_t |
1805 | | binary_format<std::bfloat16_t>::exact_power_of_ten(int64_t power) { |
1806 | | // Work around clang bug https://godbolt.org/z/zedh7rrhc |
1807 | | return (void)powers_of_ten[0], powers_of_ten[power]; |
1808 | | } |
1809 | | |
1810 | | template <> |
1811 | | inline constexpr int binary_format<std::bfloat16_t>::max_exponent_fast_path() { |
1812 | | return 3; |
1813 | | } |
1814 | | |
1815 | | template <> |
1816 | | inline constexpr binary_format<std::bfloat16_t>::equiv_uint |
1817 | | binary_format<std::bfloat16_t>::exponent_mask() { |
1818 | | return 0x7F80; |
1819 | | } |
1820 | | |
1821 | | template <> |
1822 | | inline constexpr binary_format<std::bfloat16_t>::equiv_uint |
1823 | | binary_format<std::bfloat16_t>::mantissa_mask() { |
1824 | | return 0x007F; |
1825 | | } |
1826 | | |
1827 | | template <> |
1828 | | inline constexpr binary_format<std::bfloat16_t>::equiv_uint |
1829 | | binary_format<std::bfloat16_t>::hidden_bit_mask() { |
1830 | | return 0x0080; |
1831 | | } |
1832 | | |
1833 | | template <> |
1834 | | inline constexpr int binary_format<std::bfloat16_t>::mantissa_explicit_bits() { |
1835 | | return 7; |
1836 | | } |
1837 | | |
1838 | | template <> |
1839 | | inline constexpr uint64_t |
1840 | | binary_format<std::bfloat16_t>::max_mantissa_fast_path() { |
1841 | | return uint64_t(2) << mantissa_explicit_bits(); |
1842 | | } |
1843 | | |
1844 | | template <> |
1845 | | inline constexpr uint64_t |
1846 | | binary_format<std::bfloat16_t>::max_mantissa_fast_path(int64_t power) { |
1847 | | // caller is responsible to ensure that |
1848 | | // power >= 0 && power <= 3 |
1849 | | // |
1850 | | // Work around clang bug https://godbolt.org/z/zedh7rrhc |
1851 | | return (void)max_mantissa[0], max_mantissa[power]; |
1852 | | } |
1853 | | |
1854 | | template <> |
1855 | | inline constexpr int binary_format<std::bfloat16_t>::min_exponent_fast_path() { |
1856 | | return 0; |
1857 | | } |
1858 | | |
1859 | | template <> |
1860 | | inline constexpr int |
1861 | | binary_format<std::bfloat16_t>::max_exponent_round_to_even() { |
1862 | | return 3; |
1863 | | } |
1864 | | |
1865 | | template <> |
1866 | | inline constexpr int |
1867 | | binary_format<std::bfloat16_t>::min_exponent_round_to_even() { |
1868 | | return -24; |
1869 | | } |
1870 | | |
1871 | | template <> |
1872 | | inline constexpr int binary_format<std::bfloat16_t>::minimum_exponent() { |
1873 | | return -127; |
1874 | | } |
1875 | | |
1876 | | template <> |
1877 | | inline constexpr int binary_format<std::bfloat16_t>::infinite_power() { |
1878 | | return 0xFF; |
1879 | | } |
1880 | | |
1881 | | template <> inline constexpr int binary_format<std::bfloat16_t>::sign_index() { |
1882 | | return 15; |
1883 | | } |
1884 | | |
1885 | | template <> |
1886 | | inline constexpr int binary_format<std::bfloat16_t>::largest_power_of_ten() { |
1887 | | return 38; |
1888 | | } |
1889 | | |
1890 | | template <> |
1891 | | inline constexpr int binary_format<std::bfloat16_t>::smallest_power_of_ten() { |
1892 | | return -60; |
1893 | | } |
1894 | | |
1895 | | template <> |
1896 | | inline constexpr size_t binary_format<std::bfloat16_t>::max_digits() { |
1897 | | return 98; |
1898 | | } |
1899 | | #endif // __STDCPP_BFLOAT16_T__ |
1900 | | |
1901 | | template <> |
1902 | | inline constexpr uint64_t |
1903 | 0 | binary_format<double>::max_mantissa_fast_path(int64_t power) { |
1904 | | // caller is responsible to ensure that |
1905 | | // power >= 0 && power <= 22 |
1906 | | // |
1907 | | // Work around clang bug https://godbolt.org/z/zedh7rrhc |
1908 | 0 | return (void)max_mantissa[0], max_mantissa[power]; |
1909 | 0 | } |
1910 | | |
1911 | | template <> |
1912 | | inline constexpr uint64_t |
1913 | 0 | binary_format<float>::max_mantissa_fast_path(int64_t power) { |
1914 | 0 | // caller is responsible to ensure that |
1915 | 0 | // power >= 0 && power <= 10 |
1916 | 0 | // |
1917 | 0 | // Work around clang bug https://godbolt.org/z/zedh7rrhc |
1918 | 0 | return (void)max_mantissa[0], max_mantissa[power]; |
1919 | 0 | } |
1920 | | |
1921 | | template <> |
1922 | | inline constexpr double |
1923 | 2.79k | binary_format<double>::exact_power_of_ten(int64_t power) { |
1924 | | // Work around clang bug https://godbolt.org/z/zedh7rrhc |
1925 | 2.79k | return (void)powers_of_ten[0], powers_of_ten[power]; |
1926 | 2.79k | } |
1927 | | |
1928 | | template <> |
1929 | 0 | inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) { |
1930 | 0 | // Work around clang bug https://godbolt.org/z/zedh7rrhc |
1931 | 0 | return (void)powers_of_ten[0], powers_of_ten[power]; |
1932 | 0 | } |
1933 | | |
1934 | 36 | template <> inline constexpr int binary_format<double>::largest_power_of_ten() { |
1935 | 36 | return 308; |
1936 | 36 | } |
1937 | | |
1938 | 0 | template <> inline constexpr int binary_format<float>::largest_power_of_ten() { |
1939 | 0 | return 38; |
1940 | 0 | } |
1941 | | |
1942 | | template <> |
1943 | 55 | inline constexpr int binary_format<double>::smallest_power_of_ten() { |
1944 | 55 | return -342; |
1945 | 55 | } |
1946 | | |
1947 | 0 | template <> inline constexpr int binary_format<float>::smallest_power_of_ten() { |
1948 | 0 | return -64; |
1949 | 0 | } |
1950 | | |
1951 | 0 | template <> inline constexpr size_t binary_format<double>::max_digits() { |
1952 | 0 | return 769; |
1953 | 0 | } |
1954 | | |
1955 | 0 | template <> inline constexpr size_t binary_format<float>::max_digits() { |
1956 | 0 | return 114; |
1957 | 0 | } |
1958 | | |
1959 | | template <> |
1960 | | inline constexpr binary_format<float>::equiv_uint |
1961 | 0 | binary_format<float>::exponent_mask() { |
1962 | 0 | return 0x7F800000; |
1963 | 0 | } |
1964 | | |
1965 | | template <> |
1966 | | inline constexpr binary_format<double>::equiv_uint |
1967 | 0 | binary_format<double>::exponent_mask() { |
1968 | 0 | return 0x7FF0000000000000; |
1969 | 0 | } |
1970 | | |
1971 | | template <> |
1972 | | inline constexpr binary_format<float>::equiv_uint |
1973 | 0 | binary_format<float>::mantissa_mask() { |
1974 | 0 | return 0x007FFFFF; |
1975 | 0 | } |
1976 | | |
1977 | | template <> |
1978 | | inline constexpr binary_format<double>::equiv_uint |
1979 | 0 | binary_format<double>::mantissa_mask() { |
1980 | 0 | return 0x000FFFFFFFFFFFFF; |
1981 | 0 | } |
1982 | | |
1983 | | template <> |
1984 | | inline constexpr binary_format<float>::equiv_uint |
1985 | 0 | binary_format<float>::hidden_bit_mask() { |
1986 | 0 | return 0x00800000; |
1987 | 0 | } |
1988 | | |
1989 | | template <> |
1990 | | inline constexpr binary_format<double>::equiv_uint |
1991 | 0 | binary_format<double>::hidden_bit_mask() { |
1992 | 0 | return 0x0010000000000000; |
1993 | 0 | } |
1994 | | |
1995 | | template <typename T> |
1996 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void |
1997 | 50 | to_float(bool negative, adjusted_mantissa am, T &value) { |
1998 | 50 | using equiv_uint = equiv_uint_t<T>; |
1999 | 50 | equiv_uint word = equiv_uint(am.mantissa); |
2000 | 50 | word = equiv_uint(word | equiv_uint(am.power2) |
2001 | 50 | << binary_format<T>::mantissa_explicit_bits()); |
2002 | 50 | word = |
2003 | 50 | equiv_uint(word | equiv_uint(negative) << binary_format<T>::sign_index()); |
2004 | | #if FASTFLOAT_HAS_BIT_CAST |
2005 | | value = std::bit_cast<T>(word); |
2006 | | #else |
2007 | 50 | ::memcpy(&value, &word, sizeof(T)); |
2008 | 50 | #endif |
2009 | 50 | } |
2010 | | |
2011 | | template <typename = void> struct space_lut { |
2012 | | static constexpr bool value[] = { |
2013 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2014 | | 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2015 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2016 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2017 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2018 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2019 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2020 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2021 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2022 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2023 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
2024 | | }; |
2025 | | |
2026 | | #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE |
2027 | | |
2028 | | template <typename T> constexpr bool space_lut<T>::value[]; |
2029 | | |
2030 | | #endif |
2031 | | |
2032 | 0 | template <typename UC> constexpr bool is_space(UC c) { |
2033 | 0 | return c < 256 && space_lut<>::value[uint8_t(c)]; |
2034 | 0 | } |
2035 | | |
2036 | 0 | template <typename UC> static constexpr uint64_t int_cmp_zeros() { |
2037 | 0 | static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), |
2038 | 0 | "Unsupported character size"); |
2039 | 0 | return (sizeof(UC) == 1) ? 0x3030303030303030 |
2040 | 0 | : (sizeof(UC) == 2) |
2041 | 0 | ? (uint64_t(UC('0')) << 48 | uint64_t(UC('0')) << 32 | |
2042 | 0 | uint64_t(UC('0')) << 16 | UC('0')) |
2043 | 0 | : (uint64_t(UC('0')) << 32 | UC('0')); |
2044 | 0 | } |
2045 | | |
2046 | 0 | template <typename UC> static constexpr int int_cmp_len() { |
2047 | 0 | return sizeof(uint64_t) / sizeof(UC); |
2048 | 0 | } |
2049 | | |
2050 | | template <typename UC> constexpr UC const *str_const_nan(); |
2051 | | |
2052 | 311 | template <> constexpr char const *str_const_nan<char>() { return "nan"; } |
2053 | | |
2054 | 0 | template <> constexpr wchar_t const *str_const_nan<wchar_t>() { return L"nan"; } |
2055 | | |
2056 | 0 | template <> constexpr char16_t const *str_const_nan<char16_t>() { |
2057 | 0 | return u"nan"; |
2058 | 0 | } |
2059 | | |
2060 | 0 | template <> constexpr char32_t const *str_const_nan<char32_t>() { |
2061 | 0 | return U"nan"; |
2062 | 0 | } |
2063 | | |
2064 | | #ifdef __cpp_char8_t |
2065 | | template <> constexpr char8_t const *str_const_nan<char8_t>() { |
2066 | | return u8"nan"; |
2067 | | } |
2068 | | #endif |
2069 | | |
2070 | | template <typename UC> constexpr UC const *str_const_inf(); |
2071 | | |
2072 | 311 | template <> constexpr char const *str_const_inf<char>() { return "infinity"; } |
2073 | | |
2074 | 0 | template <> constexpr wchar_t const *str_const_inf<wchar_t>() { |
2075 | 0 | return L"infinity"; |
2076 | 0 | } |
2077 | | |
2078 | 0 | template <> constexpr char16_t const *str_const_inf<char16_t>() { |
2079 | 0 | return u"infinity"; |
2080 | 0 | } |
2081 | | |
2082 | 0 | template <> constexpr char32_t const *str_const_inf<char32_t>() { |
2083 | 0 | return U"infinity"; |
2084 | 0 | } |
2085 | | |
2086 | | #ifdef __cpp_char8_t |
2087 | | template <> constexpr char8_t const *str_const_inf<char8_t>() { |
2088 | | return u8"infinity"; |
2089 | | } |
2090 | | #endif |
2091 | | |
2092 | | template <typename = void> struct int_luts { |
2093 | | static constexpr uint8_t chdigit[] = { |
2094 | | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
2095 | | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
2096 | | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
2097 | | 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, |
2098 | | 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, |
2099 | | 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, |
2100 | | 35, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, |
2101 | | 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, |
2102 | | 33, 34, 35, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
2103 | | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
2104 | | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
2105 | | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
2106 | | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
2107 | | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
2108 | | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
2109 | | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
2110 | | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
2111 | | 255}; |
2112 | | |
2113 | | static constexpr size_t maxdigits_u64[] = { |
2114 | | 64, 41, 32, 28, 25, 23, 22, 21, 20, 19, 18, 18, 17, 17, 16, 16, 16, 16, |
2115 | | 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13}; |
2116 | | |
2117 | | static constexpr uint64_t min_safe_u64[] = { |
2118 | | 9223372036854775808ull, 12157665459056928801ull, 4611686018427387904, |
2119 | | 7450580596923828125, 4738381338321616896, 3909821048582988049, |
2120 | | 9223372036854775808ull, 12157665459056928801ull, 10000000000000000000ull, |
2121 | | 5559917313492231481, 2218611106740436992, 8650415919381337933, |
2122 | | 2177953337809371136, 6568408355712890625, 1152921504606846976, |
2123 | | 2862423051509815793, 6746640616477458432, 15181127029874798299ull, |
2124 | | 1638400000000000000, 3243919932521508681, 6221821273427820544, |
2125 | | 11592836324538749809ull, 876488338465357824, 1490116119384765625, |
2126 | | 2481152873203736576, 4052555153018976267, 6502111422497947648, |
2127 | | 10260628712958602189ull, 15943230000000000000ull, 787662783788549761, |
2128 | | 1152921504606846976, 1667889514952984961, 2386420683693101056, |
2129 | | 3379220508056640625, 4738381338321616896}; |
2130 | | }; |
2131 | | |
2132 | | #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE |
2133 | | |
2134 | | template <typename T> constexpr uint8_t int_luts<T>::chdigit[]; |
2135 | | |
2136 | | template <typename T> constexpr size_t int_luts<T>::maxdigits_u64[]; |
2137 | | |
2138 | | template <typename T> constexpr uint64_t int_luts<T>::min_safe_u64[]; |
2139 | | |
2140 | | #endif |
2141 | | |
2142 | | template <typename UC> |
2143 | | fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) { |
2144 | | return int_luts<>::chdigit[static_cast<unsigned char>(c)]; |
2145 | | } |
2146 | | |
2147 | 0 | fastfloat_really_inline constexpr size_t max_digits_u64(int base) { |
2148 | 0 | return int_luts<>::maxdigits_u64[base - 2]; |
2149 | 0 | } |
2150 | | |
2151 | | // If a u64 is exactly max_digits_u64() in length, this is |
2152 | | // the value below which it has definitely overflowed. |
2153 | 0 | fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) { |
2154 | 0 | return int_luts<>::min_safe_u64[base - 2]; |
2155 | 0 | } |
2156 | | |
2157 | | static_assert(tinyobj_ff::is_same<equiv_uint_t<double>, uint64_t>::value, |
2158 | | "equiv_uint should be uint64_t for double"); |
2159 | | static_assert(std::numeric_limits<double>::is_iec559, |
2160 | | "double must fulfill the requirements of IEC 559 (IEEE 754)"); |
2161 | | |
2162 | | static_assert(tinyobj_ff::is_same<equiv_uint_t<float>, uint32_t>::value, |
2163 | | "equiv_uint should be uint32_t for float"); |
2164 | | static_assert(std::numeric_limits<float>::is_iec559, |
2165 | | "float must fulfill the requirements of IEC 559 (IEEE 754)"); |
2166 | | |
2167 | | #ifdef __STDCPP_FLOAT64_T__ |
2168 | | static_assert(tinyobj_ff::is_same<equiv_uint_t<std::float64_t>, uint64_t>::value, |
2169 | | "equiv_uint should be uint64_t for std::float64_t"); |
2170 | | static_assert( |
2171 | | std::numeric_limits<std::float64_t>::is_iec559, |
2172 | | "std::float64_t must fulfill the requirements of IEC 559 (IEEE 754)"); |
2173 | | #endif // __STDCPP_FLOAT64_T__ |
2174 | | |
2175 | | #ifdef __STDCPP_FLOAT32_T__ |
2176 | | static_assert(tinyobj_ff::is_same<equiv_uint_t<std::float32_t>, uint32_t>::value, |
2177 | | "equiv_uint should be uint32_t for std::float32_t"); |
2178 | | static_assert( |
2179 | | std::numeric_limits<std::float32_t>::is_iec559, |
2180 | | "std::float32_t must fulfill the requirements of IEC 559 (IEEE 754)"); |
2181 | | #endif // __STDCPP_FLOAT32_T__ |
2182 | | |
2183 | | #ifdef __STDCPP_FLOAT16_T__ |
2184 | | static_assert( |
2185 | | tinyobj_ff::is_same<binary_format<std::float16_t>::equiv_uint, uint16_t>::value, |
2186 | | "equiv_uint should be uint16_t for std::float16_t"); |
2187 | | static_assert( |
2188 | | std::numeric_limits<std::float16_t>::is_iec559, |
2189 | | "std::float16_t must fulfill the requirements of IEC 559 (IEEE 754)"); |
2190 | | #endif // __STDCPP_FLOAT16_T__ |
2191 | | |
2192 | | #ifdef __STDCPP_BFLOAT16_T__ |
2193 | | static_assert( |
2194 | | tinyobj_ff::is_same<binary_format<std::bfloat16_t>::equiv_uint, uint16_t>::value, |
2195 | | "equiv_uint should be uint16_t for std::bfloat16_t"); |
2196 | | static_assert( |
2197 | | std::numeric_limits<std::bfloat16_t>::is_iec559, |
2198 | | "std::bfloat16_t must fulfill the requirements of IEC 559 (IEEE 754)"); |
2199 | | #endif // __STDCPP_BFLOAT16_T__ |
2200 | | |
2201 | 0 | constexpr chars_format operator~(chars_format rhs) noexcept { |
2202 | 0 | using int_type = tinyobj_ff::underlying_type<chars_format>::type; |
2203 | 0 | return static_cast<chars_format>(~static_cast<int_type>(rhs)); |
2204 | 0 | } |
2205 | | |
2206 | 29.1k | constexpr chars_format operator&(chars_format lhs, chars_format rhs) noexcept { |
2207 | 29.1k | using int_type = tinyobj_ff::underlying_type<chars_format>::type; |
2208 | 29.1k | return static_cast<chars_format>(static_cast<int_type>(lhs) & |
2209 | 29.1k | static_cast<int_type>(rhs)); |
2210 | 29.1k | } |
2211 | | |
2212 | 5.14k | constexpr chars_format operator|(chars_format lhs, chars_format rhs) noexcept { |
2213 | 5.14k | using int_type = tinyobj_ff::underlying_type<chars_format>::type; |
2214 | 5.14k | return static_cast<chars_format>(static_cast<int_type>(lhs) | |
2215 | 5.14k | static_cast<int_type>(rhs)); |
2216 | 5.14k | } |
2217 | | |
2218 | 0 | constexpr chars_format operator^(chars_format lhs, chars_format rhs) noexcept { |
2219 | 0 | using int_type = tinyobj_ff::underlying_type<chars_format>::type; |
2220 | 0 | return static_cast<chars_format>(static_cast<int_type>(lhs) ^ |
2221 | 0 | static_cast<int_type>(rhs)); |
2222 | 0 | } |
2223 | | |
2224 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format & |
2225 | 0 | operator&=(chars_format &lhs, chars_format rhs) noexcept { |
2226 | 0 | return lhs = (lhs & rhs); |
2227 | 0 | } |
2228 | | |
2229 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format & |
2230 | 0 | operator|=(chars_format &lhs, chars_format rhs) noexcept { |
2231 | 0 | return lhs = (lhs | rhs); |
2232 | 0 | } |
2233 | | |
2234 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format & |
2235 | 0 | operator^=(chars_format &lhs, chars_format rhs) noexcept { |
2236 | 0 | return lhs = (lhs ^ rhs); |
2237 | 0 | } |
2238 | | |
2239 | | namespace detail { |
2240 | | // adjust for deprecated feature macros |
2241 | 10.2k | constexpr chars_format adjust_for_feature_macros(chars_format fmt) { |
2242 | 10.2k | return fmt |
2243 | | #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS |
2244 | | | chars_format::allow_leading_plus |
2245 | | #endif |
2246 | | #ifdef FASTFLOAT_SKIP_WHITE_SPACE |
2247 | | | chars_format::skip_white_space |
2248 | | #endif |
2249 | 10.2k | ; |
2250 | 10.2k | } |
2251 | | } // namespace detail |
2252 | | |
2253 | | } // namespace fast_float |
2254 | | |
2255 | | #endif |
2256 | | |
2257 | | |
2258 | | #ifndef FASTFLOAT_FAST_FLOAT_H |
2259 | | #define FASTFLOAT_FAST_FLOAT_H |
2260 | | |
2261 | | |
2262 | | namespace fast_float { |
2263 | | /** |
2264 | | * This function parses the character sequence [first,last) for a number. It |
2265 | | * parses floating-point numbers expecting a locale-indepent format equivalent |
2266 | | * to what is used by std::strtod in the default ("C") locale. The resulting |
2267 | | * floating-point value is the closest floating-point values (using either float |
2268 | | * or double), using the "round to even" convention for values that would |
2269 | | * otherwise fall right in-between two values. That is, we provide exact parsing |
2270 | | * according to the IEEE standard. |
2271 | | * |
2272 | | * Given a successful parse, the pointer (`ptr`) in the returned value is set to |
2273 | | * point right after the parsed number, and the `value` referenced is set to the |
2274 | | * parsed value. In case of error, the returned `ec` contains a representative |
2275 | | * error, otherwise the default (`tinyobj_ff::ff_errc()`) value is stored. |
2276 | | * |
2277 | | * The implementation does not throw and does not allocate memory (e.g., with |
2278 | | * `new` or `malloc`). |
2279 | | * |
2280 | | * Like the C++17 standard, the `fast_float::from_chars` functions take an |
2281 | | * optional last argument of the type `fast_float::chars_format`. It is a bitset |
2282 | | * value: we check whether `fmt & fast_float::chars_format::fixed` and `fmt & |
2283 | | * fast_float::chars_format::scientific` are set to determine whether we allow |
2284 | | * the fixed point and scientific notation respectively. The default is |
2285 | | * `fast_float::chars_format::general` which allows both `fixed` and |
2286 | | * `scientific`. |
2287 | | */ |
2288 | | template <typename T, typename UC = char, |
2289 | | typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>::value)> |
2290 | | FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> |
2291 | | from_chars(UC const *first, UC const *last, T &value, |
2292 | | chars_format fmt = chars_format::general) noexcept; |
2293 | | |
2294 | | /** |
2295 | | * Like from_chars, but accepts an `options` argument to govern number parsing. |
2296 | | * Both for floating-point types and integer types. |
2297 | | */ |
2298 | | template <typename T, typename UC = char> |
2299 | | FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> |
2300 | | from_chars_advanced(UC const *first, UC const *last, T &value, |
2301 | | parse_options_t<UC> options) noexcept; |
2302 | | |
2303 | | /** |
2304 | | * from_chars for integer types. |
2305 | | */ |
2306 | | template <typename T, typename UC = char, |
2307 | | typename = FASTFLOAT_ENABLE_IF(is_supported_integer_type<T>::value)> |
2308 | | FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> |
2309 | | from_chars(UC const *first, UC const *last, T &value, int base = 10) noexcept; |
2310 | | |
2311 | | } // namespace fast_float |
2312 | | |
2313 | | #endif // FASTFLOAT_FAST_FLOAT_H |
2314 | | |
2315 | | #ifndef FASTFLOAT_ASCII_NUMBER_H |
2316 | | #define FASTFLOAT_ASCII_NUMBER_H |
2317 | | |
2318 | | #include <cctype> |
2319 | | #include <cstring> |
2320 | | #include <limits> |
2321 | | |
2322 | | |
2323 | | #ifdef FASTFLOAT_SSE2 |
2324 | | #include <emmintrin.h> |
2325 | | #endif |
2326 | | |
2327 | | #ifdef FASTFLOAT_NEON |
2328 | | #include <arm_neon.h> |
2329 | | #endif |
2330 | | |
2331 | | namespace fast_float { |
2332 | | |
2333 | 0 | template <typename UC> fastfloat_really_inline constexpr bool has_simd_opt() { |
2334 | 0 | #ifdef FASTFLOAT_HAS_SIMD |
2335 | 0 | return tinyobj_ff::is_same<UC, char16_t>::value; |
2336 | 0 | #else |
2337 | 0 | return false; |
2338 | 0 | #endif |
2339 | 0 | } |
2340 | | |
2341 | | // Next function can be micro-optimized, but compilers are entirely |
2342 | | // able to optimize it well. |
2343 | | template <typename UC> |
2344 | 102k | fastfloat_really_inline constexpr bool is_integer(UC c) noexcept { |
2345 | 102k | return !(c > UC('9') || c < UC('0')); |
2346 | 102k | } |
2347 | | |
2348 | 0 | fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) { |
2349 | 0 | return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 | |
2350 | 0 | (val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 | |
2351 | 0 | (val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 | |
2352 | 0 | (val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56; |
2353 | 0 | } |
2354 | | |
2355 | | // Read 8 UC into a u64. Truncates UC if not char. |
2356 | | template <typename UC> |
2357 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t |
2358 | 403 | read8_to_u64(UC const *chars) { |
2359 | 403 | if (cpp20_and_in_constexpr() || !tinyobj_ff::is_same<UC, char>::value) { |
2360 | 0 | uint64_t val = 0; |
2361 | 0 | for (int i = 0; i < 8; ++i) { |
2362 | 0 | val |= uint64_t(uint8_t(*chars)) << (i * 8); |
2363 | 0 | ++chars; |
2364 | 0 | } |
2365 | 0 | return val; |
2366 | 0 | } |
2367 | 403 | uint64_t val; |
2368 | 403 | ::memcpy(&val, chars, sizeof(uint64_t)); |
2369 | | #if FASTFLOAT_IS_BIG_ENDIAN == 1 |
2370 | | // Need to read as-if the number was in little-endian order. |
2371 | | val = byteswap(val); |
2372 | | #endif |
2373 | 403 | return val; |
2374 | 403 | } |
2375 | | |
2376 | | #ifdef FASTFLOAT_SSE2 |
2377 | | |
2378 | 0 | fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) { |
2379 | 0 | FASTFLOAT_SIMD_DISABLE_WARNINGS |
2380 | 0 | __m128i const packed = _mm_packus_epi16(data, data); |
2381 | 0 | #ifdef FASTFLOAT_64BIT |
2382 | 0 | return uint64_t(_mm_cvtsi128_si64(packed)); |
2383 | 0 | #else |
2384 | 0 | uint64_t value; |
2385 | 0 | // Visual Studio + older versions of GCC don't support _mm_storeu_si64 |
2386 | 0 | _mm_storel_epi64(reinterpret_cast<__m128i *>(&value), packed); |
2387 | 0 | return value; |
2388 | 0 | #endif |
2389 | 0 | FASTFLOAT_SIMD_RESTORE_WARNINGS |
2390 | 0 | } |
2391 | | |
2392 | 0 | fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) { |
2393 | 0 | FASTFLOAT_SIMD_DISABLE_WARNINGS |
2394 | 0 | return simd_read8_to_u64( |
2395 | 0 | _mm_loadu_si128(reinterpret_cast<__m128i const *>(chars))); |
2396 | 0 | FASTFLOAT_SIMD_RESTORE_WARNINGS |
2397 | 0 | } |
2398 | | |
2399 | | #elif defined(FASTFLOAT_NEON) |
2400 | | |
2401 | | fastfloat_really_inline uint64_t simd_read8_to_u64(uint16x8_t const data) { |
2402 | | FASTFLOAT_SIMD_DISABLE_WARNINGS |
2403 | | uint8x8_t utf8_packed = vmovn_u16(data); |
2404 | | return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0); |
2405 | | FASTFLOAT_SIMD_RESTORE_WARNINGS |
2406 | | } |
2407 | | |
2408 | | fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) { |
2409 | | FASTFLOAT_SIMD_DISABLE_WARNINGS |
2410 | | return simd_read8_to_u64( |
2411 | | vld1q_u16(reinterpret_cast<uint16_t const *>(chars))); |
2412 | | FASTFLOAT_SIMD_RESTORE_WARNINGS |
2413 | | } |
2414 | | |
2415 | | #endif // FASTFLOAT_SSE2 |
2416 | | |
2417 | | // MSVC SFINAE is broken pre-VS2017 |
2418 | | #if defined(_MSC_VER) && _MSC_VER <= 1900 |
2419 | | template <typename UC> |
2420 | | #else |
2421 | | template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0> |
2422 | | #endif |
2423 | | // dummy for compile |
2424 | 0 | uint64_t simd_read8_to_u64(UC const *) { |
2425 | 0 | return 0; |
2426 | 0 | } |
2427 | | |
2428 | | // credit @aqrit |
2429 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t |
2430 | 190 | parse_eight_digits_unrolled(uint64_t val) { |
2431 | 190 | uint64_t const mask = 0x000000FF000000FF; |
2432 | 190 | uint64_t const mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) |
2433 | 190 | uint64_t const mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) |
2434 | 190 | val -= 0x3030303030303030; |
2435 | 190 | val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; |
2436 | 190 | val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; |
2437 | 190 | return uint32_t(val); |
2438 | 190 | } |
2439 | | |
2440 | | // Call this if chars are definitely 8 digits. |
2441 | | template <typename UC> |
2442 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t |
2443 | 0 | parse_eight_digits_unrolled(UC const *chars) noexcept { |
2444 | 0 | if (cpp20_and_in_constexpr() || !has_simd_opt<UC>()) { |
2445 | 0 | return parse_eight_digits_unrolled(read8_to_u64(chars)); // truncation okay |
2446 | 0 | } |
2447 | 0 | return parse_eight_digits_unrolled(simd_read8_to_u64(chars)); |
2448 | 0 | } |
2449 | | |
2450 | | // credit @aqrit |
2451 | | fastfloat_really_inline constexpr bool |
2452 | 213 | is_made_of_eight_digits_fast(uint64_t val) noexcept { |
2453 | 213 | return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & |
2454 | 213 | 0x8080808080808080)); |
2455 | 213 | } |
2456 | | |
2457 | | #ifdef FASTFLOAT_HAS_SIMD |
2458 | | |
2459 | | // Call this if chars might not be 8 digits. |
2460 | | // Using this style (instead of is_made_of_eight_digits_fast() then |
2461 | | // parse_eight_digits_unrolled()) ensures we don't load SIMD registers twice. |
2462 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool |
2463 | | simd_parse_if_eight_digits_unrolled(char16_t const *chars, |
2464 | 0 | uint64_t &i) noexcept { |
2465 | 0 | if (cpp20_and_in_constexpr()) { |
2466 | 0 | return false; |
2467 | 0 | } |
2468 | 0 | #ifdef FASTFLOAT_SSE2 |
2469 | 0 | FASTFLOAT_SIMD_DISABLE_WARNINGS |
2470 | 0 | __m128i const data = |
2471 | 0 | _mm_loadu_si128(reinterpret_cast<__m128i const *>(chars)); |
2472 | 0 |
|
2473 | 0 | // (x - '0') <= 9 |
2474 | 0 | // http://0x80.pl/articles/simd-parsing-int-sequences.html |
2475 | 0 | __m128i const t0 = _mm_add_epi16(data, _mm_set1_epi16(32720)); |
2476 | 0 | __m128i const t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759)); |
2477 | 0 |
|
2478 | 0 | if (_mm_movemask_epi8(t1) == 0) { |
2479 | 0 | i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data)); |
2480 | 0 | return true; |
2481 | 0 | } else |
2482 | 0 | return false; |
2483 | 0 | FASTFLOAT_SIMD_RESTORE_WARNINGS |
2484 | 0 | #elif defined(FASTFLOAT_NEON) |
2485 | 0 | FASTFLOAT_SIMD_DISABLE_WARNINGS |
2486 | 0 | uint16x8_t const data = vld1q_u16(reinterpret_cast<uint16_t const *>(chars)); |
2487 | 0 |
|
2488 | 0 | // (x - '0') <= 9 |
2489 | 0 | // http://0x80.pl/articles/simd-parsing-int-sequences.html |
2490 | 0 | uint16x8_t const t0 = vsubq_u16(data, vmovq_n_u16('0')); |
2491 | 0 | uint16x8_t const mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1)); |
2492 | 0 |
|
2493 | 0 | if (vminvq_u16(mask) == 0xFFFF) { |
2494 | 0 | i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data)); |
2495 | 0 | return true; |
2496 | 0 | } else |
2497 | 0 | return false; |
2498 | 0 | FASTFLOAT_SIMD_RESTORE_WARNINGS |
2499 | 0 | #else |
2500 | 0 | (void)chars; |
2501 | 0 | (void)i; |
2502 | 0 | return false; |
2503 | 0 | #endif // FASTFLOAT_SSE2 |
2504 | 0 | } |
2505 | | |
2506 | | #endif // FASTFLOAT_HAS_SIMD |
2507 | | |
2508 | | // MSVC SFINAE is broken pre-VS2017 |
2509 | | #if defined(_MSC_VER) && _MSC_VER <= 1900 |
2510 | | template <typename UC> |
2511 | | #else |
2512 | | template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0> |
2513 | | #endif |
2514 | | // dummy for compile |
2515 | | bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) { |
2516 | | return 0; |
2517 | | } |
2518 | | |
2519 | | template <typename UC, FASTFLOAT_ENABLE_IF(!tinyobj_ff::is_same<UC, char>::value) = 0> |
2520 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void |
2521 | | loop_parse_if_eight_digits(UC const *&p, UC const *const pend, uint64_t &i) { |
2522 | | if (!has_simd_opt<UC>()) { |
2523 | | return; |
2524 | | } |
2525 | | while ((tinyobj_ff::distance(p, pend) >= 8) && |
2526 | | simd_parse_if_eight_digits_unrolled( |
2527 | | p, i)) { // in rare cases, this will overflow, but that's ok |
2528 | | p += 8; |
2529 | | } |
2530 | | } |
2531 | | |
2532 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void |
2533 | | loop_parse_if_eight_digits(char const *&p, char const *const pend, |
2534 | 412 | uint64_t &i) { |
2535 | | // optimizes better than parse_if_eight_digits_unrolled() for UC = char. |
2536 | 602 | while ((tinyobj_ff::distance(p, pend) >= 8) && |
2537 | 213 | is_made_of_eight_digits_fast(read8_to_u64(p))) { |
2538 | 190 | i = i * 100000000 + |
2539 | 190 | parse_eight_digits_unrolled(read8_to_u64( |
2540 | 190 | p)); // in rare cases, this will overflow, but that's ok |
2541 | 190 | p += 8; |
2542 | 190 | } |
2543 | 412 | } |
2544 | | |
2545 | | enum class parse_error { |
2546 | | no_error, |
2547 | | // [JSON-only] The minus sign must be followed by an integer. |
2548 | | missing_integer_after_sign, |
2549 | | // A sign must be followed by an integer or dot. |
2550 | | missing_integer_or_dot_after_sign, |
2551 | | // [JSON-only] The integer part must not have leading zeros. |
2552 | | leading_zeros_in_integer_part, |
2553 | | // [JSON-only] The integer part must have at least one digit. |
2554 | | no_digits_in_integer_part, |
2555 | | // [JSON-only] If there is a decimal point, there must be digits in the |
2556 | | // fractional part. |
2557 | | no_digits_in_fractional_part, |
2558 | | // The mantissa must have at least one digit. |
2559 | | no_digits_in_mantissa, |
2560 | | // Scientific notation requires an exponential part. |
2561 | | missing_exponential_part, |
2562 | | }; |
2563 | | |
2564 | | template <typename UC> struct parsed_number_string_t { |
2565 | | int64_t exponent{0}; |
2566 | | uint64_t mantissa{0}; |
2567 | | UC const *lastmatch{nullptr}; |
2568 | | bool negative{false}; |
2569 | | bool valid{false}; |
2570 | | bool too_many_digits{false}; |
2571 | | // contains the range of the significant digits |
2572 | | span<UC const> integer{}; // non-nullable |
2573 | | span<UC const> fraction{}; // nullable |
2574 | | parse_error error{parse_error::no_error}; |
2575 | | }; |
2576 | | |
2577 | | using byte_span = span<char const>; |
2578 | | using parsed_number_string = parsed_number_string_t<char>; |
2579 | | |
2580 | | template <typename UC> |
2581 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC> |
2582 | 2.29k | report_parse_error(UC const *p, parse_error error) { |
2583 | 2.29k | parsed_number_string_t<UC> answer; |
2584 | 2.29k | answer.valid = false; |
2585 | 2.29k | answer.lastmatch = p; |
2586 | 2.29k | answer.error = error; |
2587 | 2.29k | return answer; |
2588 | 2.29k | } |
2589 | | |
2590 | | // Assuming that you use no more than 19 digits, this will |
2591 | | // parse an ASCII string. |
2592 | | template <bool basic_json_fmt, typename UC> |
2593 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC> |
2594 | | parse_number_string(UC const *p, UC const *pend, |
2595 | 5.14k | parse_options_t<UC> options) noexcept { |
2596 | 5.14k | chars_format const fmt = detail::adjust_for_feature_macros(options.format); |
2597 | 5.14k | UC const decimal_point = options.decimal_point; |
2598 | | |
2599 | 5.14k | parsed_number_string_t<UC> answer; |
2600 | 5.14k | answer.valid = false; |
2601 | 5.14k | answer.too_many_digits = false; |
2602 | | // assume p < pend, so dereference without checks; |
2603 | 5.14k | answer.negative = (*p == UC('-')); |
2604 | | // C++17 20.19.3.(7.1) explicitly forbids '+' sign here |
2605 | 5.14k | if ((*p == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) && |
2606 | 4.69k | !basic_json_fmt && *p == UC('+'))) { |
2607 | 479 | ++p; |
2608 | 479 | if (p == pend) { |
2609 | 354 | return report_parse_error<UC>( |
2610 | 354 | p, parse_error::missing_integer_or_dot_after_sign); |
2611 | 354 | } |
2612 | 125 | FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) { |
2613 | 0 | if (!is_integer(*p)) { // a sign must be followed by an integer |
2614 | 0 | return report_parse_error<UC>(p, |
2615 | 0 | parse_error::missing_integer_after_sign); |
2616 | 0 | } |
2617 | 0 | } |
2618 | 125 | else { |
2619 | 125 | if (!is_integer(*p) && |
2620 | 106 | (*p != |
2621 | 106 | decimal_point)) { // a sign must be followed by an integer or the dot |
2622 | 98 | return report_parse_error<UC>( |
2623 | 98 | p, parse_error::missing_integer_or_dot_after_sign); |
2624 | 98 | } |
2625 | 125 | } |
2626 | 125 | } |
2627 | 4.68k | UC const *const start_digits = p; |
2628 | | |
2629 | 4.68k | uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) |
2630 | | |
2631 | 54.3k | while ((p != pend) && is_integer(*p)) { |
2632 | | // a multiplication by 10 is cheaper than an arbitrary integer |
2633 | | // multiplication |
2634 | 49.6k | i = 10 * i + |
2635 | 49.6k | uint64_t(*p - |
2636 | 49.6k | UC('0')); // might overflow, we will handle the overflow later |
2637 | 49.6k | ++p; |
2638 | 49.6k | } |
2639 | 4.68k | UC const *const end_of_integer_part = p; |
2640 | 4.68k | int64_t digit_count = int64_t(end_of_integer_part - start_digits); |
2641 | 4.68k | answer.integer = span<UC const>(start_digits, size_t(digit_count)); |
2642 | 4.68k | FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) { |
2643 | | // at least 1 digit in integer part, without leading zeros |
2644 | 0 | if (digit_count == 0) { |
2645 | 0 | return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part); |
2646 | 0 | } |
2647 | 0 | if ((start_digits[0] == UC('0') && digit_count > 1)) { |
2648 | 0 | return report_parse_error<UC>(start_digits, |
2649 | 0 | parse_error::leading_zeros_in_integer_part); |
2650 | 0 | } |
2651 | 0 | } |
2652 | | |
2653 | 4.68k | int64_t exponent = 0; |
2654 | 4.68k | bool const has_decimal_point = (p != pend) && (*p == decimal_point); |
2655 | 4.68k | if (has_decimal_point) { |
2656 | 412 | ++p; |
2657 | 412 | UC const *before = p; |
2658 | | // can occur at most twice without overflowing, but let it occur more, since |
2659 | | // for integers with many digits, digit parsing is the primary bottleneck. |
2660 | 412 | loop_parse_if_eight_digits(p, pend, i); |
2661 | | |
2662 | 752 | while ((p != pend) && is_integer(*p)) { |
2663 | 340 | uint8_t digit = uint8_t(*p - UC('0')); |
2664 | 340 | ++p; |
2665 | 340 | i = i * 10 + digit; // in rare cases, this will overflow, but that's ok |
2666 | 340 | } |
2667 | 412 | exponent = before - p; |
2668 | 412 | answer.fraction = span<UC const>(before, size_t(p - before)); |
2669 | 412 | digit_count -= exponent; |
2670 | 412 | } |
2671 | 4.68k | FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) { |
2672 | | // at least 1 digit in fractional part |
2673 | 0 | if (has_decimal_point && exponent == 0) { |
2674 | 0 | return report_parse_error<UC>(p, |
2675 | 0 | parse_error::no_digits_in_fractional_part); |
2676 | 0 | } |
2677 | 0 | } |
2678 | 4.68k | else if (digit_count == 0) { // we must have encountered at least one integer! |
2679 | 1.84k | return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa); |
2680 | 1.84k | } |
2681 | 2.84k | int64_t exp_number = 0; // explicit exponential part |
2682 | 2.84k | if ((uint64_t(fmt & chars_format::scientific) && (p != pend) && |
2683 | 1.08k | ((UC('e') == *p) || (UC('E') == *p))) || |
2684 | 2.22k | (uint64_t(fmt & detail::basic_fortran_fmt) && (p != pend) && |
2685 | 0 | ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) || |
2686 | 627 | (UC('D') == *p)))) { |
2687 | 627 | UC const *location_of_e = p; |
2688 | 627 | if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) || |
2689 | 627 | (UC('D') == *p)) { |
2690 | 627 | ++p; |
2691 | 627 | } |
2692 | 627 | bool neg_exp = false; |
2693 | 627 | if ((p != pend) && (UC('-') == *p)) { |
2694 | 46 | neg_exp = true; |
2695 | 46 | ++p; |
2696 | 581 | } else if ((p != pend) && |
2697 | 180 | (UC('+') == |
2698 | 180 | *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) |
2699 | 93 | ++p; |
2700 | 93 | } |
2701 | 627 | if ((p == pend) || !is_integer(*p)) { |
2702 | 519 | if (!uint64_t(fmt & chars_format::fixed)) { |
2703 | | // The exponential part is invalid for scientific notation, so it must |
2704 | | // be a trailing token for fixed notation. However, fixed notation is |
2705 | | // disabled, so report a scientific notation error. |
2706 | 0 | return report_parse_error<UC>(p, parse_error::missing_exponential_part); |
2707 | 0 | } |
2708 | | // Otherwise, we will be ignoring the 'e'. |
2709 | 519 | p = location_of_e; |
2710 | 519 | } else { |
2711 | 48.8k | while ((p != pend) && is_integer(*p)) { |
2712 | 48.7k | uint8_t digit = uint8_t(*p - UC('0')); |
2713 | 48.7k | if (exp_number < 0x10000000) { |
2714 | 48.7k | exp_number = 10 * exp_number + digit; |
2715 | 48.7k | } |
2716 | 48.7k | ++p; |
2717 | 48.7k | } |
2718 | 108 | if (neg_exp) { |
2719 | 31 | exp_number = -exp_number; |
2720 | 31 | } |
2721 | 108 | exponent += exp_number; |
2722 | 108 | } |
2723 | 2.22k | } else { |
2724 | | // If it scientific and not fixed, we have to bail out. |
2725 | 2.22k | if (uint64_t(fmt & chars_format::scientific) && |
2726 | 2.22k | !uint64_t(fmt & chars_format::fixed)) { |
2727 | 0 | return report_parse_error<UC>(p, parse_error::missing_exponential_part); |
2728 | 0 | } |
2729 | 2.22k | } |
2730 | 2.84k | answer.lastmatch = p; |
2731 | 2.84k | answer.valid = true; |
2732 | | |
2733 | | // If we frequently had to deal with long strings of digits, |
2734 | | // we could extend our code by using a 128-bit integer instead |
2735 | | // of a 64-bit integer. However, this is uncommon. |
2736 | | // |
2737 | | // We can deal with up to 19 digits. |
2738 | 2.84k | if (digit_count > 19) { // this is uncommon |
2739 | | // It is possible that the integer had an overflow. |
2740 | | // We have to handle the case where we have 0.0000somenumber. |
2741 | | // We need to be mindful of the case where we only have zeroes... |
2742 | | // E.g., 0.000000000...000. |
2743 | 15 | UC const *start = start_digits; |
2744 | 46.6k | while ((start != pend) && (*start == UC('0') || *start == decimal_point)) { |
2745 | 46.6k | if (*start == UC('0')) { |
2746 | 46.6k | digit_count--; |
2747 | 46.6k | } |
2748 | 46.6k | start++; |
2749 | 46.6k | } |
2750 | | |
2751 | 15 | if (digit_count > 19) { |
2752 | 10 | answer.too_many_digits = true; |
2753 | | // Let us start again, this time, avoiding overflows. |
2754 | | // We don't need to check if is_integer, since we use the |
2755 | | // pre-tokenized spans from above. |
2756 | 10 | i = 0; |
2757 | 10 | p = answer.integer.ptr; |
2758 | 10 | UC const *int_end = p + answer.integer.len(); |
2759 | 10 | uint64_t const minimal_nineteen_digit_integer{1000000000000000000}; |
2760 | 950 | while ((i < minimal_nineteen_digit_integer) && (p != int_end)) { |
2761 | 940 | i = i * 10 + uint64_t(*p - UC('0')); |
2762 | 940 | ++p; |
2763 | 940 | } |
2764 | 10 | if (i >= minimal_nineteen_digit_integer) { // We have a big integers |
2765 | 5 | exponent = end_of_integer_part - p + exp_number; |
2766 | 5 | } else { // We have a value with a fractional component. |
2767 | 5 | p = answer.fraction.ptr; |
2768 | 5 | UC const *frac_end = p + answer.fraction.len(); |
2769 | 214 | while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) { |
2770 | 209 | i = i * 10 + uint64_t(*p - UC('0')); |
2771 | 209 | ++p; |
2772 | 209 | } |
2773 | 5 | exponent = answer.fraction.ptr - p + exp_number; |
2774 | 5 | } |
2775 | | // We have now corrected both exponent and i, to a truncated value |
2776 | 10 | } |
2777 | 15 | } |
2778 | 2.84k | answer.exponent = exponent; |
2779 | 2.84k | answer.mantissa = i; |
2780 | 2.84k | return answer; |
2781 | 2.84k | } Unexecuted instantiation: fast_float::parsed_number_string_t<char> fast_float::parse_number_string<true, char>(char const*, char const*, fast_float::parse_options_t<char>) fast_float::parsed_number_string_t<char> fast_float::parse_number_string<false, char>(char const*, char const*, fast_float::parse_options_t<char>) Line | Count | Source | 2595 | 5.14k | parse_options_t<UC> options) noexcept { | 2596 | 5.14k | chars_format const fmt = detail::adjust_for_feature_macros(options.format); | 2597 | 5.14k | UC const decimal_point = options.decimal_point; | 2598 | | | 2599 | 5.14k | parsed_number_string_t<UC> answer; | 2600 | 5.14k | answer.valid = false; | 2601 | 5.14k | answer.too_many_digits = false; | 2602 | | // assume p < pend, so dereference without checks; | 2603 | 5.14k | answer.negative = (*p == UC('-')); | 2604 | | // C++17 20.19.3.(7.1) explicitly forbids '+' sign here | 2605 | 5.14k | if ((*p == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) && | 2606 | 4.69k | !basic_json_fmt && *p == UC('+'))) { | 2607 | 479 | ++p; | 2608 | 479 | if (p == pend) { | 2609 | 354 | return report_parse_error<UC>( | 2610 | 354 | p, parse_error::missing_integer_or_dot_after_sign); | 2611 | 354 | } | 2612 | 125 | FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) { | 2613 | 0 | if (!is_integer(*p)) { // a sign must be followed by an integer | 2614 | 0 | return report_parse_error<UC>(p, | 2615 | 0 | parse_error::missing_integer_after_sign); | 2616 | 0 | } | 2617 | 0 | } | 2618 | 125 | else { | 2619 | 125 | if (!is_integer(*p) && | 2620 | 106 | (*p != | 2621 | 106 | decimal_point)) { // a sign must be followed by an integer or the dot | 2622 | 98 | return report_parse_error<UC>( | 2623 | 98 | p, parse_error::missing_integer_or_dot_after_sign); | 2624 | 98 | } | 2625 | 125 | } | 2626 | 125 | } | 2627 | 4.68k | UC const *const start_digits = p; | 2628 | | | 2629 | 4.68k | uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) | 2630 | | | 2631 | 54.3k | while ((p != pend) && is_integer(*p)) { | 2632 | | // a multiplication by 10 is cheaper than an arbitrary integer | 2633 | | // multiplication | 2634 | 49.6k | i = 10 * i + | 2635 | 49.6k | uint64_t(*p - | 2636 | 49.6k | UC('0')); // might overflow, we will handle the overflow later | 2637 | 49.6k | ++p; | 2638 | 49.6k | } | 2639 | 4.68k | UC const *const end_of_integer_part = p; | 2640 | 4.68k | int64_t digit_count = int64_t(end_of_integer_part - start_digits); | 2641 | 4.68k | answer.integer = span<UC const>(start_digits, size_t(digit_count)); | 2642 | 4.68k | FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) { | 2643 | | // at least 1 digit in integer part, without leading zeros | 2644 | 0 | if (digit_count == 0) { | 2645 | 0 | return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part); | 2646 | 0 | } | 2647 | 0 | if ((start_digits[0] == UC('0') && digit_count > 1)) { | 2648 | 0 | return report_parse_error<UC>(start_digits, | 2649 | 0 | parse_error::leading_zeros_in_integer_part); | 2650 | 0 | } | 2651 | 0 | } | 2652 | | | 2653 | 4.68k | int64_t exponent = 0; | 2654 | 4.68k | bool const has_decimal_point = (p != pend) && (*p == decimal_point); | 2655 | 4.68k | if (has_decimal_point) { | 2656 | 412 | ++p; | 2657 | 412 | UC const *before = p; | 2658 | | // can occur at most twice without overflowing, but let it occur more, since | 2659 | | // for integers with many digits, digit parsing is the primary bottleneck. | 2660 | 412 | loop_parse_if_eight_digits(p, pend, i); | 2661 | | | 2662 | 752 | while ((p != pend) && is_integer(*p)) { | 2663 | 340 | uint8_t digit = uint8_t(*p - UC('0')); | 2664 | 340 | ++p; | 2665 | 340 | i = i * 10 + digit; // in rare cases, this will overflow, but that's ok | 2666 | 340 | } | 2667 | 412 | exponent = before - p; | 2668 | 412 | answer.fraction = span<UC const>(before, size_t(p - before)); | 2669 | 412 | digit_count -= exponent; | 2670 | 412 | } | 2671 | 4.68k | FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) { | 2672 | | // at least 1 digit in fractional part | 2673 | 0 | if (has_decimal_point && exponent == 0) { | 2674 | 0 | return report_parse_error<UC>(p, | 2675 | 0 | parse_error::no_digits_in_fractional_part); | 2676 | 0 | } | 2677 | 0 | } | 2678 | 4.68k | else if (digit_count == 0) { // we must have encountered at least one integer! | 2679 | 1.84k | return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa); | 2680 | 1.84k | } | 2681 | 2.84k | int64_t exp_number = 0; // explicit exponential part | 2682 | 2.84k | if ((uint64_t(fmt & chars_format::scientific) && (p != pend) && | 2683 | 1.08k | ((UC('e') == *p) || (UC('E') == *p))) || | 2684 | 2.22k | (uint64_t(fmt & detail::basic_fortran_fmt) && (p != pend) && | 2685 | 0 | ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) || | 2686 | 627 | (UC('D') == *p)))) { | 2687 | 627 | UC const *location_of_e = p; | 2688 | 627 | if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) || | 2689 | 627 | (UC('D') == *p)) { | 2690 | 627 | ++p; | 2691 | 627 | } | 2692 | 627 | bool neg_exp = false; | 2693 | 627 | if ((p != pend) && (UC('-') == *p)) { | 2694 | 46 | neg_exp = true; | 2695 | 46 | ++p; | 2696 | 581 | } else if ((p != pend) && | 2697 | 180 | (UC('+') == | 2698 | 180 | *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) | 2699 | 93 | ++p; | 2700 | 93 | } | 2701 | 627 | if ((p == pend) || !is_integer(*p)) { | 2702 | 519 | if (!uint64_t(fmt & chars_format::fixed)) { | 2703 | | // The exponential part is invalid for scientific notation, so it must | 2704 | | // be a trailing token for fixed notation. However, fixed notation is | 2705 | | // disabled, so report a scientific notation error. | 2706 | 0 | return report_parse_error<UC>(p, parse_error::missing_exponential_part); | 2707 | 0 | } | 2708 | | // Otherwise, we will be ignoring the 'e'. | 2709 | 519 | p = location_of_e; | 2710 | 519 | } else { | 2711 | 48.8k | while ((p != pend) && is_integer(*p)) { | 2712 | 48.7k | uint8_t digit = uint8_t(*p - UC('0')); | 2713 | 48.7k | if (exp_number < 0x10000000) { | 2714 | 48.7k | exp_number = 10 * exp_number + digit; | 2715 | 48.7k | } | 2716 | 48.7k | ++p; | 2717 | 48.7k | } | 2718 | 108 | if (neg_exp) { | 2719 | 31 | exp_number = -exp_number; | 2720 | 31 | } | 2721 | 108 | exponent += exp_number; | 2722 | 108 | } | 2723 | 2.22k | } else { | 2724 | | // If it scientific and not fixed, we have to bail out. | 2725 | 2.22k | if (uint64_t(fmt & chars_format::scientific) && | 2726 | 2.22k | !uint64_t(fmt & chars_format::fixed)) { | 2727 | 0 | return report_parse_error<UC>(p, parse_error::missing_exponential_part); | 2728 | 0 | } | 2729 | 2.22k | } | 2730 | 2.84k | answer.lastmatch = p; | 2731 | 2.84k | answer.valid = true; | 2732 | | | 2733 | | // If we frequently had to deal with long strings of digits, | 2734 | | // we could extend our code by using a 128-bit integer instead | 2735 | | // of a 64-bit integer. However, this is uncommon. | 2736 | | // | 2737 | | // We can deal with up to 19 digits. | 2738 | 2.84k | if (digit_count > 19) { // this is uncommon | 2739 | | // It is possible that the integer had an overflow. | 2740 | | // We have to handle the case where we have 0.0000somenumber. | 2741 | | // We need to be mindful of the case where we only have zeroes... | 2742 | | // E.g., 0.000000000...000. | 2743 | 15 | UC const *start = start_digits; | 2744 | 46.6k | while ((start != pend) && (*start == UC('0') || *start == decimal_point)) { | 2745 | 46.6k | if (*start == UC('0')) { | 2746 | 46.6k | digit_count--; | 2747 | 46.6k | } | 2748 | 46.6k | start++; | 2749 | 46.6k | } | 2750 | | | 2751 | 15 | if (digit_count > 19) { | 2752 | 10 | answer.too_many_digits = true; | 2753 | | // Let us start again, this time, avoiding overflows. | 2754 | | // We don't need to check if is_integer, since we use the | 2755 | | // pre-tokenized spans from above. | 2756 | 10 | i = 0; | 2757 | 10 | p = answer.integer.ptr; | 2758 | 10 | UC const *int_end = p + answer.integer.len(); | 2759 | 10 | uint64_t const minimal_nineteen_digit_integer{1000000000000000000}; | 2760 | 950 | while ((i < minimal_nineteen_digit_integer) && (p != int_end)) { | 2761 | 940 | i = i * 10 + uint64_t(*p - UC('0')); | 2762 | 940 | ++p; | 2763 | 940 | } | 2764 | 10 | if (i >= minimal_nineteen_digit_integer) { // We have a big integers | 2765 | 5 | exponent = end_of_integer_part - p + exp_number; | 2766 | 5 | } else { // We have a value with a fractional component. | 2767 | 5 | p = answer.fraction.ptr; | 2768 | 5 | UC const *frac_end = p + answer.fraction.len(); | 2769 | 214 | while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) { | 2770 | 209 | i = i * 10 + uint64_t(*p - UC('0')); | 2771 | 209 | ++p; | 2772 | 209 | } | 2773 | 5 | exponent = answer.fraction.ptr - p + exp_number; | 2774 | 5 | } | 2775 | | // We have now corrected both exponent and i, to a truncated value | 2776 | 10 | } | 2777 | 15 | } | 2778 | 2.84k | answer.exponent = exponent; | 2779 | 2.84k | answer.mantissa = i; | 2780 | 2.84k | return answer; | 2781 | 2.84k | } |
|
2782 | | |
2783 | | template <typename T, typename UC> |
2784 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> |
2785 | | parse_int_string(UC const *p, UC const *pend, T &value, |
2786 | | parse_options_t<UC> options) { |
2787 | | chars_format const fmt = detail::adjust_for_feature_macros(options.format); |
2788 | | int const base = options.base; |
2789 | | |
2790 | | from_chars_result_t<UC> answer; |
2791 | | |
2792 | | UC const *const first = p; |
2793 | | |
2794 | | bool const negative = (*p == UC('-')); |
2795 | | #ifdef FASTFLOAT_VISUAL_STUDIO |
2796 | | #pragma warning(push) |
2797 | | #pragma warning(disable : 4127) |
2798 | | #endif |
2799 | | if (!tinyobj_ff::is_signed<T>::value && negative) { |
2800 | | #ifdef FASTFLOAT_VISUAL_STUDIO |
2801 | | #pragma warning(pop) |
2802 | | #endif |
2803 | | answer.ec = tinyobj_ff::ff_errc::invalid_argument; |
2804 | | answer.ptr = first; |
2805 | | return answer; |
2806 | | } |
2807 | | if ((*p == UC('-')) || |
2808 | | (uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) { |
2809 | | ++p; |
2810 | | } |
2811 | | |
2812 | | UC const *const start_num = p; |
2813 | | |
2814 | | while (p != pend && *p == UC('0')) { |
2815 | | ++p; |
2816 | | } |
2817 | | |
2818 | | bool const has_leading_zeros = p > start_num; |
2819 | | |
2820 | | UC const *const start_digits = p; |
2821 | | |
2822 | | uint64_t i = 0; |
2823 | | if (base == 10) { |
2824 | | loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible |
2825 | | } |
2826 | | while (p != pend) { |
2827 | | uint8_t digit = ch_to_digit(*p); |
2828 | | if (digit >= base) { |
2829 | | break; |
2830 | | } |
2831 | | i = uint64_t(base) * i + digit; // might overflow, check this later |
2832 | | p++; |
2833 | | } |
2834 | | |
2835 | | size_t digit_count = size_t(p - start_digits); |
2836 | | |
2837 | | if (digit_count == 0) { |
2838 | | if (has_leading_zeros) { |
2839 | | value = 0; |
2840 | | answer.ec = tinyobj_ff::ff_errc(); |
2841 | | answer.ptr = p; |
2842 | | } else { |
2843 | | answer.ec = tinyobj_ff::ff_errc::invalid_argument; |
2844 | | answer.ptr = first; |
2845 | | } |
2846 | | return answer; |
2847 | | } |
2848 | | |
2849 | | answer.ptr = p; |
2850 | | |
2851 | | // check u64 overflow |
2852 | | size_t max_digits = max_digits_u64(base); |
2853 | | if (digit_count > max_digits) { |
2854 | | answer.ec = tinyobj_ff::ff_errc::result_out_of_range; |
2855 | | return answer; |
2856 | | } |
2857 | | // this check can be eliminated for all other types, but they will all require |
2858 | | // a max_digits(base) equivalent |
2859 | | if (digit_count == max_digits && i < min_safe_u64(base)) { |
2860 | | answer.ec = tinyobj_ff::ff_errc::result_out_of_range; |
2861 | | return answer; |
2862 | | } |
2863 | | |
2864 | | // check other types overflow |
2865 | | if (!tinyobj_ff::is_same<T, uint64_t>::value) { |
2866 | | if (i > uint64_t(std::numeric_limits<T>::max()) + uint64_t(negative)) { |
2867 | | answer.ec = tinyobj_ff::ff_errc::result_out_of_range; |
2868 | | return answer; |
2869 | | } |
2870 | | } |
2871 | | |
2872 | | if (negative) { |
2873 | | #ifdef FASTFLOAT_VISUAL_STUDIO |
2874 | | #pragma warning(push) |
2875 | | #pragma warning(disable : 4146) |
2876 | | #endif |
2877 | | // this weird workaround is required because: |
2878 | | // - converting unsigned to signed when its value is greater than signed max |
2879 | | // is UB pre-C++23. |
2880 | | // - reinterpret_casting (~i + 1) would work, but it is not constexpr |
2881 | | // this is always optimized into a neg instruction (note: T is an integer |
2882 | | // type) |
2883 | | value = T(-std::numeric_limits<T>::max() - |
2884 | | T(i - uint64_t(std::numeric_limits<T>::max()))); |
2885 | | #ifdef FASTFLOAT_VISUAL_STUDIO |
2886 | | #pragma warning(pop) |
2887 | | #endif |
2888 | | } else { |
2889 | | value = T(i); |
2890 | | } |
2891 | | |
2892 | | answer.ec = tinyobj_ff::ff_errc(); |
2893 | | return answer; |
2894 | | } |
2895 | | |
2896 | | } // namespace fast_float |
2897 | | |
2898 | | #endif |
2899 | | |
2900 | | #ifndef FASTFLOAT_FAST_TABLE_H |
2901 | | #define FASTFLOAT_FAST_TABLE_H |
2902 | | |
2903 | | namespace fast_float { |
2904 | | |
2905 | | /** |
2906 | | * When mapping numbers from decimal to binary, |
2907 | | * we go from w * 10^q to m * 2^p but we have |
2908 | | * 10^q = 5^q * 2^q, so effectively |
2909 | | * we are trying to match |
2910 | | * w * 2^q * 5^q to m * 2^p. Thus the powers of two |
2911 | | * are not a concern since they can be represented |
2912 | | * exactly using the binary notation, only the powers of five |
2913 | | * affect the binary significand. |
2914 | | */ |
2915 | | |
2916 | | /** |
2917 | | * The smallest non-zero float (binary64) is 2^-1074. |
2918 | | * We take as input numbers of the form w x 10^q where w < 2^64. |
2919 | | * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076. |
2920 | | * However, we have that |
2921 | | * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^-1074. |
2922 | | * Thus it is possible for a number of the form w * 10^-342 where |
2923 | | * w is a 64-bit value to be a non-zero floating-point number. |
2924 | | ********* |
2925 | | * Any number of form w * 10^309 where w>= 1 is going to be |
2926 | | * infinite in binary64 so we never need to worry about powers |
2927 | | * of 5 greater than 308. |
2928 | | */ |
2929 | | template <class unused = void> struct powers_template { |
2930 | | |
2931 | | constexpr static int smallest_power_of_five = |
2932 | | binary_format<double>::smallest_power_of_ten(); |
2933 | | constexpr static int largest_power_of_five = |
2934 | | binary_format<double>::largest_power_of_ten(); |
2935 | | constexpr static int number_of_entries = |
2936 | | 2 * (largest_power_of_five - smallest_power_of_five + 1); |
2937 | | // Powers of five from 5^-342 all the way to 5^308 rounded toward one. |
2938 | | constexpr static uint64_t power_of_five_128[number_of_entries] = { |
2939 | | 0xeef453d6923bd65a, 0x113faa2906a13b3f, |
2940 | | 0x9558b4661b6565f8, 0x4ac7ca59a424c507, |
2941 | | 0xbaaee17fa23ebf76, 0x5d79bcf00d2df649, |
2942 | | 0xe95a99df8ace6f53, 0xf4d82c2c107973dc, |
2943 | | 0x91d8a02bb6c10594, 0x79071b9b8a4be869, |
2944 | | 0xb64ec836a47146f9, 0x9748e2826cdee284, |
2945 | | 0xe3e27a444d8d98b7, 0xfd1b1b2308169b25, |
2946 | | 0x8e6d8c6ab0787f72, 0xfe30f0f5e50e20f7, |
2947 | | 0xb208ef855c969f4f, 0xbdbd2d335e51a935, |
2948 | | 0xde8b2b66b3bc4723, 0xad2c788035e61382, |
2949 | | 0x8b16fb203055ac76, 0x4c3bcb5021afcc31, |
2950 | | 0xaddcb9e83c6b1793, 0xdf4abe242a1bbf3d, |
2951 | | 0xd953e8624b85dd78, 0xd71d6dad34a2af0d, |
2952 | | 0x87d4713d6f33aa6b, 0x8672648c40e5ad68, |
2953 | | 0xa9c98d8ccb009506, 0x680efdaf511f18c2, |
2954 | | 0xd43bf0effdc0ba48, 0x212bd1b2566def2, |
2955 | | 0x84a57695fe98746d, 0x14bb630f7604b57, |
2956 | | 0xa5ced43b7e3e9188, 0x419ea3bd35385e2d, |
2957 | | 0xcf42894a5dce35ea, 0x52064cac828675b9, |
2958 | | 0x818995ce7aa0e1b2, 0x7343efebd1940993, |
2959 | | 0xa1ebfb4219491a1f, 0x1014ebe6c5f90bf8, |
2960 | | 0xca66fa129f9b60a6, 0xd41a26e077774ef6, |
2961 | | 0xfd00b897478238d0, 0x8920b098955522b4, |
2962 | | 0x9e20735e8cb16382, 0x55b46e5f5d5535b0, |
2963 | | 0xc5a890362fddbc62, 0xeb2189f734aa831d, |
2964 | | 0xf712b443bbd52b7b, 0xa5e9ec7501d523e4, |
2965 | | 0x9a6bb0aa55653b2d, 0x47b233c92125366e, |
2966 | | 0xc1069cd4eabe89f8, 0x999ec0bb696e840a, |
2967 | | 0xf148440a256e2c76, 0xc00670ea43ca250d, |
2968 | | 0x96cd2a865764dbca, 0x380406926a5e5728, |
2969 | | 0xbc807527ed3e12bc, 0xc605083704f5ecf2, |
2970 | | 0xeba09271e88d976b, 0xf7864a44c633682e, |
2971 | | 0x93445b8731587ea3, 0x7ab3ee6afbe0211d, |
2972 | | 0xb8157268fdae9e4c, 0x5960ea05bad82964, |
2973 | | 0xe61acf033d1a45df, 0x6fb92487298e33bd, |
2974 | | 0x8fd0c16206306bab, 0xa5d3b6d479f8e056, |
2975 | | 0xb3c4f1ba87bc8696, 0x8f48a4899877186c, |
2976 | | 0xe0b62e2929aba83c, 0x331acdabfe94de87, |
2977 | | 0x8c71dcd9ba0b4925, 0x9ff0c08b7f1d0b14, |
2978 | | 0xaf8e5410288e1b6f, 0x7ecf0ae5ee44dd9, |
2979 | | 0xdb71e91432b1a24a, 0xc9e82cd9f69d6150, |
2980 | | 0x892731ac9faf056e, 0xbe311c083a225cd2, |
2981 | | 0xab70fe17c79ac6ca, 0x6dbd630a48aaf406, |
2982 | | 0xd64d3d9db981787d, 0x92cbbccdad5b108, |
2983 | | 0x85f0468293f0eb4e, 0x25bbf56008c58ea5, |
2984 | | 0xa76c582338ed2621, 0xaf2af2b80af6f24e, |
2985 | | 0xd1476e2c07286faa, 0x1af5af660db4aee1, |
2986 | | 0x82cca4db847945ca, 0x50d98d9fc890ed4d, |
2987 | | 0xa37fce126597973c, 0xe50ff107bab528a0, |
2988 | | 0xcc5fc196fefd7d0c, 0x1e53ed49a96272c8, |
2989 | | 0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7a, |
2990 | | 0x9faacf3df73609b1, 0x77b191618c54e9ac, |
2991 | | 0xc795830d75038c1d, 0xd59df5b9ef6a2417, |
2992 | | 0xf97ae3d0d2446f25, 0x4b0573286b44ad1d, |
2993 | | 0x9becce62836ac577, 0x4ee367f9430aec32, |
2994 | | 0xc2e801fb244576d5, 0x229c41f793cda73f, |
2995 | | 0xf3a20279ed56d48a, 0x6b43527578c1110f, |
2996 | | 0x9845418c345644d6, 0x830a13896b78aaa9, |
2997 | | 0xbe5691ef416bd60c, 0x23cc986bc656d553, |
2998 | | 0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa8, |
2999 | | 0x94b3a202eb1c3f39, 0x7bf7d71432f3d6a9, |
3000 | | 0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc53, |
3001 | | 0xe858ad248f5c22c9, 0xd1b3400f8f9cff68, |
3002 | | 0x91376c36d99995be, 0x23100809b9c21fa1, |
3003 | | 0xb58547448ffffb2d, 0xabd40a0c2832a78a, |
3004 | | 0xe2e69915b3fff9f9, 0x16c90c8f323f516c, |
3005 | | 0x8dd01fad907ffc3b, 0xae3da7d97f6792e3, |
3006 | | 0xb1442798f49ffb4a, 0x99cd11cfdf41779c, |
3007 | | 0xdd95317f31c7fa1d, 0x40405643d711d583, |
3008 | | 0x8a7d3eef7f1cfc52, 0x482835ea666b2572, |
3009 | | 0xad1c8eab5ee43b66, 0xda3243650005eecf, |
3010 | | 0xd863b256369d4a40, 0x90bed43e40076a82, |
3011 | | 0x873e4f75e2224e68, 0x5a7744a6e804a291, |
3012 | | 0xa90de3535aaae202, 0x711515d0a205cb36, |
3013 | | 0xd3515c2831559a83, 0xd5a5b44ca873e03, |
3014 | | 0x8412d9991ed58091, 0xe858790afe9486c2, |
3015 | | 0xa5178fff668ae0b6, 0x626e974dbe39a872, |
3016 | | 0xce5d73ff402d98e3, 0xfb0a3d212dc8128f, |
3017 | | 0x80fa687f881c7f8e, 0x7ce66634bc9d0b99, |
3018 | | 0xa139029f6a239f72, 0x1c1fffc1ebc44e80, |
3019 | | 0xc987434744ac874e, 0xa327ffb266b56220, |
3020 | | 0xfbe9141915d7a922, 0x4bf1ff9f0062baa8, |
3021 | | 0x9d71ac8fada6c9b5, 0x6f773fc3603db4a9, |
3022 | | 0xc4ce17b399107c22, 0xcb550fb4384d21d3, |
3023 | | 0xf6019da07f549b2b, 0x7e2a53a146606a48, |
3024 | | 0x99c102844f94e0fb, 0x2eda7444cbfc426d, |
3025 | | 0xc0314325637a1939, 0xfa911155fefb5308, |
3026 | | 0xf03d93eebc589f88, 0x793555ab7eba27ca, |
3027 | | 0x96267c7535b763b5, 0x4bc1558b2f3458de, |
3028 | | 0xbbb01b9283253ca2, 0x9eb1aaedfb016f16, |
3029 | | 0xea9c227723ee8bcb, 0x465e15a979c1cadc, |
3030 | | 0x92a1958a7675175f, 0xbfacd89ec191ec9, |
3031 | | 0xb749faed14125d36, 0xcef980ec671f667b, |
3032 | | 0xe51c79a85916f484, 0x82b7e12780e7401a, |
3033 | | 0x8f31cc0937ae58d2, 0xd1b2ecb8b0908810, |
3034 | | 0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa15, |
3035 | | 0xdfbdcece67006ac9, 0x67a791e093e1d49a, |
3036 | | 0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e0, |
3037 | | 0xaecc49914078536d, 0x58fae9f773886e18, |
3038 | | 0xda7f5bf590966848, 0xaf39a475506a899e, |
3039 | | 0x888f99797a5e012d, 0x6d8406c952429603, |
3040 | | 0xaab37fd7d8f58178, 0xc8e5087ba6d33b83, |
3041 | | 0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a64, |
3042 | | 0x855c3be0a17fcd26, 0x5cf2eea09a55067f, |
3043 | | 0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481e, |
3044 | | 0xd0601d8efc57b08b, 0xf13b94daf124da26, |
3045 | | 0x823c12795db6ce57, 0x76c53d08d6b70858, |
3046 | | 0xa2cb1717b52481ed, 0x54768c4b0c64ca6e, |
3047 | | 0xcb7ddcdda26da268, 0xa9942f5dcf7dfd09, |
3048 | | 0xfe5d54150b090b02, 0xd3f93b35435d7c4c, |
3049 | | 0x9efa548d26e5a6e1, 0xc47bc5014a1a6daf, |
3050 | | 0xc6b8e9b0709f109a, 0x359ab6419ca1091b, |
3051 | | 0xf867241c8cc6d4c0, 0xc30163d203c94b62, |
3052 | | 0x9b407691d7fc44f8, 0x79e0de63425dcf1d, |
3053 | | 0xc21094364dfb5636, 0x985915fc12f542e4, |
3054 | | 0xf294b943e17a2bc4, 0x3e6f5b7b17b2939d, |
3055 | | 0x979cf3ca6cec5b5a, 0xa705992ceecf9c42, |
3056 | | 0xbd8430bd08277231, 0x50c6ff782a838353, |
3057 | | 0xece53cec4a314ebd, 0xa4f8bf5635246428, |
3058 | | 0x940f4613ae5ed136, 0x871b7795e136be99, |
3059 | | 0xb913179899f68584, 0x28e2557b59846e3f, |
3060 | | 0xe757dd7ec07426e5, 0x331aeada2fe589cf, |
3061 | | 0x9096ea6f3848984f, 0x3ff0d2c85def7621, |
3062 | | 0xb4bca50b065abe63, 0xfed077a756b53a9, |
3063 | | 0xe1ebce4dc7f16dfb, 0xd3e8495912c62894, |
3064 | | 0x8d3360f09cf6e4bd, 0x64712dd7abbbd95c, |
3065 | | 0xb080392cc4349dec, 0xbd8d794d96aacfb3, |
3066 | | 0xdca04777f541c567, 0xecf0d7a0fc5583a0, |
3067 | | 0x89e42caaf9491b60, 0xf41686c49db57244, |
3068 | | 0xac5d37d5b79b6239, 0x311c2875c522ced5, |
3069 | | 0xd77485cb25823ac7, 0x7d633293366b828b, |
3070 | | 0x86a8d39ef77164bc, 0xae5dff9c02033197, |
3071 | | 0xa8530886b54dbdeb, 0xd9f57f830283fdfc, |
3072 | | 0xd267caa862a12d66, 0xd072df63c324fd7b, |
3073 | | 0x8380dea93da4bc60, 0x4247cb9e59f71e6d, |
3074 | | 0xa46116538d0deb78, 0x52d9be85f074e608, |
3075 | | 0xcd795be870516656, 0x67902e276c921f8b, |
3076 | | 0x806bd9714632dff6, 0xba1cd8a3db53b6, |
3077 | | 0xa086cfcd97bf97f3, 0x80e8a40eccd228a4, |
3078 | | 0xc8a883c0fdaf7df0, 0x6122cd128006b2cd, |
3079 | | 0xfad2a4b13d1b5d6c, 0x796b805720085f81, |
3080 | | 0x9cc3a6eec6311a63, 0xcbe3303674053bb0, |
3081 | | 0xc3f490aa77bd60fc, 0xbedbfc4411068a9c, |
3082 | | 0xf4f1b4d515acb93b, 0xee92fb5515482d44, |
3083 | | 0x991711052d8bf3c5, 0x751bdd152d4d1c4a, |
3084 | | 0xbf5cd54678eef0b6, 0xd262d45a78a0635d, |
3085 | | 0xef340a98172aace4, 0x86fb897116c87c34, |
3086 | | 0x9580869f0e7aac0e, 0xd45d35e6ae3d4da0, |
3087 | | 0xbae0a846d2195712, 0x8974836059cca109, |
3088 | | 0xe998d258869facd7, 0x2bd1a438703fc94b, |
3089 | | 0x91ff83775423cc06, 0x7b6306a34627ddcf, |
3090 | | 0xb67f6455292cbf08, 0x1a3bc84c17b1d542, |
3091 | | 0xe41f3d6a7377eeca, 0x20caba5f1d9e4a93, |
3092 | | 0x8e938662882af53e, 0x547eb47b7282ee9c, |
3093 | | 0xb23867fb2a35b28d, 0xe99e619a4f23aa43, |
3094 | | 0xdec681f9f4c31f31, 0x6405fa00e2ec94d4, |
3095 | | 0x8b3c113c38f9f37e, 0xde83bc408dd3dd04, |
3096 | | 0xae0b158b4738705e, 0x9624ab50b148d445, |
3097 | | 0xd98ddaee19068c76, 0x3badd624dd9b0957, |
3098 | | 0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d6, |
3099 | | 0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4c, |
3100 | | 0xd47487cc8470652b, 0x7647c3200069671f, |
3101 | | 0x84c8d4dfd2c63f3b, 0x29ecd9f40041e073, |
3102 | | 0xa5fb0a17c777cf09, 0xf468107100525890, |
3103 | | 0xcf79cc9db955c2cc, 0x7182148d4066eeb4, |
3104 | | 0x81ac1fe293d599bf, 0xc6f14cd848405530, |
3105 | | 0xa21727db38cb002f, 0xb8ada00e5a506a7c, |
3106 | | 0xca9cf1d206fdc03b, 0xa6d90811f0e4851c, |
3107 | | 0xfd442e4688bd304a, 0x908f4a166d1da663, |
3108 | | 0x9e4a9cec15763e2e, 0x9a598e4e043287fe, |
3109 | | 0xc5dd44271ad3cdba, 0x40eff1e1853f29fd, |
3110 | | 0xf7549530e188c128, 0xd12bee59e68ef47c, |
3111 | | 0x9a94dd3e8cf578b9, 0x82bb74f8301958ce, |
3112 | | 0xc13a148e3032d6e7, 0xe36a52363c1faf01, |
3113 | | 0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac1, |
3114 | | 0x96f5600f15a7b7e5, 0x29ab103a5ef8c0b9, |
3115 | | 0xbcb2b812db11a5de, 0x7415d448f6b6f0e7, |
3116 | | 0xebdf661791d60f56, 0x111b495b3464ad21, |
3117 | | 0x936b9fcebb25c995, 0xcab10dd900beec34, |
3118 | | 0xb84687c269ef3bfb, 0x3d5d514f40eea742, |
3119 | | 0xe65829b3046b0afa, 0xcb4a5a3112a5112, |
3120 | | 0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ab, |
3121 | | 0xb3f4e093db73a093, 0x59ed216765690f56, |
3122 | | 0xe0f218b8d25088b8, 0x306869c13ec3532c, |
3123 | | 0x8c974f7383725573, 0x1e414218c73a13fb, |
3124 | | 0xafbd2350644eeacf, 0xe5d1929ef90898fa, |
3125 | | 0xdbac6c247d62a583, 0xdf45f746b74abf39, |
3126 | | 0x894bc396ce5da772, 0x6b8bba8c328eb783, |
3127 | | 0xab9eb47c81f5114f, 0x66ea92f3f326564, |
3128 | | 0xd686619ba27255a2, 0xc80a537b0efefebd, |
3129 | | 0x8613fd0145877585, 0xbd06742ce95f5f36, |
3130 | | 0xa798fc4196e952e7, 0x2c48113823b73704, |
3131 | | 0xd17f3b51fca3a7a0, 0xf75a15862ca504c5, |
3132 | | 0x82ef85133de648c4, 0x9a984d73dbe722fb, |
3133 | | 0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebba, |
3134 | | 0xcc963fee10b7d1b3, 0x318df905079926a8, |
3135 | | 0xffbbcfe994e5c61f, 0xfdf17746497f7052, |
3136 | | 0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa633, |
3137 | | 0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc0, |
3138 | | 0xf9bd690a1b68637b, 0x3dfdce7aa3c673b0, |
3139 | | 0x9c1661a651213e2d, 0x6bea10ca65c084e, |
3140 | | 0xc31bfa0fe5698db8, 0x486e494fcff30a62, |
3141 | | 0xf3e2f893dec3f126, 0x5a89dba3c3efccfa, |
3142 | | 0x986ddb5c6b3a76b7, 0xf89629465a75e01c, |
3143 | | 0xbe89523386091465, 0xf6bbb397f1135823, |
3144 | | 0xee2ba6c0678b597f, 0x746aa07ded582e2c, |
3145 | | 0x94db483840b717ef, 0xa8c2a44eb4571cdc, |
3146 | | 0xba121a4650e4ddeb, 0x92f34d62616ce413, |
3147 | | 0xe896a0d7e51e1566, 0x77b020baf9c81d17, |
3148 | | 0x915e2486ef32cd60, 0xace1474dc1d122e, |
3149 | | 0xb5b5ada8aaff80b8, 0xd819992132456ba, |
3150 | | 0xe3231912d5bf60e6, 0x10e1fff697ed6c69, |
3151 | | 0x8df5efabc5979c8f, 0xca8d3ffa1ef463c1, |
3152 | | 0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb2, |
3153 | | 0xddd0467c64bce4a0, 0xac7cb3f6d05ddbde, |
3154 | | 0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96b, |
3155 | | 0xad4ab7112eb3929d, 0x86c16c98d2c953c6, |
3156 | | 0xd89d64d57a607744, 0xe871c7bf077ba8b7, |
3157 | | 0x87625f056c7c4a8b, 0x11471cd764ad4972, |
3158 | | 0xa93af6c6c79b5d2d, 0xd598e40d3dd89bcf, |
3159 | | 0xd389b47879823479, 0x4aff1d108d4ec2c3, |
3160 | | 0x843610cb4bf160cb, 0xcedf722a585139ba, |
3161 | | 0xa54394fe1eedb8fe, 0xc2974eb4ee658828, |
3162 | | 0xce947a3da6a9273e, 0x733d226229feea32, |
3163 | | 0x811ccc668829b887, 0x806357d5a3f525f, |
3164 | | 0xa163ff802a3426a8, 0xca07c2dcb0cf26f7, |
3165 | | 0xc9bcff6034c13052, 0xfc89b393dd02f0b5, |
3166 | | 0xfc2c3f3841f17c67, 0xbbac2078d443ace2, |
3167 | | 0x9d9ba7832936edc0, 0xd54b944b84aa4c0d, |
3168 | | 0xc5029163f384a931, 0xa9e795e65d4df11, |
3169 | | 0xf64335bcf065d37d, 0x4d4617b5ff4a16d5, |
3170 | | 0x99ea0196163fa42e, 0x504bced1bf8e4e45, |
3171 | | 0xc06481fb9bcf8d39, 0xe45ec2862f71e1d6, |
3172 | | 0xf07da27a82c37088, 0x5d767327bb4e5a4c, |
3173 | | 0x964e858c91ba2655, 0x3a6a07f8d510f86f, |
3174 | | 0xbbe226efb628afea, 0x890489f70a55368b, |
3175 | | 0xeadab0aba3b2dbe5, 0x2b45ac74ccea842e, |
3176 | | 0x92c8ae6b464fc96f, 0x3b0b8bc90012929d, |
3177 | | 0xb77ada0617e3bbcb, 0x9ce6ebb40173744, |
3178 | | 0xe55990879ddcaabd, 0xcc420a6a101d0515, |
3179 | | 0x8f57fa54c2a9eab6, 0x9fa946824a12232d, |
3180 | | 0xb32df8e9f3546564, 0x47939822dc96abf9, |
3181 | | 0xdff9772470297ebd, 0x59787e2b93bc56f7, |
3182 | | 0x8bfbea76c619ef36, 0x57eb4edb3c55b65a, |
3183 | | 0xaefae51477a06b03, 0xede622920b6b23f1, |
3184 | | 0xdab99e59958885c4, 0xe95fab368e45eced, |
3185 | | 0x88b402f7fd75539b, 0x11dbcb0218ebb414, |
3186 | | 0xaae103b5fcd2a881, 0xd652bdc29f26a119, |
3187 | | 0xd59944a37c0752a2, 0x4be76d3346f0495f, |
3188 | | 0x857fcae62d8493a5, 0x6f70a4400c562ddb, |
3189 | | 0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb952, |
3190 | | 0xd097ad07a71f26b2, 0x7e2000a41346a7a7, |
3191 | | 0x825ecc24c873782f, 0x8ed400668c0c28c8, |
3192 | | 0xa2f67f2dfa90563b, 0x728900802f0f32fa, |
3193 | | 0xcbb41ef979346bca, 0x4f2b40a03ad2ffb9, |
3194 | | 0xfea126b7d78186bc, 0xe2f610c84987bfa8, |
3195 | | 0x9f24b832e6b0f436, 0xdd9ca7d2df4d7c9, |
3196 | | 0xc6ede63fa05d3143, 0x91503d1c79720dbb, |
3197 | | 0xf8a95fcf88747d94, 0x75a44c6397ce912a, |
3198 | | 0x9b69dbe1b548ce7c, 0xc986afbe3ee11aba, |
3199 | | 0xc24452da229b021b, 0xfbe85badce996168, |
3200 | | 0xf2d56790ab41c2a2, 0xfae27299423fb9c3, |
3201 | | 0x97c560ba6b0919a5, 0xdccd879fc967d41a, |
3202 | | 0xbdb6b8e905cb600f, 0x5400e987bbc1c920, |
3203 | | 0xed246723473e3813, 0x290123e9aab23b68, |
3204 | | 0x9436c0760c86e30b, 0xf9a0b6720aaf6521, |
3205 | | 0xb94470938fa89bce, 0xf808e40e8d5b3e69, |
3206 | | 0xe7958cb87392c2c2, 0xb60b1d1230b20e04, |
3207 | | 0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c2, |
3208 | | 0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af3, |
3209 | | 0xe2280b6c20dd5232, 0x25c6da63c38de1b0, |
3210 | | 0x8d590723948a535f, 0x579c487e5a38ad0e, |
3211 | | 0xb0af48ec79ace837, 0x2d835a9df0c6d851, |
3212 | | 0xdcdb1b2798182244, 0xf8e431456cf88e65, |
3213 | | 0x8a08f0f8bf0f156b, 0x1b8e9ecb641b58ff, |
3214 | | 0xac8b2d36eed2dac5, 0xe272467e3d222f3f, |
3215 | | 0xd7adf884aa879177, 0x5b0ed81dcc6abb0f, |
3216 | | 0x86ccbb52ea94baea, 0x98e947129fc2b4e9, |
3217 | | 0xa87fea27a539e9a5, 0x3f2398d747b36224, |
3218 | | 0xd29fe4b18e88640e, 0x8eec7f0d19a03aad, |
3219 | | 0x83a3eeeef9153e89, 0x1953cf68300424ac, |
3220 | | 0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd7, |
3221 | | 0xcdb02555653131b6, 0x3792f412cb06794d, |
3222 | | 0x808e17555f3ebf11, 0xe2bbd88bbee40bd0, |
3223 | | 0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec4, |
3224 | | 0xc8de047564d20a8b, 0xf245825a5a445275, |
3225 | | 0xfb158592be068d2e, 0xeed6e2f0f0d56712, |
3226 | | 0x9ced737bb6c4183d, 0x55464dd69685606b, |
3227 | | 0xc428d05aa4751e4c, 0xaa97e14c3c26b886, |
3228 | | 0xf53304714d9265df, 0xd53dd99f4b3066a8, |
3229 | | 0x993fe2c6d07b7fab, 0xe546a8038efe4029, |
3230 | | 0xbf8fdb78849a5f96, 0xde98520472bdd033, |
3231 | | 0xef73d256a5c0f77c, 0x963e66858f6d4440, |
3232 | | 0x95a8637627989aad, 0xdde7001379a44aa8, |
3233 | | 0xbb127c53b17ec159, 0x5560c018580d5d52, |
3234 | | 0xe9d71b689dde71af, 0xaab8f01e6e10b4a6, |
3235 | | 0x9226712162ab070d, 0xcab3961304ca70e8, |
3236 | | 0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d22, |
3237 | | 0xe45c10c42a2b3b05, 0x8cb89a7db77c506a, |
3238 | | 0x8eb98a7a9a5b04e3, 0x77f3608e92adb242, |
3239 | | 0xb267ed1940f1c61c, 0x55f038b237591ed3, |
3240 | | 0xdf01e85f912e37a3, 0x6b6c46dec52f6688, |
3241 | | 0x8b61313bbabce2c6, 0x2323ac4b3b3da015, |
3242 | | 0xae397d8aa96c1b77, 0xabec975e0a0d081a, |
3243 | | 0xd9c7dced53c72255, 0x96e7bd358c904a21, |
3244 | | 0x881cea14545c7575, 0x7e50d64177da2e54, |
3245 | | 0xaa242499697392d2, 0xdde50bd1d5d0b9e9, |
3246 | | 0xd4ad2dbfc3d07787, 0x955e4ec64b44e864, |
3247 | | 0x84ec3c97da624ab4, 0xbd5af13bef0b113e, |
3248 | | 0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58e, |
3249 | | 0xcfb11ead453994ba, 0x67de18eda5814af2, |
3250 | | 0x81ceb32c4b43fcf4, 0x80eacf948770ced7, |
3251 | | 0xa2425ff75e14fc31, 0xa1258379a94d028d, |
3252 | | 0xcad2f7f5359a3b3e, 0x96ee45813a04330, |
3253 | | 0xfd87b5f28300ca0d, 0x8bca9d6e188853fc, |
3254 | | 0x9e74d1b791e07e48, 0x775ea264cf55347e, |
3255 | | 0xc612062576589dda, 0x95364afe032a819e, |
3256 | | 0xf79687aed3eec551, 0x3a83ddbd83f52205, |
3257 | | 0x9abe14cd44753b52, 0xc4926a9672793543, |
3258 | | 0xc16d9a0095928a27, 0x75b7053c0f178294, |
3259 | | 0xf1c90080baf72cb1, 0x5324c68b12dd6339, |
3260 | | 0x971da05074da7bee, 0xd3f6fc16ebca5e04, |
3261 | | 0xbce5086492111aea, 0x88f4bb1ca6bcf585, |
3262 | | 0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6, |
3263 | | 0x9392ee8e921d5d07, 0x3aff322e62439fd0, |
3264 | | 0xb877aa3236a4b449, 0x9befeb9fad487c3, |
3265 | | 0xe69594bec44de15b, 0x4c2ebe687989a9b4, |
3266 | | 0x901d7cf73ab0acd9, 0xf9d37014bf60a11, |
3267 | | 0xb424dc35095cd80f, 0x538484c19ef38c95, |
3268 | | 0xe12e13424bb40e13, 0x2865a5f206b06fba, |
3269 | | 0x8cbccc096f5088cb, 0xf93f87b7442e45d4, |
3270 | | 0xafebff0bcb24aafe, 0xf78f69a51539d749, |
3271 | | 0xdbe6fecebdedd5be, 0xb573440e5a884d1c, |
3272 | | 0x89705f4136b4a597, 0x31680a88f8953031, |
3273 | | 0xabcc77118461cefc, 0xfdc20d2b36ba7c3e, |
3274 | | 0xd6bf94d5e57a42bc, 0x3d32907604691b4d, |
3275 | | 0x8637bd05af6c69b5, 0xa63f9a49c2c1b110, |
3276 | | 0xa7c5ac471b478423, 0xfcf80dc33721d54, |
3277 | | 0xd1b71758e219652b, 0xd3c36113404ea4a9, |
3278 | | 0x83126e978d4fdf3b, 0x645a1cac083126ea, |
3279 | | 0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4, |
3280 | | 0xcccccccccccccccc, 0xcccccccccccccccd, |
3281 | | 0x8000000000000000, 0x0, |
3282 | | 0xa000000000000000, 0x0, |
3283 | | 0xc800000000000000, 0x0, |
3284 | | 0xfa00000000000000, 0x0, |
3285 | | 0x9c40000000000000, 0x0, |
3286 | | 0xc350000000000000, 0x0, |
3287 | | 0xf424000000000000, 0x0, |
3288 | | 0x9896800000000000, 0x0, |
3289 | | 0xbebc200000000000, 0x0, |
3290 | | 0xee6b280000000000, 0x0, |
3291 | | 0x9502f90000000000, 0x0, |
3292 | | 0xba43b74000000000, 0x0, |
3293 | | 0xe8d4a51000000000, 0x0, |
3294 | | 0x9184e72a00000000, 0x0, |
3295 | | 0xb5e620f480000000, 0x0, |
3296 | | 0xe35fa931a0000000, 0x0, |
3297 | | 0x8e1bc9bf04000000, 0x0, |
3298 | | 0xb1a2bc2ec5000000, 0x0, |
3299 | | 0xde0b6b3a76400000, 0x0, |
3300 | | 0x8ac7230489e80000, 0x0, |
3301 | | 0xad78ebc5ac620000, 0x0, |
3302 | | 0xd8d726b7177a8000, 0x0, |
3303 | | 0x878678326eac9000, 0x0, |
3304 | | 0xa968163f0a57b400, 0x0, |
3305 | | 0xd3c21bcecceda100, 0x0, |
3306 | | 0x84595161401484a0, 0x0, |
3307 | | 0xa56fa5b99019a5c8, 0x0, |
3308 | | 0xcecb8f27f4200f3a, 0x0, |
3309 | | 0x813f3978f8940984, 0x4000000000000000, |
3310 | | 0xa18f07d736b90be5, 0x5000000000000000, |
3311 | | 0xc9f2c9cd04674ede, 0xa400000000000000, |
3312 | | 0xfc6f7c4045812296, 0x4d00000000000000, |
3313 | | 0x9dc5ada82b70b59d, 0xf020000000000000, |
3314 | | 0xc5371912364ce305, 0x6c28000000000000, |
3315 | | 0xf684df56c3e01bc6, 0xc732000000000000, |
3316 | | 0x9a130b963a6c115c, 0x3c7f400000000000, |
3317 | | 0xc097ce7bc90715b3, 0x4b9f100000000000, |
3318 | | 0xf0bdc21abb48db20, 0x1e86d40000000000, |
3319 | | 0x96769950b50d88f4, 0x1314448000000000, |
3320 | | 0xbc143fa4e250eb31, 0x17d955a000000000, |
3321 | | 0xeb194f8e1ae525fd, 0x5dcfab0800000000, |
3322 | | 0x92efd1b8d0cf37be, 0x5aa1cae500000000, |
3323 | | 0xb7abc627050305ad, 0xf14a3d9e40000000, |
3324 | | 0xe596b7b0c643c719, 0x6d9ccd05d0000000, |
3325 | | 0x8f7e32ce7bea5c6f, 0xe4820023a2000000, |
3326 | | 0xb35dbf821ae4f38b, 0xdda2802c8a800000, |
3327 | | 0xe0352f62a19e306e, 0xd50b2037ad200000, |
3328 | | 0x8c213d9da502de45, 0x4526f422cc340000, |
3329 | | 0xaf298d050e4395d6, 0x9670b12b7f410000, |
3330 | | 0xdaf3f04651d47b4c, 0x3c0cdd765f114000, |
3331 | | 0x88d8762bf324cd0f, 0xa5880a69fb6ac800, |
3332 | | 0xab0e93b6efee0053, 0x8eea0d047a457a00, |
3333 | | 0xd5d238a4abe98068, 0x72a4904598d6d880, |
3334 | | 0x85a36366eb71f041, 0x47a6da2b7f864750, |
3335 | | 0xa70c3c40a64e6c51, 0x999090b65f67d924, |
3336 | | 0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d, |
3337 | | 0x82818f1281ed449f, 0xbff8f10e7a8921a4, |
3338 | | 0xa321f2d7226895c7, 0xaff72d52192b6a0d, |
3339 | | 0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490, |
3340 | | 0xfee50b7025c36a08, 0x2f236d04753d5b4, |
3341 | | 0x9f4f2726179a2245, 0x1d762422c946590, |
3342 | | 0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5, |
3343 | | 0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2, |
3344 | | 0x9b934c3b330c8577, 0x63cc55f49f88eb2f, |
3345 | | 0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb, |
3346 | | 0xf316271c7fc3908a, 0x8bef464e3945ef7a, |
3347 | | 0x97edd871cfda3a56, 0x97758bf0e3cbb5ac, |
3348 | | 0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317, |
3349 | | 0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd, |
3350 | | 0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a, |
3351 | | 0xb975d6b6ee39e436, 0xb3e2fd538e122b44, |
3352 | | 0xe7d34c64a9c85d44, 0x60dbbca87196b616, |
3353 | | 0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd, |
3354 | | 0xb51d13aea4a488dd, 0x6babab6398bdbe41, |
3355 | | 0xe264589a4dcdab14, 0xc696963c7eed2dd1, |
3356 | | 0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2, |
3357 | | 0xb0de65388cc8ada8, 0x3b25a55f43294bcb, |
3358 | | 0xdd15fe86affad912, 0x49ef0eb713f39ebe, |
3359 | | 0x8a2dbf142dfcc7ab, 0x6e3569326c784337, |
3360 | | 0xacb92ed9397bf996, 0x49c2c37f07965404, |
3361 | | 0xd7e77a8f87daf7fb, 0xdc33745ec97be906, |
3362 | | 0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3, |
3363 | | 0xa8acd7c0222311bc, 0xc40832ea0d68ce0c, |
3364 | | 0xd2d80db02aabd62b, 0xf50a3fa490c30190, |
3365 | | 0x83c7088e1aab65db, 0x792667c6da79e0fa, |
3366 | | 0xa4b8cab1a1563f52, 0x577001b891185938, |
3367 | | 0xcde6fd5e09abcf26, 0xed4c0226b55e6f86, |
3368 | | 0x80b05e5ac60b6178, 0x544f8158315b05b4, |
3369 | | 0xa0dc75f1778e39d6, 0x696361ae3db1c721, |
3370 | | 0xc913936dd571c84c, 0x3bc3a19cd1e38e9, |
3371 | | 0xfb5878494ace3a5f, 0x4ab48a04065c723, |
3372 | | 0x9d174b2dcec0e47b, 0x62eb0d64283f9c76, |
3373 | | 0xc45d1df942711d9a, 0x3ba5d0bd324f8394, |
3374 | | 0xf5746577930d6500, 0xca8f44ec7ee36479, |
3375 | | 0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb, |
3376 | | 0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e, |
3377 | | 0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e, |
3378 | | 0x95d04aee3b80ece5, 0xbba1f1d158724a12, |
3379 | | 0xbb445da9ca61281f, 0x2a8a6e45ae8edc97, |
3380 | | 0xea1575143cf97226, 0xf52d09d71a3293bd, |
3381 | | 0x924d692ca61be758, 0x593c2626705f9c56, |
3382 | | 0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c, |
3383 | | 0xe498f455c38b997a, 0xb6dfb9c0f956447, |
3384 | | 0x8edf98b59a373fec, 0x4724bd4189bd5eac, |
3385 | | 0xb2977ee300c50fe7, 0x58edec91ec2cb657, |
3386 | | 0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed, |
3387 | | 0x8b865b215899f46c, 0xbd79e0d20082ee74, |
3388 | | 0xae67f1e9aec07187, 0xecd8590680a3aa11, |
3389 | | 0xda01ee641a708de9, 0xe80e6f4820cc9495, |
3390 | | 0x884134fe908658b2, 0x3109058d147fdcdd, |
3391 | | 0xaa51823e34a7eede, 0xbd4b46f0599fd415, |
3392 | | 0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a, |
3393 | | 0x850fadc09923329e, 0x3e2cf6bc604ddb0, |
3394 | | 0xa6539930bf6bff45, 0x84db8346b786151c, |
3395 | | 0xcfe87f7cef46ff16, 0xe612641865679a63, |
3396 | | 0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e, |
3397 | | 0xa26da3999aef7749, 0xe3be5e330f38f09d, |
3398 | | 0xcb090c8001ab551c, 0x5cadf5bfd3072cc5, |
3399 | | 0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6, |
3400 | | 0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa, |
3401 | | 0xc646d63501a1511d, 0xb281e1fd541501b8, |
3402 | | 0xf7d88bc24209a565, 0x1f225a7ca91a4226, |
3403 | | 0x9ae757596946075f, 0x3375788de9b06958, |
3404 | | 0xc1a12d2fc3978937, 0x52d6b1641c83ae, |
3405 | | 0xf209787bb47d6b84, 0xc0678c5dbd23a49a, |
3406 | | 0x9745eb4d50ce6332, 0xf840b7ba963646e0, |
3407 | | 0xbd176620a501fbff, 0xb650e5a93bc3d898, |
3408 | | 0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe, |
3409 | | 0x93ba47c980e98cdf, 0xc66f336c36b10137, |
3410 | | 0xb8a8d9bbe123f017, 0xb80b0047445d4184, |
3411 | | 0xe6d3102ad96cec1d, 0xa60dc059157491e5, |
3412 | | 0x9043ea1ac7e41392, 0x87c89837ad68db2f, |
3413 | | 0xb454e4a179dd1877, 0x29babe4598c311fb, |
3414 | | 0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a, |
3415 | | 0x8ce2529e2734bb1d, 0x1899e4a65f58660c, |
3416 | | 0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f, |
3417 | | 0xdc21a1171d42645d, 0x76707543f4fa1f73, |
3418 | | 0x899504ae72497eba, 0x6a06494a791c53a8, |
3419 | | 0xabfa45da0edbde69, 0x487db9d17636892, |
3420 | | 0xd6f8d7509292d603, 0x45a9d2845d3c42b6, |
3421 | | 0x865b86925b9bc5c2, 0xb8a2392ba45a9b2, |
3422 | | 0xa7f26836f282b732, 0x8e6cac7768d7141e, |
3423 | | 0xd1ef0244af2364ff, 0x3207d795430cd926, |
3424 | | 0x8335616aed761f1f, 0x7f44e6bd49e807b8, |
3425 | | 0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6, |
3426 | | 0xcd036837130890a1, 0x36dba887c37a8c0f, |
3427 | | 0x802221226be55a64, 0xc2494954da2c9789, |
3428 | | 0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c, |
3429 | | 0xc83553c5c8965d3d, 0x6f92829494e5acc7, |
3430 | | 0xfa42a8b73abbf48c, 0xcb772339ba1f17f9, |
3431 | | 0x9c69a97284b578d7, 0xff2a760414536efb, |
3432 | | 0xc38413cf25e2d70d, 0xfef5138519684aba, |
3433 | | 0xf46518c2ef5b8cd1, 0x7eb258665fc25d69, |
3434 | | 0x98bf2f79d5993802, 0xef2f773ffbd97a61, |
3435 | | 0xbeeefb584aff8603, 0xaafb550ffacfd8fa, |
3436 | | 0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38, |
3437 | | 0x952ab45cfa97a0b2, 0xdd945a747bf26183, |
3438 | | 0xba756174393d88df, 0x94f971119aeef9e4, |
3439 | | 0xe912b9d1478ceb17, 0x7a37cd5601aab85d, |
3440 | | 0x91abb422ccb812ee, 0xac62e055c10ab33a, |
3441 | | 0xb616a12b7fe617aa, 0x577b986b314d6009, |
3442 | | 0xe39c49765fdf9d94, 0xed5a7e85fda0b80b, |
3443 | | 0x8e41ade9fbebc27d, 0x14588f13be847307, |
3444 | | 0xb1d219647ae6b31c, 0x596eb2d8ae258fc8, |
3445 | | 0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb, |
3446 | | 0x8aec23d680043bee, 0x25de7bb9480d5854, |
3447 | | 0xada72ccc20054ae9, 0xaf561aa79a10ae6a, |
3448 | | 0xd910f7ff28069da4, 0x1b2ba1518094da04, |
3449 | | 0x87aa9aff79042286, 0x90fb44d2f05d0842, |
3450 | | 0xa99541bf57452b28, 0x353a1607ac744a53, |
3451 | | 0xd3fa922f2d1675f2, 0x42889b8997915ce8, |
3452 | | 0x847c9b5d7c2e09b7, 0x69956135febada11, |
3453 | | 0xa59bc234db398c25, 0x43fab9837e699095, |
3454 | | 0xcf02b2c21207ef2e, 0x94f967e45e03f4bb, |
3455 | | 0x8161afb94b44f57d, 0x1d1be0eebac278f5, |
3456 | | 0xa1ba1ba79e1632dc, 0x6462d92a69731732, |
3457 | | 0xca28a291859bbf93, 0x7d7b8f7503cfdcfe, |
3458 | | 0xfcb2cb35e702af78, 0x5cda735244c3d43e, |
3459 | | 0x9defbf01b061adab, 0x3a0888136afa64a7, |
3460 | | 0xc56baec21c7a1916, 0x88aaa1845b8fdd0, |
3461 | | 0xf6c69a72a3989f5b, 0x8aad549e57273d45, |
3462 | | 0x9a3c2087a63f6399, 0x36ac54e2f678864b, |
3463 | | 0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd, |
3464 | | 0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5, |
3465 | | 0x969eb7c47859e743, 0x9f644ae5a4b1b325, |
3466 | | 0xbc4665b596706114, 0x873d5d9f0dde1fee, |
3467 | | 0xeb57ff22fc0c7959, 0xa90cb506d155a7ea, |
3468 | | 0x9316ff75dd87cbd8, 0x9a7f12442d588f2, |
3469 | | 0xb7dcbf5354e9bece, 0xc11ed6d538aeb2f, |
3470 | | 0xe5d3ef282a242e81, 0x8f1668c8a86da5fa, |
3471 | | 0x8fa475791a569d10, 0xf96e017d694487bc, |
3472 | | 0xb38d92d760ec4455, 0x37c981dcc395a9ac, |
3473 | | 0xe070f78d3927556a, 0x85bbe253f47b1417, |
3474 | | 0x8c469ab843b89562, 0x93956d7478ccec8e, |
3475 | | 0xaf58416654a6babb, 0x387ac8d1970027b2, |
3476 | | 0xdb2e51bfe9d0696a, 0x6997b05fcc0319e, |
3477 | | 0x88fcf317f22241e2, 0x441fece3bdf81f03, |
3478 | | 0xab3c2fddeeaad25a, 0xd527e81cad7626c3, |
3479 | | 0xd60b3bd56a5586f1, 0x8a71e223d8d3b074, |
3480 | | 0x85c7056562757456, 0xf6872d5667844e49, |
3481 | | 0xa738c6bebb12d16c, 0xb428f8ac016561db, |
3482 | | 0xd106f86e69d785c7, 0xe13336d701beba52, |
3483 | | 0x82a45b450226b39c, 0xecc0024661173473, |
3484 | | 0xa34d721642b06084, 0x27f002d7f95d0190, |
3485 | | 0xcc20ce9bd35c78a5, 0x31ec038df7b441f4, |
3486 | | 0xff290242c83396ce, 0x7e67047175a15271, |
3487 | | 0x9f79a169bd203e41, 0xf0062c6e984d386, |
3488 | | 0xc75809c42c684dd1, 0x52c07b78a3e60868, |
3489 | | 0xf92e0c3537826145, 0xa7709a56ccdf8a82, |
3490 | | 0x9bbcc7a142b17ccb, 0x88a66076400bb691, |
3491 | | 0xc2abf989935ddbfe, 0x6acff893d00ea435, |
3492 | | 0xf356f7ebf83552fe, 0x583f6b8c4124d43, |
3493 | | 0x98165af37b2153de, 0xc3727a337a8b704a, |
3494 | | 0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c, |
3495 | | 0xeda2ee1c7064130c, 0x1162def06f79df73, |
3496 | | 0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8, |
3497 | | 0xb9a74a0637ce2ee1, 0x6d953e2bd7173692, |
3498 | | 0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437, |
3499 | | 0x910ab1d4db9914a0, 0x1d9c9892400a22a2, |
3500 | | 0xb54d5e4a127f59c8, 0x2503beb6d00cab4b, |
3501 | | 0xe2a0b5dc971f303a, 0x2e44ae64840fd61d, |
3502 | | 0x8da471a9de737e24, 0x5ceaecfed289e5d2, |
3503 | | 0xb10d8e1456105dad, 0x7425a83e872c5f47, |
3504 | | 0xdd50f1996b947518, 0xd12f124e28f77719, |
3505 | | 0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f, |
3506 | | 0xace73cbfdc0bfb7b, 0x636cc64d1001550b, |
3507 | | 0xd8210befd30efa5a, 0x3c47f7e05401aa4e, |
3508 | | 0x8714a775e3e95c78, 0x65acfaec34810a71, |
3509 | | 0xa8d9d1535ce3b396, 0x7f1839a741a14d0d, |
3510 | | 0xd31045a8341ca07c, 0x1ede48111209a050, |
3511 | | 0x83ea2b892091e44d, 0x934aed0aab460432, |
3512 | | 0xa4e4b66b68b65d60, 0xf81da84d5617853f, |
3513 | | 0xce1de40642e3f4b9, 0x36251260ab9d668e, |
3514 | | 0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019, |
3515 | | 0xa1075a24e4421730, 0xb24cf65b8612f81f, |
3516 | | 0xc94930ae1d529cfc, 0xdee033f26797b627, |
3517 | | 0xfb9b7cd9a4a7443c, 0x169840ef017da3b1, |
3518 | | 0x9d412e0806e88aa5, 0x8e1f289560ee864e, |
3519 | | 0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2, |
3520 | | 0xf5b5d7ec8acb58a2, 0xae10af696774b1db, |
3521 | | 0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29, |
3522 | | 0xbff610b0cc6edd3f, 0x17fd090a58d32af3, |
3523 | | 0xeff394dcff8a948e, 0xddfc4b4cef07f5b0, |
3524 | | 0x95f83d0a1fb69cd9, 0x4abdaf101564f98e, |
3525 | | 0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1, |
3526 | | 0xea53df5fd18d5513, 0x84c86189216dc5ed, |
3527 | | 0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4, |
3528 | | 0xb7118682dbb66a77, 0x3fbc8c33221dc2a1, |
3529 | | 0xe4d5e82392a40515, 0xfabaf3feaa5334a, |
3530 | | 0x8f05b1163ba6832d, 0x29cb4d87f2a7400e, |
3531 | | 0xb2c71d5bca9023f8, 0x743e20e9ef511012, |
3532 | | 0xdf78e4b2bd342cf6, 0x914da9246b255416, |
3533 | | 0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e, |
3534 | | 0xae9672aba3d0c320, 0xa184ac2473b529b1, |
3535 | | 0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e, |
3536 | | 0x8865899617fb1871, 0x7e2fa67c7a658892, |
3537 | | 0xaa7eebfb9df9de8d, 0xddbb901b98feeab7, |
3538 | | 0xd51ea6fa85785631, 0x552a74227f3ea565, |
3539 | | 0x8533285c936b35de, 0xd53a88958f87275f, |
3540 | | 0xa67ff273b8460356, 0x8a892abaf368f137, |
3541 | | 0xd01fef10a657842c, 0x2d2b7569b0432d85, |
3542 | | 0x8213f56a67f6b29b, 0x9c3b29620e29fc73, |
3543 | | 0xa298f2c501f45f42, 0x8349f3ba91b47b8f, |
3544 | | 0xcb3f2f7642717713, 0x241c70a936219a73, |
3545 | | 0xfe0efb53d30dd4d7, 0xed238cd383aa0110, |
3546 | | 0x9ec95d1463e8a506, 0xf4363804324a40aa, |
3547 | | 0xc67bb4597ce2ce48, 0xb143c6053edcd0d5, |
3548 | | 0xf81aa16fdc1b81da, 0xdd94b7868e94050a, |
3549 | | 0x9b10a4e5e9913128, 0xca7cf2b4191c8326, |
3550 | | 0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0, |
3551 | | 0xf24a01a73cf2dccf, 0xbc633b39673c8cec, |
3552 | | 0x976e41088617ca01, 0xd5be0503e085d813, |
3553 | | 0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18, |
3554 | | 0xec9c459d51852ba2, 0xddf8e7d60ed1219e, |
3555 | | 0x93e1ab8252f33b45, 0xcabb90e5c942b503, |
3556 | | 0xb8da1662e7b00a17, 0x3d6a751f3b936243, |
3557 | | 0xe7109bfba19c0c9d, 0xcc512670a783ad4, |
3558 | | 0x906a617d450187e2, 0x27fb2b80668b24c5, |
3559 | | 0xb484f9dc9641e9da, 0xb1f9f660802dedf6, |
3560 | | 0xe1a63853bbd26451, 0x5e7873f8a0396973, |
3561 | | 0x8d07e33455637eb2, 0xdb0b487b6423e1e8, |
3562 | | 0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62, |
3563 | | 0xdc5c5301c56b75f7, 0x7641a140cc7810fb, |
3564 | | 0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d, |
3565 | | 0xac2820d9623bf429, 0x546345fa9fbdcd44, |
3566 | | 0xd732290fbacaf133, 0xa97c177947ad4095, |
3567 | | 0x867f59a9d4bed6c0, 0x49ed8eabcccc485d, |
3568 | | 0xa81f301449ee8c70, 0x5c68f256bfff5a74, |
3569 | | 0xd226fc195c6a2f8c, 0x73832eec6fff3111, |
3570 | | 0x83585d8fd9c25db7, 0xc831fd53c5ff7eab, |
3571 | | 0xa42e74f3d032f525, 0xba3e7ca8b77f5e55, |
3572 | | 0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb, |
3573 | | 0x80444b5e7aa7cf85, 0x7980d163cf5b81b3, |
3574 | | 0xa0555e361951c366, 0xd7e105bcc332621f, |
3575 | | 0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7, |
3576 | | 0xfa856334878fc150, 0xb14f98f6f0feb951, |
3577 | | 0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3, |
3578 | | 0xc3b8358109e84f07, 0xa862f80ec4700c8, |
3579 | | 0xf4a642e14c6262c8, 0xcd27bb612758c0fa, |
3580 | | 0x98e7e9cccfbd7dbd, 0x8038d51cb897789c, |
3581 | | 0xbf21e44003acdd2c, 0xe0470a63e6bd56c3, |
3582 | | 0xeeea5d5004981478, 0x1858ccfce06cac74, |
3583 | | 0x95527a5202df0ccb, 0xf37801e0c43ebc8, |
3584 | | 0xbaa718e68396cffd, 0xd30560258f54e6ba, |
3585 | | 0xe950df20247c83fd, 0x47c6b82ef32a2069, |
3586 | | 0x91d28b7416cdd27e, 0x4cdc331d57fa5441, |
3587 | | 0xb6472e511c81471d, 0xe0133fe4adf8e952, |
3588 | | 0xe3d8f9e563a198e5, 0x58180fddd97723a6, |
3589 | | 0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648, |
3590 | | }; |
3591 | | }; |
3592 | | |
3593 | | #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE |
3594 | | |
3595 | | template <class unused> |
3596 | | constexpr uint64_t |
3597 | | powers_template<unused>::power_of_five_128[number_of_entries]; |
3598 | | |
3599 | | #endif |
3600 | | |
3601 | | using powers = powers_template<>; |
3602 | | |
3603 | | } // namespace fast_float |
3604 | | |
3605 | | #endif |
3606 | | |
3607 | | #ifndef FASTFLOAT_DECIMAL_TO_BINARY_H |
3608 | | #define FASTFLOAT_DECIMAL_TO_BINARY_H |
3609 | | |
3610 | | #include <cmath> |
3611 | | #include <cstdlib> |
3612 | | #include <cstring> |
3613 | | |
3614 | | namespace fast_float { |
3615 | | |
3616 | | // This will compute or rather approximate w * 5**q and return a pair of 64-bit |
3617 | | // words approximating the result, with the "high" part corresponding to the |
3618 | | // most significant bits and the low part corresponding to the least significant |
3619 | | // bits. |
3620 | | // |
3621 | | template <int bit_precision> |
3622 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128 |
3623 | 25 | compute_product_approximation(int64_t q, uint64_t w) { |
3624 | 25 | int const index = 2 * int(q - powers::smallest_power_of_five); |
3625 | | // For small values of q, e.g., q in [0,27], the answer is always exact |
3626 | | // because The line value128 firstproduct = full_multiplication(w, |
3627 | | // power_of_five_128[index]); gives the exact answer. |
3628 | 25 | value128 firstproduct = |
3629 | 25 | full_multiplication(w, powers::power_of_five_128[index]); |
3630 | 25 | static_assert((bit_precision >= 0) && (bit_precision <= 64), |
3631 | 25 | " precision should be in (0,64]"); |
3632 | 25 | constexpr uint64_t precision_mask = |
3633 | 25 | (bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision) |
3634 | 25 | : uint64_t(0xFFFFFFFFFFFFFFFF); |
3635 | 25 | if ((firstproduct.high & precision_mask) == |
3636 | 25 | precision_mask) { // could further guard with (lower + w < lower) |
3637 | | // regarding the second product, we only need secondproduct.high, but our |
3638 | | // expectation is that the compiler will optimize this extra work away if |
3639 | | // needed. |
3640 | 1 | value128 secondproduct = |
3641 | 1 | full_multiplication(w, powers::power_of_five_128[index + 1]); |
3642 | 1 | firstproduct.low += secondproduct.high; |
3643 | 1 | if (secondproduct.high > firstproduct.low) { |
3644 | 1 | firstproduct.high++; |
3645 | 1 | } |
3646 | 1 | } |
3647 | 25 | return firstproduct; |
3648 | 25 | } |
3649 | | |
3650 | | namespace detail { |
3651 | | /** |
3652 | | * For q in (0,350), we have that |
3653 | | * f = (((152170 + 65536) * q ) >> 16); |
3654 | | * is equal to |
3655 | | * floor(p) + q |
3656 | | * where |
3657 | | * p = log(5**q)/log(2) = q * log(5)/log(2) |
3658 | | * |
3659 | | * For negative values of q in (-400,0), we have that |
3660 | | * f = (((152170 + 65536) * q ) >> 16); |
3661 | | * is equal to |
3662 | | * -ceil(p) + q |
3663 | | * where |
3664 | | * p = log(5**-q)/log(2) = -q * log(5)/log(2) |
3665 | | */ |
3666 | 25 | constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept { |
3667 | 25 | return (((152170 + 65536) * q) >> 16) + 63; |
3668 | 25 | } |
3669 | | } // namespace detail |
3670 | | |
3671 | | // create an adjusted mantissa, biased by the invalid power2 |
3672 | | // for significant digits already multiplied by 10 ** q. |
3673 | | template <typename binary> |
3674 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa |
3675 | 0 | compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept { |
3676 | 0 | int hilz = int(w >> 63) ^ 1; |
3677 | 0 | adjusted_mantissa answer; |
3678 | 0 | answer.mantissa = w << hilz; |
3679 | 0 | int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent(); |
3680 | 0 | answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + |
3681 | 0 | invalid_am_bias); |
3682 | 0 | return answer; |
3683 | 0 | } |
3684 | | |
3685 | | // w * 10 ** q, without rounding the representation up. |
3686 | | // the power2 in the exponent will be adjusted by invalid_am_bias. |
3687 | | template <typename binary> |
3688 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa |
3689 | 0 | compute_error(int64_t q, uint64_t w) noexcept { |
3690 | 0 | int lz = leading_zeroes(w); |
3691 | 0 | w <<= lz; |
3692 | 0 | value128 product = |
3693 | 0 | compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w); |
3694 | 0 | return compute_error_scaled<binary>(q, product.high, lz); |
3695 | 0 | } |
3696 | | |
3697 | | // Computers w * 10 ** q. |
3698 | | // The returned value should be a valid number that simply needs to be |
3699 | | // packed. However, in some very rare cases, the computation will fail. In such |
3700 | | // cases, we return an adjusted_mantissa with a negative power of 2: the caller |
3701 | | // should recompute in such cases. |
3702 | | template <typename binary> |
3703 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa |
3704 | 60 | compute_float(int64_t q, uint64_t w) noexcept { |
3705 | 60 | adjusted_mantissa answer; |
3706 | 60 | if ((w == 0) || (q < binary::smallest_power_of_ten())) { |
3707 | 24 | answer.power2 = 0; |
3708 | 24 | answer.mantissa = 0; |
3709 | | // result should be zero |
3710 | 24 | return answer; |
3711 | 24 | } |
3712 | 36 | if (q > binary::largest_power_of_ten()) { |
3713 | | // we want to get infinity: |
3714 | 11 | answer.power2 = binary::infinite_power(); |
3715 | 11 | answer.mantissa = 0; |
3716 | 11 | return answer; |
3717 | 11 | } |
3718 | | // At this point in time q is in [powers::smallest_power_of_five, |
3719 | | // powers::largest_power_of_five]. |
3720 | | |
3721 | | // We want the most significant bit of i to be 1. Shift if needed. |
3722 | 25 | int lz = leading_zeroes(w); |
3723 | 25 | w <<= lz; |
3724 | | |
3725 | | // The required precision is binary::mantissa_explicit_bits() + 3 because |
3726 | | // 1. We need the implicit bit |
3727 | | // 2. We need an extra bit for rounding purposes |
3728 | | // 3. We might lose a bit due to the "upperbit" routine (result too small, |
3729 | | // requiring a shift) |
3730 | | |
3731 | 25 | value128 product = |
3732 | 25 | compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w); |
3733 | | // The computed 'product' is always sufficient. |
3734 | | // Mathematical proof: |
3735 | | // Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to |
3736 | | // appear) See script/mushtak_lemire.py |
3737 | | |
3738 | | // The "compute_product_approximation" function can be slightly slower than a |
3739 | | // branchless approach: value128 product = compute_product(q, w); but in |
3740 | | // practice, we can win big with the compute_product_approximation if its |
3741 | | // additional branch is easily predicted. Which is best is data specific. |
3742 | 25 | int upperbit = int(product.high >> 63); |
3743 | 25 | int shift = upperbit + 64 - binary::mantissa_explicit_bits() - 3; |
3744 | | |
3745 | 25 | answer.mantissa = product.high >> shift; |
3746 | | |
3747 | 25 | answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - |
3748 | 25 | binary::minimum_exponent()); |
3749 | 25 | if (answer.power2 <= 0) { // we have a subnormal? |
3750 | | // Here have that answer.power2 <= 0 so -answer.power2 >= 0 |
3751 | 0 | if (-answer.power2 + 1 >= |
3752 | 0 | 64) { // if we have more than 64 bits below the minimum exponent, you |
3753 | | // have a zero for sure. |
3754 | 0 | answer.power2 = 0; |
3755 | 0 | answer.mantissa = 0; |
3756 | | // result should be zero |
3757 | 0 | return answer; |
3758 | 0 | } |
3759 | | // next line is safe because -answer.power2 + 1 < 64 |
3760 | 0 | answer.mantissa >>= -answer.power2 + 1; |
3761 | | // Thankfully, we can't have both "round-to-even" and subnormals because |
3762 | | // "round-to-even" only occurs for powers close to 0 in the 32-bit and |
3763 | | // and 64-bit case (with no more than 19 digits). |
3764 | 0 | answer.mantissa += (answer.mantissa & 1); // round up |
3765 | 0 | answer.mantissa >>= 1; |
3766 | | // There is a weird scenario where we don't have a subnormal but just. |
3767 | | // Suppose we start with 2.2250738585072013e-308, we end up |
3768 | | // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal |
3769 | | // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round |
3770 | | // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer |
3771 | | // subnormal, but we can only know this after rounding. |
3772 | | // So we only declare a subnormal if we are smaller than the threshold. |
3773 | 0 | answer.power2 = |
3774 | 0 | (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) |
3775 | 0 | ? 0 |
3776 | 0 | : 1; |
3777 | 0 | return answer; |
3778 | 0 | } |
3779 | | |
3780 | | // usually, we round *up*, but if we fall right in between and and we have an |
3781 | | // even basis, we need to round down |
3782 | | // We are only concerned with the cases where 5**q fits in single 64-bit word. |
3783 | 25 | if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && |
3784 | 0 | (q <= binary::max_exponent_round_to_even()) && |
3785 | 0 | ((answer.mantissa & 3) == 1)) { // we may fall between two floats! |
3786 | | // To be in-between two floats we need that in doing |
3787 | | // answer.mantissa = product.high >> (upperbit + 64 - |
3788 | | // binary::mantissa_explicit_bits() - 3); |
3789 | | // ... we dropped out only zeroes. But if this happened, then we can go |
3790 | | // back!!! |
3791 | 0 | if ((answer.mantissa << shift) == product.high) { |
3792 | 0 | answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up |
3793 | 0 | } |
3794 | 0 | } |
3795 | | |
3796 | 25 | answer.mantissa += (answer.mantissa & 1); // round up |
3797 | 25 | answer.mantissa >>= 1; |
3798 | 25 | if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) { |
3799 | 1 | answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits()); |
3800 | 1 | answer.power2++; // undo previous addition |
3801 | 1 | } |
3802 | | |
3803 | 25 | answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits()); |
3804 | 25 | if (answer.power2 >= binary::infinite_power()) { // infinity |
3805 | 0 | answer.power2 = binary::infinite_power(); |
3806 | 0 | answer.mantissa = 0; |
3807 | 0 | } |
3808 | 25 | return answer; |
3809 | 25 | } |
3810 | | |
3811 | | } // namespace fast_float |
3812 | | |
3813 | | #endif |
3814 | | |
3815 | | #ifndef FASTFLOAT_BIGINT_H |
3816 | | #define FASTFLOAT_BIGINT_H |
3817 | | |
3818 | | #include <cstring> |
3819 | | |
3820 | | |
3821 | | namespace fast_float { |
3822 | | |
3823 | | // the limb width: we want efficient multiplication of double the bits in |
3824 | | // limb, or for 64-bit limbs, at least 64-bit multiplication where we can |
3825 | | // extract the high and low parts efficiently. this is every 64-bit |
3826 | | // architecture except for sparc, which emulates 128-bit multiplication. |
3827 | | // we might have platforms where `CHAR_BIT` is not 8, so let's avoid |
3828 | | // doing `8 * sizeof(limb)`. |
3829 | | #if defined(FASTFLOAT_64BIT) && !defined(__sparc) |
3830 | | #define FASTFLOAT_64BIT_LIMB 1 |
3831 | | typedef uint64_t limb; |
3832 | | constexpr size_t limb_bits = 64; |
3833 | | #else |
3834 | | #define FASTFLOAT_32BIT_LIMB |
3835 | | typedef uint32_t limb; |
3836 | | constexpr size_t limb_bits = 32; |
3837 | | #endif |
3838 | | |
3839 | | typedef span<limb> limb_span; |
3840 | | |
3841 | | // number of bits in a bigint. this needs to be at least the number |
3842 | | // of bits required to store the largest bigint, which is |
3843 | | // `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or |
3844 | | // ~3600 bits, so we round to 4000. |
3845 | | constexpr size_t bigint_bits = 4000; |
3846 | | constexpr size_t bigint_limbs = bigint_bits / limb_bits; |
3847 | | |
3848 | | // vector-like type that is allocated on the stack. the entire |
3849 | | // buffer is pre-allocated, and only the length changes. |
3850 | | template <uint16_t size> struct stackvec { |
3851 | | limb data[size]; |
3852 | | // we never need more than 150 limbs |
3853 | | uint16_t length{0}; |
3854 | | |
3855 | 0 | stackvec() = default; |
3856 | | stackvec(stackvec const &) = delete; |
3857 | | stackvec &operator=(stackvec const &) = delete; |
3858 | | stackvec(stackvec &&) = delete; |
3859 | | stackvec &operator=(stackvec &&other) = delete; |
3860 | | |
3861 | | // create stack vector from existing limb span. |
3862 | 0 | FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) { |
3863 | 0 | FASTFLOAT_ASSERT(try_extend(s)); |
3864 | 0 | } |
3865 | | |
3866 | 0 | FASTFLOAT_CONSTEXPR14 limb &operator[](size_t index) noexcept { |
3867 | 0 | FASTFLOAT_DEBUG_ASSERT(index < length); |
3868 | 0 | return data[index]; |
3869 | 0 | } |
3870 | | |
3871 | 0 | FASTFLOAT_CONSTEXPR14 const limb &operator[](size_t index) const noexcept { |
3872 | 0 | FASTFLOAT_DEBUG_ASSERT(index < length); |
3873 | 0 | return data[index]; |
3874 | 0 | } |
3875 | | |
3876 | | // index from the end of the container |
3877 | 0 | FASTFLOAT_CONSTEXPR14 const limb &rindex(size_t index) const noexcept { |
3878 | 0 | FASTFLOAT_DEBUG_ASSERT(index < length); |
3879 | 0 | size_t rindex = length - index - 1; |
3880 | 0 | return data[rindex]; |
3881 | 0 | } |
3882 | | |
3883 | | // set the length, without bounds checking. |
3884 | 0 | FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept { |
3885 | 0 | length = uint16_t(len); |
3886 | 0 | } |
3887 | | |
3888 | 0 | constexpr size_t len() const noexcept { return length; } |
3889 | | |
3890 | 0 | constexpr bool is_empty() const noexcept { return length == 0; } |
3891 | | |
3892 | 0 | constexpr size_t capacity() const noexcept { return size; } |
3893 | | |
3894 | | // append item to vector, without bounds checking |
3895 | 0 | FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept { |
3896 | 0 | data[length] = value; |
3897 | 0 | length++; |
3898 | 0 | } |
3899 | | |
3900 | | // append item to vector, returning if item was added |
3901 | 0 | FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept { |
3902 | 0 | if (len() < capacity()) { |
3903 | 0 | push_unchecked(value); |
3904 | 0 | return true; |
3905 | 0 | } else { |
3906 | 0 | return false; |
3907 | 0 | } |
3908 | 0 | } |
3909 | | |
3910 | | // add items to the vector, from a span, without bounds checking |
3911 | 0 | FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept { |
3912 | 0 | limb *ptr = data + length; |
3913 | 0 | tinyobj_ff::copy_n(s.ptr, s.len(), ptr); |
3914 | 0 | set_len(len() + s.len()); |
3915 | 0 | } |
3916 | | |
3917 | | // try to add items to the vector, returning if items were added |
3918 | 0 | FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept { |
3919 | 0 | if (len() + s.len() <= capacity()) { |
3920 | 0 | extend_unchecked(s); |
3921 | 0 | return true; |
3922 | 0 | } else { |
3923 | 0 | return false; |
3924 | 0 | } |
3925 | 0 | } |
3926 | | |
3927 | | // resize the vector, without bounds checking |
3928 | | // if the new size is longer than the vector, assign value to each |
3929 | | // appended item. |
3930 | | FASTFLOAT_CONSTEXPR20 |
3931 | 0 | void resize_unchecked(size_t new_len, limb value) noexcept { |
3932 | 0 | if (new_len > len()) { |
3933 | 0 | size_t count = new_len - len(); |
3934 | 0 | limb *first = data + len(); |
3935 | 0 | limb *last = first + count; |
3936 | 0 | tinyobj_ff::fill(first, last, value); |
3937 | 0 | set_len(new_len); |
3938 | 0 | } else { |
3939 | 0 | set_len(new_len); |
3940 | 0 | } |
3941 | 0 | } |
3942 | | |
3943 | | // try to resize the vector, returning if the vector was resized. |
3944 | 0 | FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept { |
3945 | 0 | if (new_len > capacity()) { |
3946 | 0 | return false; |
3947 | 0 | } else { |
3948 | 0 | resize_unchecked(new_len, value); |
3949 | 0 | return true; |
3950 | 0 | } |
3951 | 0 | } |
3952 | | |
3953 | | // check if any limbs are non-zero after the given index. |
3954 | | // this needs to be done in reverse order, since the index |
3955 | | // is relative to the most significant limbs. |
3956 | 0 | FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept { |
3957 | 0 | while (index < len()) { |
3958 | 0 | if (rindex(index) != 0) { |
3959 | 0 | return true; |
3960 | 0 | } |
3961 | 0 | index++; |
3962 | 0 | } |
3963 | 0 | return false; |
3964 | 0 | } |
3965 | | |
3966 | | // normalize the big integer, so most-significant zero limbs are removed. |
3967 | 0 | FASTFLOAT_CONSTEXPR14 void normalize() noexcept { |
3968 | 0 | while (len() > 0 && rindex(0) == 0) { |
3969 | 0 | length--; |
3970 | 0 | } |
3971 | 0 | } |
3972 | | }; |
3973 | | |
3974 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t |
3975 | 0 | empty_hi64(bool &truncated) noexcept { |
3976 | 0 | truncated = false; |
3977 | 0 | return 0; |
3978 | 0 | } |
3979 | | |
3980 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t |
3981 | 0 | uint64_hi64(uint64_t r0, bool &truncated) noexcept { |
3982 | 0 | truncated = false; |
3983 | 0 | int shl = leading_zeroes(r0); |
3984 | 0 | return r0 << shl; |
3985 | 0 | } |
3986 | | |
3987 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t |
3988 | 0 | uint64_hi64(uint64_t r0, uint64_t r1, bool &truncated) noexcept { |
3989 | 0 | int shl = leading_zeroes(r0); |
3990 | 0 | if (shl == 0) { |
3991 | 0 | truncated = r1 != 0; |
3992 | 0 | return r0; |
3993 | 0 | } else { |
3994 | 0 | int shr = 64 - shl; |
3995 | 0 | truncated = (r1 << shl) != 0; |
3996 | 0 | return (r0 << shl) | (r1 >> shr); |
3997 | 0 | } |
3998 | 0 | } |
3999 | | |
4000 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t |
4001 | 0 | uint32_hi64(uint32_t r0, bool &truncated) noexcept { |
4002 | 0 | return uint64_hi64(r0, truncated); |
4003 | 0 | } |
4004 | | |
4005 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t |
4006 | 0 | uint32_hi64(uint32_t r0, uint32_t r1, bool &truncated) noexcept { |
4007 | 0 | uint64_t x0 = r0; |
4008 | 0 | uint64_t x1 = r1; |
4009 | 0 | return uint64_hi64((x0 << 32) | x1, truncated); |
4010 | 0 | } |
4011 | | |
4012 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t |
4013 | 0 | uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool &truncated) noexcept { |
4014 | 0 | uint64_t x0 = r0; |
4015 | 0 | uint64_t x1 = r1; |
4016 | 0 | uint64_t x2 = r2; |
4017 | 0 | return uint64_hi64(x0, (x1 << 32) | x2, truncated); |
4018 | 0 | } |
4019 | | |
4020 | | // add two small integers, checking for overflow. |
4021 | | // we want an efficient operation. for msvc, where |
4022 | | // we don't have built-in intrinsics, this is still |
4023 | | // pretty fast. |
4024 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb |
4025 | 0 | scalar_add(limb x, limb y, bool &overflow) noexcept { |
4026 | 0 | limb z; |
4027 | | // gcc and clang |
4028 | 0 | #if defined(__has_builtin) |
4029 | 0 | #if __has_builtin(__builtin_add_overflow) |
4030 | 0 | if (!cpp20_and_in_constexpr()) { |
4031 | 0 | overflow = __builtin_add_overflow(x, y, &z); |
4032 | 0 | return z; |
4033 | 0 | } |
4034 | 0 | #endif |
4035 | 0 | #endif |
4036 | | |
4037 | | // generic, this still optimizes correctly on MSVC. |
4038 | 0 | z = x + y; |
4039 | 0 | overflow = z < x; |
4040 | 0 | return z; |
4041 | 0 | } |
4042 | | |
4043 | | // multiply two small integers, getting both the high and low bits. |
4044 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb |
4045 | 0 | scalar_mul(limb x, limb y, limb &carry) noexcept { |
4046 | 0 | #ifdef FASTFLOAT_64BIT_LIMB |
4047 | 0 | #if defined(__SIZEOF_INT128__) |
4048 | | // GCC and clang both define it as an extension. |
4049 | 0 | __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry); |
4050 | 0 | carry = limb(z >> limb_bits); |
4051 | 0 | return limb(z); |
4052 | | #else |
4053 | | // fallback, no native 128-bit integer multiplication with carry. |
4054 | | // on msvc, this optimizes identically, somehow. |
4055 | | value128 z = full_multiplication(x, y); |
4056 | | bool overflow; |
4057 | | z.low = scalar_add(z.low, carry, overflow); |
4058 | | z.high += uint64_t(overflow); // cannot overflow |
4059 | | carry = z.high; |
4060 | | return z.low; |
4061 | | #endif |
4062 | | #else |
4063 | | uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry); |
4064 | | carry = limb(z >> limb_bits); |
4065 | | return limb(z); |
4066 | | #endif |
4067 | 0 | } |
4068 | | |
4069 | | // add scalar value to bigint starting from offset. |
4070 | | // used in grade school multiplication |
4071 | | template <uint16_t size> |
4072 | | inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y, |
4073 | 0 | size_t start) noexcept { |
4074 | 0 | size_t index = start; |
4075 | 0 | limb carry = y; |
4076 | 0 | bool overflow; |
4077 | 0 | while (carry != 0 && index < vec.len()) { |
4078 | 0 | vec[index] = scalar_add(vec[index], carry, overflow); |
4079 | 0 | carry = limb(overflow); |
4080 | 0 | index += 1; |
4081 | 0 | } |
4082 | 0 | if (carry != 0) { |
4083 | 0 | FASTFLOAT_TRY(vec.try_push(carry)); |
4084 | 0 | } |
4085 | 0 | return true; |
4086 | 0 | } |
4087 | | |
4088 | | // add scalar value to bigint. |
4089 | | template <uint16_t size> |
4090 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool |
4091 | 0 | small_add(stackvec<size> &vec, limb y) noexcept { |
4092 | 0 | return small_add_from(vec, y, 0); |
4093 | 0 | } |
4094 | | |
4095 | | // multiply bigint by scalar value. |
4096 | | template <uint16_t size> |
4097 | | inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec, |
4098 | 0 | limb y) noexcept { |
4099 | 0 | limb carry = 0; |
4100 | 0 | for (size_t index = 0; index < vec.len(); index++) { |
4101 | 0 | vec[index] = scalar_mul(vec[index], y, carry); |
4102 | 0 | } |
4103 | 0 | if (carry != 0) { |
4104 | 0 | FASTFLOAT_TRY(vec.try_push(carry)); |
4105 | 0 | } |
4106 | 0 | return true; |
4107 | 0 | } |
4108 | | |
4109 | | // add bigint to bigint starting from index. |
4110 | | // used in grade school multiplication |
4111 | | template <uint16_t size> |
4112 | | FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y, |
4113 | 0 | size_t start) noexcept { |
4114 | | // the effective x buffer is from `xstart..x.len()`, so exit early |
4115 | | // if we can't get that current range. |
4116 | 0 | if (x.len() < start || y.len() > x.len() - start) { |
4117 | 0 | FASTFLOAT_TRY(x.try_resize(y.len() + start, 0)); |
4118 | 0 | } |
4119 | | |
4120 | 0 | bool carry = false; |
4121 | 0 | for (size_t index = 0; index < y.len(); index++) { |
4122 | 0 | limb xi = x[index + start]; |
4123 | 0 | limb yi = y[index]; |
4124 | 0 | bool c1 = false; |
4125 | 0 | bool c2 = false; |
4126 | 0 | xi = scalar_add(xi, yi, c1); |
4127 | 0 | if (carry) { |
4128 | 0 | xi = scalar_add(xi, 1, c2); |
4129 | 0 | } |
4130 | 0 | x[index + start] = xi; |
4131 | 0 | carry = c1 | c2; |
4132 | 0 | } |
4133 | | |
4134 | | // handle overflow |
4135 | 0 | if (carry) { |
4136 | 0 | FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start)); |
4137 | 0 | } |
4138 | 0 | return true; |
4139 | 0 | } |
4140 | | |
4141 | | // add bigint to bigint. |
4142 | | template <uint16_t size> |
4143 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool |
4144 | | large_add_from(stackvec<size> &x, limb_span y) noexcept { |
4145 | | return large_add_from(x, y, 0); |
4146 | | } |
4147 | | |
4148 | | // grade-school multiplication algorithm |
4149 | | template <uint16_t size> |
4150 | 0 | FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept { |
4151 | 0 | limb_span xs = limb_span(x.data, x.len()); |
4152 | 0 | stackvec<size> z(xs); |
4153 | 0 | limb_span zs = limb_span(z.data, z.len()); |
4154 | |
|
4155 | 0 | if (y.len() != 0) { |
4156 | 0 | limb y0 = y[0]; |
4157 | 0 | FASTFLOAT_TRY(small_mul(x, y0)); |
4158 | 0 | for (size_t index = 1; index < y.len(); index++) { |
4159 | 0 | limb yi = y[index]; |
4160 | 0 | stackvec<size> zi; |
4161 | 0 | if (yi != 0) { |
4162 | | // re-use the same buffer throughout |
4163 | 0 | zi.set_len(0); |
4164 | 0 | FASTFLOAT_TRY(zi.try_extend(zs)); |
4165 | 0 | FASTFLOAT_TRY(small_mul(zi, yi)); |
4166 | 0 | limb_span zis = limb_span(zi.data, zi.len()); |
4167 | 0 | FASTFLOAT_TRY(large_add_from(x, zis, index)); |
4168 | 0 | } |
4169 | 0 | } |
4170 | 0 | } |
4171 | | |
4172 | 0 | x.normalize(); |
4173 | 0 | return true; |
4174 | 0 | } |
4175 | | |
4176 | | // grade-school multiplication algorithm |
4177 | | template <uint16_t size> |
4178 | 0 | FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept { |
4179 | 0 | if (y.len() == 1) { |
4180 | 0 | FASTFLOAT_TRY(small_mul(x, y[0])); |
4181 | 0 | } else { |
4182 | 0 | FASTFLOAT_TRY(long_mul(x, y)); |
4183 | 0 | } |
4184 | 0 | return true; |
4185 | 0 | } |
4186 | | |
4187 | | template <typename = void> struct pow5_tables { |
4188 | | static constexpr uint32_t large_step = 135; |
4189 | | static constexpr uint64_t small_power_of_5[] = { |
4190 | | 1UL, |
4191 | | 5UL, |
4192 | | 25UL, |
4193 | | 125UL, |
4194 | | 625UL, |
4195 | | 3125UL, |
4196 | | 15625UL, |
4197 | | 78125UL, |
4198 | | 390625UL, |
4199 | | 1953125UL, |
4200 | | 9765625UL, |
4201 | | 48828125UL, |
4202 | | 244140625UL, |
4203 | | 1220703125UL, |
4204 | | 6103515625UL, |
4205 | | 30517578125UL, |
4206 | | 152587890625UL, |
4207 | | 762939453125UL, |
4208 | | 3814697265625UL, |
4209 | | 19073486328125UL, |
4210 | | 95367431640625UL, |
4211 | | 476837158203125UL, |
4212 | | 2384185791015625UL, |
4213 | | 11920928955078125UL, |
4214 | | 59604644775390625UL, |
4215 | | 298023223876953125UL, |
4216 | | 1490116119384765625UL, |
4217 | | 7450580596923828125UL, |
4218 | | }; |
4219 | | #ifdef FASTFLOAT_64BIT_LIMB |
4220 | | constexpr static limb large_power_of_5[] = { |
4221 | | 1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL, |
4222 | | 10482974169319127550UL, 198276706040285095UL}; |
4223 | | #else |
4224 | | constexpr static limb large_power_of_5[] = { |
4225 | | 4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U, |
4226 | | 1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U}; |
4227 | | #endif |
4228 | | }; |
4229 | | |
4230 | | #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE |
4231 | | |
4232 | | template <typename T> constexpr uint32_t pow5_tables<T>::large_step; |
4233 | | |
4234 | | template <typename T> constexpr uint64_t pow5_tables<T>::small_power_of_5[]; |
4235 | | |
4236 | | template <typename T> constexpr limb pow5_tables<T>::large_power_of_5[]; |
4237 | | |
4238 | | #endif |
4239 | | |
4240 | | // big integer type. implements a small subset of big integer |
4241 | | // arithmetic, using simple algorithms since asymptotically |
4242 | | // faster algorithms are slower for a small number of limbs. |
4243 | | // all operations assume the big-integer is normalized. |
4244 | | struct bigint : pow5_tables<> { |
4245 | | // storage of the limbs, in little-endian order. |
4246 | | stackvec<bigint_limbs> vec; |
4247 | | |
4248 | 0 | FASTFLOAT_CONSTEXPR20 bigint() : vec() {} |
4249 | | |
4250 | | bigint(bigint const &) = delete; |
4251 | | bigint &operator=(bigint const &) = delete; |
4252 | | bigint(bigint &&) = delete; |
4253 | | bigint &operator=(bigint &&other) = delete; |
4254 | | |
4255 | 0 | FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) : vec() { |
4256 | 0 | #ifdef FASTFLOAT_64BIT_LIMB |
4257 | 0 | vec.push_unchecked(value); |
4258 | | #else |
4259 | | vec.push_unchecked(uint32_t(value)); |
4260 | | vec.push_unchecked(uint32_t(value >> 32)); |
4261 | | #endif |
4262 | 0 | vec.normalize(); |
4263 | 0 | } |
4264 | | |
4265 | | // get the high 64 bits from the vector, and if bits were truncated. |
4266 | | // this is to get the significant digits for the float. |
4267 | 0 | FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool &truncated) const noexcept { |
4268 | 0 | #ifdef FASTFLOAT_64BIT_LIMB |
4269 | 0 | if (vec.len() == 0) { |
4270 | 0 | return empty_hi64(truncated); |
4271 | 0 | } else if (vec.len() == 1) { |
4272 | 0 | return uint64_hi64(vec.rindex(0), truncated); |
4273 | 0 | } else { |
4274 | 0 | uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated); |
4275 | 0 | truncated |= vec.nonzero(2); |
4276 | 0 | return result; |
4277 | 0 | } |
4278 | | #else |
4279 | | if (vec.len() == 0) { |
4280 | | return empty_hi64(truncated); |
4281 | | } else if (vec.len() == 1) { |
4282 | | return uint32_hi64(vec.rindex(0), truncated); |
4283 | | } else if (vec.len() == 2) { |
4284 | | return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated); |
4285 | | } else { |
4286 | | uint64_t result = |
4287 | | uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated); |
4288 | | truncated |= vec.nonzero(3); |
4289 | | return result; |
4290 | | } |
4291 | | #endif |
4292 | 0 | } |
4293 | | |
4294 | | // compare two big integers, returning the large value. |
4295 | | // assumes both are normalized. if the return value is |
4296 | | // negative, other is larger, if the return value is |
4297 | | // positive, this is larger, otherwise they are equal. |
4298 | | // the limbs are stored in little-endian order, so we |
4299 | | // must compare the limbs in ever order. |
4300 | 0 | FASTFLOAT_CONSTEXPR20 int compare(bigint const &other) const noexcept { |
4301 | 0 | if (vec.len() > other.vec.len()) { |
4302 | 0 | return 1; |
4303 | 0 | } else if (vec.len() < other.vec.len()) { |
4304 | 0 | return -1; |
4305 | 0 | } else { |
4306 | 0 | for (size_t index = vec.len(); index > 0; index--) { |
4307 | 0 | limb xi = vec[index - 1]; |
4308 | 0 | limb yi = other.vec[index - 1]; |
4309 | 0 | if (xi > yi) { |
4310 | 0 | return 1; |
4311 | 0 | } else if (xi < yi) { |
4312 | 0 | return -1; |
4313 | 0 | } |
4314 | 0 | } |
4315 | 0 | return 0; |
4316 | 0 | } |
4317 | 0 | } |
4318 | | |
4319 | | // shift left each limb n bits, carrying over to the new limb |
4320 | | // returns true if we were able to shift all the digits. |
4321 | 0 | FASTFLOAT_CONSTEXPR20 bool shl_bits(size_t n) noexcept { |
4322 | | // Internally, for each item, we shift left by n, and add the previous |
4323 | | // right shifted limb-bits. |
4324 | | // For example, we transform (for u8) shifted left 2, to: |
4325 | | // b10100100 b01000010 |
4326 | | // b10 b10010001 b00001000 |
4327 | 0 | FASTFLOAT_DEBUG_ASSERT(n != 0); |
4328 | 0 | FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8); |
4329 | |
|
4330 | 0 | size_t shl = n; |
4331 | 0 | size_t shr = limb_bits - shl; |
4332 | 0 | limb prev = 0; |
4333 | 0 | for (size_t index = 0; index < vec.len(); index++) { |
4334 | 0 | limb xi = vec[index]; |
4335 | 0 | vec[index] = (xi << shl) | (prev >> shr); |
4336 | 0 | prev = xi; |
4337 | 0 | } |
4338 | |
|
4339 | 0 | limb carry = prev >> shr; |
4340 | 0 | if (carry != 0) { |
4341 | 0 | return vec.try_push(carry); |
4342 | 0 | } |
4343 | 0 | return true; |
4344 | 0 | } |
4345 | | |
4346 | | // move the limbs left by `n` limbs. |
4347 | 0 | FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept { |
4348 | 0 | FASTFLOAT_DEBUG_ASSERT(n != 0); |
4349 | 0 | if (n + vec.len() > vec.capacity()) { |
4350 | 0 | return false; |
4351 | 0 | } else if (!vec.is_empty()) { |
4352 | | // move limbs |
4353 | 0 | limb *dst = vec.data + n; |
4354 | 0 | limb const *src = vec.data; |
4355 | 0 | tinyobj_ff::copy_backward(src, src + vec.len(), dst + vec.len()); |
4356 | | // fill in empty limbs |
4357 | 0 | limb *first = vec.data; |
4358 | 0 | limb *last = first + n; |
4359 | 0 | tinyobj_ff::fill(first, last, 0); |
4360 | 0 | vec.set_len(n + vec.len()); |
4361 | 0 | return true; |
4362 | 0 | } else { |
4363 | 0 | return true; |
4364 | 0 | } |
4365 | 0 | } |
4366 | | |
4367 | | // move the limbs left by `n` bits. |
4368 | 0 | FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept { |
4369 | 0 | size_t rem = n % limb_bits; |
4370 | 0 | size_t div = n / limb_bits; |
4371 | 0 | if (rem != 0) { |
4372 | 0 | FASTFLOAT_TRY(shl_bits(rem)); |
4373 | 0 | } |
4374 | 0 | if (div != 0) { |
4375 | 0 | FASTFLOAT_TRY(shl_limbs(div)); |
4376 | 0 | } |
4377 | 0 | return true; |
4378 | 0 | } |
4379 | | |
4380 | | // get the number of leading zeros in the bigint. |
4381 | 0 | FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept { |
4382 | 0 | if (vec.is_empty()) { |
4383 | 0 | return 0; |
4384 | 0 | } else { |
4385 | 0 | #ifdef FASTFLOAT_64BIT_LIMB |
4386 | 0 | return leading_zeroes(vec.rindex(0)); |
4387 | | #else |
4388 | | // no use defining a specialized leading_zeroes for a 32-bit type. |
4389 | | uint64_t r0 = vec.rindex(0); |
4390 | | return leading_zeroes(r0 << 32); |
4391 | | #endif |
4392 | 0 | } |
4393 | 0 | } |
4394 | | |
4395 | | // get the number of bits in the bigint. |
4396 | 0 | FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept { |
4397 | 0 | int lz = ctlz(); |
4398 | 0 | return int(limb_bits * vec.len()) - lz; |
4399 | 0 | } |
4400 | | |
4401 | 0 | FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { return small_mul(vec, y); } |
4402 | | |
4403 | 0 | FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); } |
4404 | | |
4405 | | // multiply as if by 2 raised to a power. |
4406 | 0 | FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept { return shl(exp); } |
4407 | | |
4408 | | // multiply as if by 5 raised to a power. |
4409 | 0 | FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept { |
4410 | | // multiply by a power of 5 |
4411 | 0 | size_t large_length = sizeof(large_power_of_5) / sizeof(limb); |
4412 | 0 | limb_span large = limb_span(large_power_of_5, large_length); |
4413 | 0 | while (exp >= large_step) { |
4414 | 0 | FASTFLOAT_TRY(large_mul(vec, large)); |
4415 | 0 | exp -= large_step; |
4416 | 0 | } |
4417 | 0 | #ifdef FASTFLOAT_64BIT_LIMB |
4418 | 0 | uint32_t small_step = 27; |
4419 | 0 | limb max_native = 7450580596923828125UL; |
4420 | | #else |
4421 | | uint32_t small_step = 13; |
4422 | | limb max_native = 1220703125U; |
4423 | | #endif |
4424 | 0 | while (exp >= small_step) { |
4425 | 0 | FASTFLOAT_TRY(small_mul(vec, max_native)); |
4426 | 0 | exp -= small_step; |
4427 | 0 | } |
4428 | 0 | if (exp != 0) { |
4429 | | // Work around clang bug https://godbolt.org/z/zedh7rrhc |
4430 | | // This is similar to https://github.com/llvm/llvm-project/issues/47746, |
4431 | | // except the workaround described there don't work here |
4432 | 0 | FASTFLOAT_TRY(small_mul( |
4433 | 0 | vec, limb(((void)small_power_of_5[0], small_power_of_5[exp])))); |
4434 | 0 | } |
4435 | | |
4436 | 0 | return true; |
4437 | 0 | } |
4438 | | |
4439 | | // multiply as if by 10 raised to a power. |
4440 | 0 | FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept { |
4441 | 0 | FASTFLOAT_TRY(pow5(exp)); |
4442 | 0 | return pow2(exp); |
4443 | 0 | } |
4444 | | }; |
4445 | | |
4446 | | } // namespace fast_float |
4447 | | |
4448 | | #endif |
4449 | | |
4450 | | #ifndef FASTFLOAT_DIGIT_COMPARISON_H |
4451 | | #define FASTFLOAT_DIGIT_COMPARISON_H |
4452 | | |
4453 | | #include <cstring> |
4454 | | |
4455 | | |
4456 | | namespace fast_float { |
4457 | | |
4458 | | // 1e0 to 1e19 |
4459 | | constexpr static uint64_t powers_of_ten_uint64[] = {1UL, |
4460 | | 10UL, |
4461 | | 100UL, |
4462 | | 1000UL, |
4463 | | 10000UL, |
4464 | | 100000UL, |
4465 | | 1000000UL, |
4466 | | 10000000UL, |
4467 | | 100000000UL, |
4468 | | 1000000000UL, |
4469 | | 10000000000UL, |
4470 | | 100000000000UL, |
4471 | | 1000000000000UL, |
4472 | | 10000000000000UL, |
4473 | | 100000000000000UL, |
4474 | | 1000000000000000UL, |
4475 | | 10000000000000000UL, |
4476 | | 100000000000000000UL, |
4477 | | 1000000000000000000UL, |
4478 | | 10000000000000000000UL}; |
4479 | | |
4480 | | // calculate the exponent, in scientific notation, of the number. |
4481 | | // this algorithm is not even close to optimized, but it has no practical |
4482 | | // effect on performance: in order to have a faster algorithm, we'd need |
4483 | | // to slow down performance for faster algorithms, and this is still fast. |
4484 | | template <typename UC> |
4485 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t |
4486 | 0 | scientific_exponent(parsed_number_string_t<UC> &num) noexcept { |
4487 | 0 | uint64_t mantissa = num.mantissa; |
4488 | 0 | int32_t exponent = int32_t(num.exponent); |
4489 | 0 | while (mantissa >= 10000) { |
4490 | 0 | mantissa /= 10000; |
4491 | 0 | exponent += 4; |
4492 | 0 | } |
4493 | 0 | while (mantissa >= 100) { |
4494 | 0 | mantissa /= 100; |
4495 | 0 | exponent += 2; |
4496 | 0 | } |
4497 | 0 | while (mantissa >= 10) { |
4498 | 0 | mantissa /= 10; |
4499 | 0 | exponent += 1; |
4500 | 0 | } |
4501 | 0 | return exponent; |
4502 | 0 | } |
4503 | | |
4504 | | // this converts a native floating-point number to an extended-precision float. |
4505 | | template <typename T> |
4506 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa |
4507 | 0 | to_extended(T value) noexcept { |
4508 | 0 | using equiv_uint = equiv_uint_t<T>; |
4509 | 0 | constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask(); |
4510 | 0 | constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask(); |
4511 | 0 | constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask(); |
4512 | |
|
4513 | 0 | adjusted_mantissa am; |
4514 | 0 | int32_t bias = binary_format<T>::mantissa_explicit_bits() - |
4515 | 0 | binary_format<T>::minimum_exponent(); |
4516 | 0 | equiv_uint bits; |
4517 | | #if FASTFLOAT_HAS_BIT_CAST |
4518 | | bits = std::bit_cast<equiv_uint>(value); |
4519 | | #else |
4520 | 0 | ::memcpy(&bits, &value, sizeof(T)); |
4521 | 0 | #endif |
4522 | 0 | if ((bits & exponent_mask) == 0) { |
4523 | | // denormal |
4524 | 0 | am.power2 = 1 - bias; |
4525 | 0 | am.mantissa = bits & mantissa_mask; |
4526 | 0 | } else { |
4527 | | // normal |
4528 | 0 | am.power2 = int32_t((bits & exponent_mask) >> |
4529 | 0 | binary_format<T>::mantissa_explicit_bits()); |
4530 | 0 | am.power2 -= bias; |
4531 | 0 | am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; |
4532 | 0 | } |
4533 | |
|
4534 | 0 | return am; |
4535 | 0 | } |
4536 | | |
4537 | | // get the extended precision value of the halfway point between b and b+u. |
4538 | | // we are given a native float that represents b, so we need to adjust it |
4539 | | // halfway between b and b+u. |
4540 | | template <typename T> |
4541 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa |
4542 | 0 | to_extended_halfway(T value) noexcept { |
4543 | 0 | adjusted_mantissa am = to_extended(value); |
4544 | 0 | am.mantissa <<= 1; |
4545 | 0 | am.mantissa += 1; |
4546 | 0 | am.power2 -= 1; |
4547 | 0 | return am; |
4548 | 0 | } |
4549 | | |
4550 | | // round an extended-precision float to the nearest machine float. |
4551 | | template <typename T, typename callback> |
4552 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am, |
4553 | 0 | callback cb) noexcept { |
4554 | 0 | int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1; |
4555 | 0 | if (-am.power2 >= mantissa_shift) { |
4556 | | // have a denormal float |
4557 | 0 | int32_t shift = -am.power2 + 1; |
4558 | 0 | cb(am, tinyobj_ff::min_val<int32_t>(shift, 64)); |
4559 | | // check for round-up: if rounding-nearest carried us to the hidden bit. |
4560 | 0 | am.power2 = (am.mantissa < |
4561 | 0 | (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) |
4562 | 0 | ? 0 |
4563 | 0 | : 1; |
4564 | 0 | return; |
4565 | 0 | } |
4566 | | |
4567 | | // have a normal float, use the default shift. |
4568 | 0 | cb(am, mantissa_shift); |
4569 | | |
4570 | | // check for carry |
4571 | 0 | if (am.mantissa >= |
4572 | 0 | (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) { |
4573 | 0 | am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits()); |
4574 | 0 | am.power2++; |
4575 | 0 | } |
4576 | | |
4577 | | // check for infinite: we could have carried to an infinite power |
4578 | 0 | am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits()); |
4579 | 0 | if (am.power2 >= binary_format<T>::infinite_power()) { |
4580 | 0 | am.power2 = binary_format<T>::infinite_power(); |
4581 | 0 | am.mantissa = 0; |
4582 | 0 | } |
4583 | 0 | } Unexecuted instantiation: void fast_float::round<double, fast_float::positive_digit_comp<double>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}>(fast_float::adjusted_mantissa&, fast_float::positive_digit_comp<double>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1})Unexecuted instantiation: void fast_float::round<double, fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}>(fast_float::adjusted_mantissa&, fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#1})Unexecuted instantiation: void fast_float::round<double, fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2}>(fast_float::adjusted_mantissa&, fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2}) |
4584 | | |
4585 | | template <typename callback> |
4586 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void |
4587 | | round_nearest_tie_even(adjusted_mantissa &am, int32_t shift, |
4588 | 0 | callback cb) noexcept { |
4589 | 0 | uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1; |
4590 | 0 | uint64_t const halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1); |
4591 | 0 | uint64_t truncated_bits = am.mantissa & mask; |
4592 | 0 | bool is_above = truncated_bits > halfway; |
4593 | 0 | bool is_halfway = truncated_bits == halfway; |
4594 | | |
4595 | | // shift digits into position |
4596 | 0 | if (shift == 64) { |
4597 | 0 | am.mantissa = 0; |
4598 | 0 | } else { |
4599 | 0 | am.mantissa >>= shift; |
4600 | 0 | } |
4601 | 0 | am.power2 += shift; |
4602 | |
|
4603 | 0 | bool is_odd = (am.mantissa & 1) == 1; |
4604 | 0 | am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above)); |
4605 | 0 | } Unexecuted instantiation: void fast_float::round_nearest_tie_even<fast_float::positive_digit_comp<double>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1}>(fast_float::adjusted_mantissa&, int, fast_float::positive_digit_comp<double>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1})Unexecuted instantiation: void fast_float::round_nearest_tie_even<fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1}>(fast_float::adjusted_mantissa&, int, fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1}) |
4606 | | |
4607 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void |
4608 | 0 | round_down(adjusted_mantissa &am, int32_t shift) noexcept { |
4609 | 0 | if (shift == 64) { |
4610 | 0 | am.mantissa = 0; |
4611 | 0 | } else { |
4612 | 0 | am.mantissa >>= shift; |
4613 | 0 | } |
4614 | 0 | am.power2 += shift; |
4615 | 0 | } |
4616 | | |
4617 | | template <typename UC> |
4618 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void |
4619 | 0 | skip_zeros(UC const *&first, UC const *last) noexcept { |
4620 | 0 | uint64_t val; |
4621 | 0 | while (!cpp20_and_in_constexpr() && |
4622 | 0 | tinyobj_ff::distance(first, last) >= int_cmp_len<UC>()) { |
4623 | 0 | ::memcpy(&val, first, sizeof(uint64_t)); |
4624 | 0 | if (val != int_cmp_zeros<UC>()) { |
4625 | 0 | break; |
4626 | 0 | } |
4627 | 0 | first += int_cmp_len<UC>(); |
4628 | 0 | } |
4629 | 0 | while (first != last) { |
4630 | 0 | if (*first != UC('0')) { |
4631 | 0 | break; |
4632 | 0 | } |
4633 | 0 | first++; |
4634 | 0 | } |
4635 | 0 | } |
4636 | | |
4637 | | // determine if any non-zero digits were truncated. |
4638 | | // all characters must be valid digits. |
4639 | | template <typename UC> |
4640 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool |
4641 | 0 | is_truncated(UC const *first, UC const *last) noexcept { |
4642 | | // do 8-bit optimizations, can just compare to 8 literal 0s. |
4643 | 0 | uint64_t val; |
4644 | 0 | while (!cpp20_and_in_constexpr() && |
4645 | 0 | tinyobj_ff::distance(first, last) >= int_cmp_len<UC>()) { |
4646 | 0 | ::memcpy(&val, first, sizeof(uint64_t)); |
4647 | 0 | if (val != int_cmp_zeros<UC>()) { |
4648 | 0 | return true; |
4649 | 0 | } |
4650 | 0 | first += int_cmp_len<UC>(); |
4651 | 0 | } |
4652 | 0 | while (first != last) { |
4653 | 0 | if (*first != UC('0')) { |
4654 | 0 | return true; |
4655 | 0 | } |
4656 | 0 | ++first; |
4657 | 0 | } |
4658 | 0 | return false; |
4659 | 0 | } |
4660 | | |
4661 | | template <typename UC> |
4662 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool |
4663 | 0 | is_truncated(span<UC const> s) noexcept { |
4664 | 0 | return is_truncated(s.ptr, s.ptr + s.len()); |
4665 | 0 | } |
4666 | | |
4667 | | template <typename UC> |
4668 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void |
4669 | | parse_eight_digits(UC const *&p, limb &value, size_t &counter, |
4670 | 0 | size_t &count) noexcept { |
4671 | 0 | value = value * 100000000 + parse_eight_digits_unrolled(p); |
4672 | 0 | p += 8; |
4673 | 0 | counter += 8; |
4674 | 0 | count += 8; |
4675 | 0 | } |
4676 | | |
4677 | | template <typename UC> |
4678 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void |
4679 | | parse_one_digit(UC const *&p, limb &value, size_t &counter, |
4680 | 0 | size_t &count) noexcept { |
4681 | 0 | value = value * 10 + limb(*p - UC('0')); |
4682 | 0 | p++; |
4683 | 0 | counter++; |
4684 | 0 | count++; |
4685 | 0 | } |
4686 | | |
4687 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void |
4688 | 0 | add_native(bigint &big, limb power, limb value) noexcept { |
4689 | 0 | big.mul(power); |
4690 | 0 | big.add(value); |
4691 | 0 | } |
4692 | | |
4693 | | fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void |
4694 | 0 | round_up_bigint(bigint &big, size_t &count) noexcept { |
4695 | | // need to round-up the digits, but need to avoid rounding |
4696 | | // ....9999 to ...10000, which could cause a false halfway point. |
4697 | 0 | add_native(big, 10, 1); |
4698 | 0 | count++; |
4699 | 0 | } |
4700 | | |
4701 | | // parse the significant digits into a big integer |
4702 | | template <typename UC> |
4703 | | inline FASTFLOAT_CONSTEXPR20 void |
4704 | | parse_mantissa(bigint &result, parsed_number_string_t<UC> &num, |
4705 | 0 | size_t max_digits, size_t &digits) noexcept { |
4706 | | // try to minimize the number of big integer and scalar multiplication. |
4707 | | // therefore, try to parse 8 digits at a time, and multiply by the largest |
4708 | | // scalar value (9 or 19 digits) for each step. |
4709 | 0 | size_t counter = 0; |
4710 | 0 | digits = 0; |
4711 | 0 | limb value = 0; |
4712 | 0 | #ifdef FASTFLOAT_64BIT_LIMB |
4713 | 0 | size_t step = 19; |
4714 | | #else |
4715 | | size_t step = 9; |
4716 | | #endif |
4717 | | |
4718 | | // process all integer digits. |
4719 | 0 | UC const *p = num.integer.ptr; |
4720 | 0 | UC const *pend = p + num.integer.len(); |
4721 | 0 | skip_zeros(p, pend); |
4722 | | // process all digits, in increments of step per loop |
4723 | 0 | while (p != pend) { |
4724 | 0 | while ((tinyobj_ff::distance(p, pend) >= 8) && (step - counter >= 8) && |
4725 | 0 | (max_digits - digits >= 8)) { |
4726 | 0 | parse_eight_digits(p, value, counter, digits); |
4727 | 0 | } |
4728 | 0 | while (counter < step && p != pend && digits < max_digits) { |
4729 | 0 | parse_one_digit(p, value, counter, digits); |
4730 | 0 | } |
4731 | 0 | if (digits == max_digits) { |
4732 | | // add the temporary value, then check if we've truncated any digits |
4733 | 0 | add_native(result, limb(powers_of_ten_uint64[counter]), value); |
4734 | 0 | bool truncated = is_truncated(p, pend); |
4735 | 0 | if (num.fraction.ptr != nullptr) { |
4736 | 0 | truncated |= is_truncated(num.fraction); |
4737 | 0 | } |
4738 | 0 | if (truncated) { |
4739 | 0 | round_up_bigint(result, digits); |
4740 | 0 | } |
4741 | 0 | return; |
4742 | 0 | } else { |
4743 | 0 | add_native(result, limb(powers_of_ten_uint64[counter]), value); |
4744 | 0 | counter = 0; |
4745 | 0 | value = 0; |
4746 | 0 | } |
4747 | 0 | } |
4748 | | |
4749 | | // add our fraction digits, if they're available. |
4750 | 0 | if (num.fraction.ptr != nullptr) { |
4751 | 0 | p = num.fraction.ptr; |
4752 | 0 | pend = p + num.fraction.len(); |
4753 | 0 | if (digits == 0) { |
4754 | 0 | skip_zeros(p, pend); |
4755 | 0 | } |
4756 | | // process all digits, in increments of step per loop |
4757 | 0 | while (p != pend) { |
4758 | 0 | while ((tinyobj_ff::distance(p, pend) >= 8) && (step - counter >= 8) && |
4759 | 0 | (max_digits - digits >= 8)) { |
4760 | 0 | parse_eight_digits(p, value, counter, digits); |
4761 | 0 | } |
4762 | 0 | while (counter < step && p != pend && digits < max_digits) { |
4763 | 0 | parse_one_digit(p, value, counter, digits); |
4764 | 0 | } |
4765 | 0 | if (digits == max_digits) { |
4766 | | // add the temporary value, then check if we've truncated any digits |
4767 | 0 | add_native(result, limb(powers_of_ten_uint64[counter]), value); |
4768 | 0 | bool truncated = is_truncated(p, pend); |
4769 | 0 | if (truncated) { |
4770 | 0 | round_up_bigint(result, digits); |
4771 | 0 | } |
4772 | 0 | return; |
4773 | 0 | } else { |
4774 | 0 | add_native(result, limb(powers_of_ten_uint64[counter]), value); |
4775 | 0 | counter = 0; |
4776 | 0 | value = 0; |
4777 | 0 | } |
4778 | 0 | } |
4779 | 0 | } |
4780 | | |
4781 | 0 | if (counter != 0) { |
4782 | 0 | add_native(result, limb(powers_of_ten_uint64[counter]), value); |
4783 | 0 | } |
4784 | 0 | } |
4785 | | |
4786 | | template <typename T> |
4787 | | inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa |
4788 | 0 | positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept { |
4789 | 0 | FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent))); |
4790 | 0 | adjusted_mantissa answer; |
4791 | 0 | bool truncated; |
4792 | 0 | answer.mantissa = bigmant.hi64(truncated); |
4793 | 0 | int bias = binary_format<T>::mantissa_explicit_bits() - |
4794 | 0 | binary_format<T>::minimum_exponent(); |
4795 | 0 | answer.power2 = bigmant.bit_length() - 64 + bias; |
4796 | |
|
4797 | 0 | round<T>(answer, [truncated](adjusted_mantissa &a, int32_t shift) { |
4798 | 0 | round_nearest_tie_even( |
4799 | 0 | a, shift, |
4800 | 0 | [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool { |
4801 | 0 | return is_above || (is_halfway && truncated) || |
4802 | 0 | (is_odd && is_halfway); |
4803 | 0 | }); |
4804 | 0 | }); |
4805 | |
|
4806 | 0 | return answer; |
4807 | 0 | } |
4808 | | |
4809 | | // the scaling here is quite simple: we have, for the real digits `m * 10^e`, |
4810 | | // and for the theoretical digits `n * 2^f`. Since `e` is always negative, |
4811 | | // to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`. |
4812 | | // we then need to scale by `2^(f- e)`, and then the two significant digits |
4813 | | // are of the same magnitude. |
4814 | | template <typename T> |
4815 | | inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp( |
4816 | 0 | bigint &bigmant, adjusted_mantissa am, int32_t exponent) noexcept { |
4817 | 0 | bigint &real_digits = bigmant; |
4818 | 0 | int32_t real_exp = exponent; |
4819 | | |
4820 | | // get the value of `b`, rounded down, and get a bigint representation of b+h |
4821 | 0 | adjusted_mantissa am_b = am; |
4822 | | // gcc7 buf: use a lambda to remove the noexcept qualifier bug with |
4823 | | // -Wnoexcept-type. |
4824 | 0 | round<T>(am_b, |
4825 | 0 | [](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); }); |
4826 | 0 | T b; |
4827 | 0 | to_float(false, am_b, b); |
4828 | 0 | adjusted_mantissa theor = to_extended_halfway(b); |
4829 | 0 | bigint theor_digits(theor.mantissa); |
4830 | 0 | int32_t theor_exp = theor.power2; |
4831 | | |
4832 | | // scale real digits and theor digits to be same power. |
4833 | 0 | int32_t pow2_exp = theor_exp - real_exp; |
4834 | 0 | uint32_t pow5_exp = uint32_t(-real_exp); |
4835 | 0 | if (pow5_exp != 0) { |
4836 | 0 | FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp)); |
4837 | 0 | } |
4838 | 0 | if (pow2_exp > 0) { |
4839 | 0 | FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp))); |
4840 | 0 | } else if (pow2_exp < 0) { |
4841 | 0 | FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp))); |
4842 | 0 | } |
4843 | | |
4844 | | // compare digits, and use it to director rounding |
4845 | 0 | int ord = real_digits.compare(theor_digits); |
4846 | 0 | adjusted_mantissa answer = am; |
4847 | 0 | round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) { |
4848 | 0 | round_nearest_tie_even( |
4849 | 0 | a, shift, [ord](bool is_odd, bool _, bool __) -> bool { |
4850 | 0 | (void)_; // not needed, since we've done our comparison |
4851 | 0 | (void)__; // not needed, since we've done our comparison |
4852 | 0 | if (ord > 0) { |
4853 | 0 | return true; |
4854 | 0 | } else if (ord < 0) { |
4855 | 0 | return false; |
4856 | 0 | } else { |
4857 | 0 | return is_odd; |
4858 | 0 | } |
4859 | 0 | }); |
4860 | 0 | }); |
4861 | |
|
4862 | 0 | return answer; |
4863 | 0 | } |
4864 | | |
4865 | | // parse the significant digits as a big integer to unambiguously round the |
4866 | | // the significant digits. here, we are trying to determine how to round |
4867 | | // an extended float representation close to `b+h`, halfway between `b` |
4868 | | // (the float rounded-down) and `b+u`, the next positive float. this |
4869 | | // algorithm is always correct, and uses one of two approaches. when |
4870 | | // the exponent is positive relative to the significant digits (such as |
4871 | | // 1234), we create a big-integer representation, get the high 64-bits, |
4872 | | // determine if any lower bits are truncated, and use that to direct |
4873 | | // rounding. in case of a negative exponent relative to the significant |
4874 | | // digits (such as 1.2345), we create a theoretical representation of |
4875 | | // `b` as a big-integer type, scaled to the same binary exponent as |
4876 | | // the actual digits. we then compare the big integer representations |
4877 | | // of both, and use that to direct rounding. |
4878 | | template <typename T, typename UC> |
4879 | | inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa |
4880 | 0 | digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept { |
4881 | | // remove the invalid exponent bias |
4882 | 0 | am.power2 -= invalid_am_bias; |
4883 | |
|
4884 | 0 | int32_t sci_exp = scientific_exponent(num); |
4885 | 0 | size_t max_digits = binary_format<T>::max_digits(); |
4886 | 0 | size_t digits = 0; |
4887 | 0 | bigint bigmant; |
4888 | 0 | parse_mantissa(bigmant, num, max_digits, digits); |
4889 | | // can't underflow, since digits is at most max_digits. |
4890 | 0 | int32_t exponent = sci_exp + 1 - int32_t(digits); |
4891 | 0 | if (exponent >= 0) { |
4892 | 0 | return positive_digit_comp<T>(bigmant, exponent); |
4893 | 0 | } else { |
4894 | 0 | return negative_digit_comp<T>(bigmant, am, exponent); |
4895 | 0 | } |
4896 | 0 | } |
4897 | | |
4898 | | } // namespace fast_float |
4899 | | |
4900 | | #endif |
4901 | | |
4902 | | #ifndef FASTFLOAT_PARSE_NUMBER_H |
4903 | | #define FASTFLOAT_PARSE_NUMBER_H |
4904 | | |
4905 | | |
4906 | | #include <cmath> |
4907 | | #include <cstring> |
4908 | | #include <limits> |
4909 | | |
4910 | | namespace fast_float { |
4911 | | |
4912 | | namespace detail { |
4913 | | /** |
4914 | | * Special case +inf, -inf, nan, infinity, -infinity. |
4915 | | * The case comparisons could be made much faster given that we know that the |
4916 | | * strings a null-free and fixed. |
4917 | | **/ |
4918 | | template <typename T, typename UC> |
4919 | | from_chars_result_t<UC> |
4920 | | FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last, |
4921 | 2.29k | T &value, chars_format fmt) noexcept { |
4922 | 2.29k | from_chars_result_t<UC> answer{}; |
4923 | 2.29k | answer.ptr = first; |
4924 | 2.29k | answer.ec = tinyobj_ff::ff_errc(); // be optimistic |
4925 | | // assume first < last, so dereference without checks; |
4926 | 2.29k | bool const minusSign = (*first == UC('-')); |
4927 | | // C++17 20.19.3.(7.1) explicitly forbids '+' sign here |
4928 | 2.29k | if ((*first == UC('-')) || |
4929 | 1.86k | (uint64_t(fmt & chars_format::allow_leading_plus) && |
4930 | 1.86k | (*first == UC('+')))) { |
4931 | 459 | ++first; |
4932 | 459 | } |
4933 | 2.29k | if (last - first >= 3) { |
4934 | 311 | if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) { |
4935 | 0 | answer.ptr = (first += 3); |
4936 | 0 | value = minusSign ? -std::numeric_limits<T>::quiet_NaN() |
4937 | 0 | : std::numeric_limits<T>::quiet_NaN(); |
4938 | | // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, |
4939 | | // C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan). |
4940 | 0 | if (first != last && *first == UC('(')) { |
4941 | 0 | for (UC const *ptr = first + 1; ptr != last; ++ptr) { |
4942 | 0 | if (*ptr == UC(')')) { |
4943 | 0 | answer.ptr = ptr + 1; // valid nan(n-char-seq-opt) |
4944 | 0 | break; |
4945 | 0 | } else if (!((UC('a') <= *ptr && *ptr <= UC('z')) || |
4946 | 0 | (UC('A') <= *ptr && *ptr <= UC('Z')) || |
4947 | 0 | (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_'))) |
4948 | 0 | break; // forbidden char, not nan(n-char-seq-opt) |
4949 | 0 | } |
4950 | 0 | } |
4951 | 0 | return answer; |
4952 | 0 | } |
4953 | 311 | if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) { |
4954 | 0 | if ((last - first >= 8) && |
4955 | 0 | fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) { |
4956 | 0 | answer.ptr = first + 8; |
4957 | 0 | } else { |
4958 | 0 | answer.ptr = first + 3; |
4959 | 0 | } |
4960 | 0 | value = minusSign ? -std::numeric_limits<T>::infinity() |
4961 | 0 | : std::numeric_limits<T>::infinity(); |
4962 | 0 | return answer; |
4963 | 0 | } |
4964 | 311 | } |
4965 | 2.29k | answer.ec = tinyobj_ff::ff_errc::invalid_argument; |
4966 | 2.29k | return answer; |
4967 | 2.29k | } |
4968 | | |
4969 | | /** |
4970 | | * Returns true if the floating-pointing rounding mode is to 'nearest'. |
4971 | | * It is the default on most system. This function is meant to be inexpensive. |
4972 | | * Credit : @mwalcott3 |
4973 | | */ |
4974 | 2.79k | fastfloat_really_inline bool rounds_to_nearest() noexcept { |
4975 | | // https://lemire.me/blog/2020/06/26/gcc-not-nearest/ |
4976 | | #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) |
4977 | | return false; |
4978 | | #endif |
4979 | | // See |
4980 | | // A fast function to check your floating-point rounding mode |
4981 | | // https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/ |
4982 | | // |
4983 | | // This function is meant to be equivalent to : |
4984 | | // prior: #include <cfenv> |
4985 | | // return fegetround() == FE_TONEAREST; |
4986 | | // However, it is expected to be much faster than the fegetround() |
4987 | | // function call. |
4988 | | // |
4989 | | // The volatile keyword prevents the compiler from computing the function |
4990 | | // at compile-time. |
4991 | | // There might be other ways to prevent compile-time optimizations (e.g., |
4992 | | // asm). The value does not need to be std::numeric_limits<float>::min(), any |
4993 | | // small value so that 1 + x should round to 1 would do (after accounting for |
4994 | | // excess precision, as in 387 instructions). |
4995 | 2.79k | static float volatile fmin = std::numeric_limits<float>::min(); |
4996 | 2.79k | float fmini = fmin; // we copy it so that it gets loaded at most once. |
4997 | | // |
4998 | | // Explanation: |
4999 | | // Only when fegetround() == FE_TONEAREST do we have that |
5000 | | // fmin + 1.0f == 1.0f - fmin. |
5001 | | // |
5002 | | // FE_UPWARD: |
5003 | | // fmin + 1.0f > 1 |
5004 | | // 1.0f - fmin == 1 |
5005 | | // |
5006 | | // FE_DOWNWARD or FE_TOWARDZERO: |
5007 | | // fmin + 1.0f == 1 |
5008 | | // 1.0f - fmin < 1 |
5009 | | // |
5010 | | // Note: This may fail to be accurate if fast-math has been |
5011 | | // enabled, as rounding conventions may not apply. |
5012 | | #ifdef FASTFLOAT_VISUAL_STUDIO |
5013 | | #pragma warning(push) |
5014 | | // todo: is there a VS warning? |
5015 | | // see |
5016 | | // https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013 |
5017 | | #elif defined(__clang__) |
5018 | | #pragma clang diagnostic push |
5019 | 2.79k | #pragma clang diagnostic ignored "-Wfloat-equal" |
5020 | | #elif defined(__GNUC__) |
5021 | | #pragma GCC diagnostic push |
5022 | | #pragma GCC diagnostic ignored "-Wfloat-equal" |
5023 | | #endif |
5024 | 2.79k | return (fmini + 1.0f == 1.0f - fmini); |
5025 | | #ifdef FASTFLOAT_VISUAL_STUDIO |
5026 | | #pragma warning(pop) |
5027 | | #elif defined(__clang__) |
5028 | | #pragma clang diagnostic pop |
5029 | | #elif defined(__GNUC__) |
5030 | | #pragma GCC diagnostic pop |
5031 | | #endif |
5032 | 2.79k | } |
5033 | | |
5034 | | } // namespace detail |
5035 | | |
5036 | | template <typename T> struct from_chars_caller { |
5037 | | template <typename UC> |
5038 | | FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> |
5039 | | call(UC const *first, UC const *last, T &value, |
5040 | 5.14k | parse_options_t<UC> options) noexcept { |
5041 | 5.14k | return from_chars_advanced(first, last, value, options); |
5042 | 5.14k | } |
5043 | | }; |
5044 | | |
5045 | | #ifdef __STDCPP_FLOAT32_T__ |
5046 | | template <> struct from_chars_caller<std::float32_t> { |
5047 | | template <typename UC> |
5048 | | FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> |
5049 | | call(UC const *first, UC const *last, std::float32_t &value, |
5050 | | parse_options_t<UC> options) noexcept { |
5051 | | // if std::float32_t is defined, and we are in C++23 mode; macro set for |
5052 | | // float32; set value to float due to equivalence between float and |
5053 | | // float32_t |
5054 | | float val; |
5055 | | auto ret = from_chars_advanced(first, last, val, options); |
5056 | | value = val; |
5057 | | return ret; |
5058 | | } |
5059 | | }; |
5060 | | #endif |
5061 | | |
5062 | | #ifdef __STDCPP_FLOAT64_T__ |
5063 | | template <> struct from_chars_caller<std::float64_t> { |
5064 | | template <typename UC> |
5065 | | FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> |
5066 | | call(UC const *first, UC const *last, std::float64_t &value, |
5067 | | parse_options_t<UC> options) noexcept { |
5068 | | // if std::float64_t is defined, and we are in C++23 mode; macro set for |
5069 | | // float64; set value as double due to equivalence between double and |
5070 | | // float64_t |
5071 | | double val; |
5072 | | auto ret = from_chars_advanced(first, last, val, options); |
5073 | | value = val; |
5074 | | return ret; |
5075 | | } |
5076 | | }; |
5077 | | #endif |
5078 | | |
5079 | | template <typename T, typename UC, typename> |
5080 | | FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> |
5081 | | from_chars(UC const *first, UC const *last, T &value, |
5082 | 5.14k | chars_format fmt /*= chars_format::general*/) noexcept { |
5083 | 5.14k | return from_chars_caller<T>::call(first, last, value, |
5084 | 5.14k | parse_options_t<UC>(fmt)); |
5085 | 5.14k | } |
5086 | | |
5087 | | /** |
5088 | | * This function overload takes parsed_number_string_t structure that is created |
5089 | | * and populated either by from_chars_advanced function taking chars range and |
5090 | | * parsing options or other parsing custom function implemented by user. |
5091 | | */ |
5092 | | template <typename T, typename UC> |
5093 | | FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> |
5094 | 2.84k | from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept { |
5095 | | |
5096 | 2.84k | static_assert(is_supported_float_type<T>::value, |
5097 | 2.84k | "only some floating-point types are supported"); |
5098 | 2.84k | static_assert(is_supported_char_type<UC>::value, |
5099 | 2.84k | "only char, wchar_t, char16_t and char32_t are supported"); |
5100 | | |
5101 | 2.84k | from_chars_result_t<UC> answer; |
5102 | | |
5103 | 2.84k | answer.ec = tinyobj_ff::ff_errc(); // be optimistic |
5104 | 2.84k | answer.ptr = pns.lastmatch; |
5105 | | // The implementation of the Clinger's fast path is convoluted because |
5106 | | // we want round-to-nearest in all cases, irrespective of the rounding mode |
5107 | | // selected on the thread. |
5108 | | // We proceed optimistically, assuming that detail::rounds_to_nearest() |
5109 | | // returns true. |
5110 | 2.84k | if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && |
5111 | 2.82k | pns.exponent <= binary_format<T>::max_exponent_fast_path() && |
5112 | 2.80k | !pns.too_many_digits) { |
5113 | | // Unfortunately, the conventional Clinger's fast path is only possible |
5114 | | // when the system rounds to the nearest float. |
5115 | | // |
5116 | | // We expect the next branch to almost always be selected. |
5117 | | // We could check it first (before the previous branch), but |
5118 | | // there might be performance advantages at having the check |
5119 | | // be last. |
5120 | 2.79k | if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) { |
5121 | | // We have that fegetround() == FE_TONEAREST. |
5122 | | // Next is Clinger's fast path. |
5123 | 2.79k | if (pns.mantissa <= binary_format<T>::max_mantissa_fast_path()) { |
5124 | 2.79k | value = T(pns.mantissa); |
5125 | 2.79k | if (pns.exponent < 0) { |
5126 | 85 | value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); |
5127 | 2.71k | } else { |
5128 | 2.71k | value = value * binary_format<T>::exact_power_of_ten(pns.exponent); |
5129 | 2.71k | } |
5130 | 2.79k | if (pns.negative) { |
5131 | 8 | value = -value; |
5132 | 8 | } |
5133 | 2.79k | return answer; |
5134 | 2.79k | } |
5135 | 2.79k | } else { |
5136 | | // We do not have that fegetround() == FE_TONEAREST. |
5137 | | // Next is a modified Clinger's fast path, inspired by Jakub JelÃnek's |
5138 | | // proposal |
5139 | 0 | if (pns.exponent >= 0 && |
5140 | 0 | pns.mantissa <= |
5141 | 0 | binary_format<T>::max_mantissa_fast_path(pns.exponent)) { |
5142 | 0 | #if defined(__clang__) || defined(FASTFLOAT_32BIT) |
5143 | | // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD |
5144 | 0 | if (pns.mantissa == 0) { |
5145 | 0 | value = pns.negative ? T(-0.) : T(0.); |
5146 | 0 | return answer; |
5147 | 0 | } |
5148 | 0 | #endif |
5149 | 0 | value = T(pns.mantissa) * |
5150 | 0 | binary_format<T>::exact_power_of_ten(pns.exponent); |
5151 | 0 | if (pns.negative) { |
5152 | 0 | value = -value; |
5153 | 0 | } |
5154 | 0 | return answer; |
5155 | 0 | } |
5156 | 0 | } |
5157 | 2.79k | } |
5158 | 50 | adjusted_mantissa am = |
5159 | 50 | compute_float<binary_format<T>>(pns.exponent, pns.mantissa); |
5160 | 50 | if (pns.too_many_digits && am.power2 >= 0) { |
5161 | 10 | if (am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) { |
5162 | 0 | am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa); |
5163 | 0 | } |
5164 | 10 | } |
5165 | | // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) |
5166 | | // and we have an invalid power (am.power2 < 0), then we need to go the long |
5167 | | // way around again. This is very uncommon. |
5168 | 50 | if (am.power2 < 0) { |
5169 | 0 | am = digit_comp<T>(pns, am); |
5170 | 0 | } |
5171 | 50 | to_float(pns.negative, am, value); |
5172 | | // Test for over/underflow. |
5173 | 50 | if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || |
5174 | 31 | am.power2 == binary_format<T>::infinite_power()) { |
5175 | 30 | answer.ec = tinyobj_ff::ff_errc::result_out_of_range; |
5176 | 30 | } |
5177 | 50 | return answer; |
5178 | 2.84k | } |
5179 | | |
5180 | | template <typename T, typename UC> |
5181 | | FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> |
5182 | | from_chars_float_advanced(UC const *first, UC const *last, T &value, |
5183 | 5.14k | parse_options_t<UC> options) noexcept { |
5184 | | |
5185 | 5.14k | static_assert(is_supported_float_type<T>::value, |
5186 | 5.14k | "only some floating-point types are supported"); |
5187 | 5.14k | static_assert(is_supported_char_type<UC>::value, |
5188 | 5.14k | "only char, wchar_t, char16_t and char32_t are supported"); |
5189 | | |
5190 | 5.14k | chars_format const fmt = detail::adjust_for_feature_macros(options.format); |
5191 | | |
5192 | 5.14k | from_chars_result_t<UC> answer; |
5193 | 5.14k | if (uint64_t(fmt & chars_format::skip_white_space)) { |
5194 | 0 | while ((first != last) && fast_float::is_space(*first)) { |
5195 | 0 | first++; |
5196 | 0 | } |
5197 | 0 | } |
5198 | 5.14k | if (first == last) { |
5199 | 0 | answer.ec = tinyobj_ff::ff_errc::invalid_argument; |
5200 | 0 | answer.ptr = first; |
5201 | 0 | return answer; |
5202 | 0 | } |
5203 | 5.14k | parsed_number_string_t<UC> pns = |
5204 | 5.14k | uint64_t(fmt & detail::basic_json_fmt) |
5205 | 5.14k | ? parse_number_string<true, UC>(first, last, options) |
5206 | 5.14k | : parse_number_string<false, UC>(first, last, options); |
5207 | 5.14k | if (!pns.valid) { |
5208 | 2.29k | if (uint64_t(fmt & chars_format::no_infnan)) { |
5209 | 0 | answer.ec = tinyobj_ff::ff_errc::invalid_argument; |
5210 | 0 | answer.ptr = first; |
5211 | 0 | return answer; |
5212 | 2.29k | } else { |
5213 | 2.29k | return detail::parse_infnan(first, last, value, fmt); |
5214 | 2.29k | } |
5215 | 2.29k | } |
5216 | | |
5217 | | // call overload that takes parsed_number_string_t directly. |
5218 | 2.84k | return from_chars_advanced(pns, value); |
5219 | 5.14k | } |
5220 | | |
5221 | | template <typename T, typename UC, typename> |
5222 | | FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> |
5223 | | from_chars(UC const *first, UC const *last, T &value, int base) noexcept { |
5224 | | |
5225 | | static_assert(is_supported_integer_type<T>::value, |
5226 | | "only integer types are supported"); |
5227 | | static_assert(is_supported_char_type<UC>::value, |
5228 | | "only char, wchar_t, char16_t and char32_t are supported"); |
5229 | | |
5230 | | parse_options_t<UC> options; |
5231 | | options.base = base; |
5232 | | return from_chars_advanced(first, last, value, options); |
5233 | | } |
5234 | | |
5235 | | template <typename T, typename UC> |
5236 | | FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> |
5237 | | from_chars_int_advanced(UC const *first, UC const *last, T &value, |
5238 | | parse_options_t<UC> options) noexcept { |
5239 | | |
5240 | | static_assert(is_supported_integer_type<T>::value, |
5241 | | "only integer types are supported"); |
5242 | | static_assert(is_supported_char_type<UC>::value, |
5243 | | "only char, wchar_t, char16_t and char32_t are supported"); |
5244 | | |
5245 | | chars_format const fmt = detail::adjust_for_feature_macros(options.format); |
5246 | | int const base = options.base; |
5247 | | |
5248 | | from_chars_result_t<UC> answer; |
5249 | | if (uint64_t(fmt & chars_format::skip_white_space)) { |
5250 | | while ((first != last) && fast_float::is_space(*first)) { |
5251 | | first++; |
5252 | | } |
5253 | | } |
5254 | | if (first == last || base < 2 || base > 36) { |
5255 | | answer.ec = tinyobj_ff::ff_errc::invalid_argument; |
5256 | | answer.ptr = first; |
5257 | | return answer; |
5258 | | } |
5259 | | |
5260 | | return parse_int_string(first, last, value, options); |
5261 | | } |
5262 | | |
5263 | | template <size_t TypeIx> struct from_chars_advanced_caller { |
5264 | | static_assert(TypeIx > 0, "unsupported type"); |
5265 | | }; |
5266 | | |
5267 | | template <> struct from_chars_advanced_caller<1> { |
5268 | | template <typename T, typename UC> |
5269 | | FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> |
5270 | | call(UC const *first, UC const *last, T &value, |
5271 | 5.14k | parse_options_t<UC> options) noexcept { |
5272 | 5.14k | return from_chars_float_advanced(first, last, value, options); |
5273 | 5.14k | } |
5274 | | }; |
5275 | | |
5276 | | template <> struct from_chars_advanced_caller<2> { |
5277 | | template <typename T, typename UC> |
5278 | | FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> |
5279 | | call(UC const *first, UC const *last, T &value, |
5280 | | parse_options_t<UC> options) noexcept { |
5281 | | return from_chars_int_advanced(first, last, value, options); |
5282 | | } |
5283 | | }; |
5284 | | |
5285 | | template <typename T, typename UC> |
5286 | | FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> |
5287 | | from_chars_advanced(UC const *first, UC const *last, T &value, |
5288 | 5.14k | parse_options_t<UC> options) noexcept { |
5289 | 5.14k | return from_chars_advanced_caller< |
5290 | 5.14k | size_t(is_supported_float_type<T>::value) + |
5291 | 5.14k | 2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value, |
5292 | 5.14k | options); |
5293 | 5.14k | } |
5294 | | |
5295 | | } // namespace fast_float |
5296 | | |
5297 | | #endif |
5298 | | |
5299 | | |
5300 | | // --- End embedded fast_float --- |
5301 | | |
5302 | | // Clean up fast_float macros to avoid polluting the user's namespace. |
5303 | | #undef FASTFLOAT_32BIT |
5304 | | #undef FASTFLOAT_32BIT_LIMB |
5305 | | #undef FASTFLOAT_64BIT |
5306 | | #undef FASTFLOAT_64BIT_LIMB |
5307 | | #undef FASTFLOAT_ASCII_NUMBER_H |
5308 | | #undef FASTFLOAT_ASSERT |
5309 | | #undef FASTFLOAT_BIGINT_H |
5310 | | #undef FASTFLOAT_CONSTEXPR14 |
5311 | | #undef FASTFLOAT_CONSTEXPR20 |
5312 | | #undef FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H |
5313 | | #undef FASTFLOAT_DEBUG_ASSERT |
5314 | | #undef FASTFLOAT_DECIMAL_TO_BINARY_H |
5315 | | #undef FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE |
5316 | | #undef FASTFLOAT_DIGIT_COMPARISON_H |
5317 | | #undef FASTFLOAT_ENABLE_IF |
5318 | | #undef FASTFLOAT_FAST_FLOAT_H |
5319 | | #undef FASTFLOAT_FAST_TABLE_H |
5320 | | #undef FASTFLOAT_FLOAT_COMMON_H |
5321 | | #undef FASTFLOAT_HAS_BIT_CAST |
5322 | | #undef FASTFLOAT_HAS_IS_CONSTANT_EVALUATED |
5323 | | #undef FASTFLOAT_HAS_SIMD |
5324 | | #undef FASTFLOAT_IF_CONSTEXPR17 |
5325 | | #undef FASTFLOAT_IS_BIG_ENDIAN |
5326 | | #undef FASTFLOAT_IS_CONSTEXPR |
5327 | | #undef FASTFLOAT_NEON |
5328 | | #undef FASTFLOAT_PARSE_NUMBER_H |
5329 | | #undef fastfloat_really_inline |
5330 | | #undef FASTFLOAT_SIMD_DISABLE_WARNINGS |
5331 | | #undef FASTFLOAT_SIMD_RESTORE_WARNINGS |
5332 | | #undef FASTFLOAT_SSE2 |
5333 | | #undef FASTFLOAT_STRINGIZE |
5334 | | #undef FASTFLOAT_STRINGIZE_IMPL |
5335 | | #undef FASTFLOAT_TRY |
5336 | | #undef FASTFLOAT_VERSION |
5337 | | #undef FASTFLOAT_VERSION_MAJOR |
5338 | | #undef FASTFLOAT_VERSION_MINOR |
5339 | | #undef FASTFLOAT_VERSION_PATCH |
5340 | | #undef FASTFLOAT_VERSION_STR |
5341 | | #undef FASTFLOAT_VISUAL_STUDIO |
5342 | | |
5343 | | #endif // TINYOBJLOADER_DISABLE_FAST_FLOAT |
5344 | | |
5345 | | namespace tinyobj { |
5346 | | |
5347 | 8.54k | MaterialReader::~MaterialReader() {} |
5348 | | |
5349 | | // Byte-stream reader for bounds-checked text parsing. |
5350 | | // Replaces raw `const char*` token pointers with `(buf, len, idx)` triple. |
5351 | | // Every byte access is guarded by an EOF check. |
5352 | | class StreamReader { |
5353 | | public: |
5354 | | // Maximum number of bytes StreamReader will buffer from std::istream. |
5355 | | // Define this macro to a larger value if your application needs to parse |
5356 | | // very large streamed OBJ/MTL content. |
5357 | | #ifndef TINYOBJLOADER_STREAM_READER_MAX_BYTES |
5358 | 10.1k | #define TINYOBJLOADER_STREAM_READER_MAX_BYTES (size_t(256) * size_t(1024) * size_t(1024)) |
5359 | | #endif |
5360 | | |
5361 | | StreamReader(const char *buf, size_t length) |
5362 | 0 | : buf_(buf), length_(length), idx_(0), line_num_(1), col_num_(1) {} |
5363 | | |
5364 | | // Non-copyable, non-movable: buf_ may point into owned_buf_. |
5365 | | StreamReader(const StreamReader &) /* = delete */; |
5366 | | StreamReader &operator=(const StreamReader &) /* = delete */; |
5367 | | |
5368 | | // Build from std::istream by reading all content into an internal buffer. |
5369 | 10.1k | explicit StreamReader(std::istream &is) : buf_(NULL), length_(0), idx_(0), line_num_(1), col_num_(1) { |
5370 | 10.1k | const size_t max_stream_bytes = TINYOBJLOADER_STREAM_READER_MAX_BYTES; |
5371 | 10.1k | std::streampos start_pos = is.tellg(); |
5372 | 10.1k | bool can_seek = (start_pos != std::streampos(-1)); |
5373 | 10.1k | if (can_seek) { |
5374 | 10.1k | is.seekg(0, std::ios::end); |
5375 | 10.1k | std::streampos end_pos = is.tellg(); |
5376 | 10.1k | if (end_pos >= start_pos) { |
5377 | 10.1k | std::streamoff remaining_off = static_cast<std::streamoff>(end_pos - start_pos); |
5378 | 10.1k | is.seekg(start_pos); |
5379 | 10.1k | unsigned long long remaining_ull = static_cast<unsigned long long>(remaining_off); |
5380 | 10.1k | if (remaining_ull > static_cast<unsigned long long>((std::numeric_limits<size_t>::max)())) { |
5381 | 0 | std::stringstream ss; |
5382 | 0 | ss << "input stream too large for this platform (" << remaining_ull |
5383 | 0 | << " bytes exceeds size_t max " << (std::numeric_limits<size_t>::max)() << ")\n"; |
5384 | 0 | push_error(ss.str()); |
5385 | 0 | buf_ = ""; |
5386 | 0 | length_ = 0; |
5387 | 0 | return; |
5388 | 0 | } |
5389 | 10.1k | size_t remaining_size = static_cast<size_t>(remaining_ull); |
5390 | 10.1k | if (remaining_size > max_stream_bytes) { |
5391 | 0 | std::stringstream ss; |
5392 | 0 | ss << "input stream too large (" << remaining_size |
5393 | 0 | << " bytes exceeds limit " << max_stream_bytes << " bytes)\n"; |
5394 | 0 | push_error(ss.str()); |
5395 | 0 | buf_ = ""; |
5396 | 0 | length_ = 0; |
5397 | 0 | return; |
5398 | 0 | } |
5399 | 10.1k | owned_buf_.resize(remaining_size); |
5400 | 10.1k | if (remaining_size > 0) { |
5401 | 9.48k | is.read(&owned_buf_[0], static_cast<std::streamsize>(remaining_size)); |
5402 | 9.48k | } |
5403 | 10.1k | size_t actually_read = static_cast<size_t>(is.gcount()); |
5404 | 10.1k | owned_buf_.resize(actually_read); |
5405 | 10.1k | } |
5406 | 10.1k | } |
5407 | 10.1k | if (!can_seek || owned_buf_.empty()) { |
5408 | | // Stream doesn't support seeking, or seek probing failed. |
5409 | 24 | if (can_seek) is.seekg(start_pos); |
5410 | 24 | is.clear(); |
5411 | 24 | std::vector<char> content; |
5412 | 24 | char chunk[4096]; |
5413 | 24 | size_t total_read = 0; |
5414 | 24 | while (is.good()) { |
5415 | 24 | is.read(chunk, static_cast<std::streamsize>(sizeof(chunk))); |
5416 | 24 | std::streamsize nread = is.gcount(); |
5417 | 24 | if (nread <= 0) break; |
5418 | 0 | size_t n = static_cast<size_t>(nread); |
5419 | 0 | if (n > (max_stream_bytes - total_read)) { |
5420 | 0 | std::stringstream ss; |
5421 | 0 | ss << "input stream too large (exceeds limit " << max_stream_bytes |
5422 | 0 | << " bytes)\n"; |
5423 | 0 | push_error(ss.str()); |
5424 | 0 | owned_buf_.clear(); |
5425 | 0 | buf_ = ""; |
5426 | 0 | length_ = 0; |
5427 | 0 | return; |
5428 | 0 | } |
5429 | 0 | content.insert(content.end(), chunk, chunk + n); |
5430 | 0 | total_read += n; |
5431 | 0 | } |
5432 | 24 | owned_buf_.swap(content); |
5433 | 24 | } |
5434 | 10.1k | buf_ = owned_buf_.empty() ? "" : &owned_buf_[0]; |
5435 | 10.1k | length_ = owned_buf_.size(); |
5436 | 10.1k | } |
5437 | | |
5438 | 16.6M | bool eof() const { return idx_ >= length_; } |
5439 | 0 | size_t tell() const { return idx_; } |
5440 | 0 | size_t size() const { return length_; } |
5441 | 1.76M | size_t line_num() const { return line_num_; } |
5442 | 0 | size_t col_num() const { return col_num_; } |
5443 | | |
5444 | 30.7M | char peek() const { |
5445 | 30.7M | if (idx_ >= length_) return '\0'; |
5446 | 30.7M | return buf_[idx_]; |
5447 | 30.7M | } |
5448 | | |
5449 | 0 | char get() { |
5450 | 0 | if (idx_ >= length_) return '\0'; |
5451 | 0 | char c = buf_[idx_++]; |
5452 | 0 | if (c == '\n') { line_num_++; col_num_ = 1; } else { col_num_++; } |
5453 | 0 | return c; |
5454 | 0 | } |
5455 | | |
5456 | 18.9M | void advance(size_t n) { |
5457 | 34.9M | for (size_t i = 0; i < n && idx_ < length_; i++) { |
5458 | 15.9M | if (buf_[idx_] == '\n') { line_num_++; col_num_ = 1; } else { col_num_++; } |
5459 | 15.9M | idx_++; |
5460 | 15.9M | } |
5461 | 18.9M | } |
5462 | | |
5463 | 10.5M | void skip_space() { |
5464 | 10.6M | while (idx_ < length_ && (buf_[idx_] == ' ' || buf_[idx_] == '\t')) { |
5465 | 20.4k | col_num_++; |
5466 | 20.4k | idx_++; |
5467 | 20.4k | } |
5468 | 10.5M | } |
5469 | | |
5470 | 721k | void skip_space_and_cr() { |
5471 | 1.43M | while (idx_ < length_ && (buf_[idx_] == ' ' || buf_[idx_] == '\t' || buf_[idx_] == '\r')) { |
5472 | 716k | col_num_++; |
5473 | 716k | idx_++; |
5474 | 716k | } |
5475 | 721k | } |
5476 | | |
5477 | 1.89M | void skip_line() { |
5478 | 9.67M | while (idx_ < length_) { |
5479 | 9.66M | char c = buf_[idx_]; |
5480 | 9.66M | if (c == '\n') { |
5481 | 1.60M | idx_++; |
5482 | 1.60M | line_num_++; |
5483 | 1.60M | col_num_ = 1; |
5484 | 1.60M | return; |
5485 | 1.60M | } |
5486 | 8.06M | if (c == '\r') { |
5487 | 281k | idx_++; |
5488 | 281k | if (idx_ < length_ && buf_[idx_] == '\n') { |
5489 | 505 | idx_++; |
5490 | 505 | } |
5491 | 281k | line_num_++; |
5492 | 281k | col_num_ = 1; |
5493 | 281k | return; |
5494 | 281k | } |
5495 | 7.77M | col_num_++; |
5496 | 7.77M | idx_++; |
5497 | 7.77M | } |
5498 | 1.89M | } |
5499 | | |
5500 | 4.09M | bool at_line_end() const { |
5501 | 4.09M | if (idx_ >= length_) return true; |
5502 | 4.09M | char c = buf_[idx_]; |
5503 | 4.09M | return (c == '\n' || c == '\r' || c == '\0'); |
5504 | 4.09M | } |
5505 | | |
5506 | 141k | std::string read_line() { |
5507 | 141k | std::string result; |
5508 | 27.7M | while (idx_ < length_) { |
5509 | 27.7M | char c = buf_[idx_]; |
5510 | 27.7M | if (c == '\n' || c == '\r') break; |
5511 | 27.6M | result += c; |
5512 | 27.6M | col_num_++; |
5513 | 27.6M | idx_++; |
5514 | 27.6M | } |
5515 | 141k | return result; |
5516 | 141k | } |
5517 | | |
5518 | | // Reads a whitespace-delimited token. Used by tests and as a general utility. |
5519 | 0 | std::string read_token() { |
5520 | 0 | skip_space(); |
5521 | 0 | std::string result; |
5522 | 0 | while (idx_ < length_) { |
5523 | 0 | char c = buf_[idx_]; |
5524 | 0 | if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break; |
5525 | 0 | result += c; |
5526 | 0 | col_num_++; |
5527 | 0 | idx_++; |
5528 | 0 | } |
5529 | 0 | return result; |
5530 | 0 | } |
5531 | | |
5532 | 4.13M | bool match(const char *prefix, size_t len) const { |
5533 | 4.13M | if (idx_ >= length_ || len > length_ - idx_) return false; |
5534 | 4.12M | return (memcmp(buf_ + idx_, prefix, len) == 0); |
5535 | 4.13M | } |
5536 | | |
5537 | 0 | bool char_at(size_t offset, char c) const { |
5538 | 0 | if (idx_ >= length_ || offset >= length_ - idx_) return false; |
5539 | 0 | return buf_[idx_ + offset] == c; |
5540 | 0 | } |
5541 | | |
5542 | 2.52M | char peek_at(size_t offset) const { |
5543 | 2.52M | if (idx_ >= length_ || offset >= length_ - idx_) return '\0'; |
5544 | 2.52M | return buf_[idx_ + offset]; |
5545 | 2.52M | } |
5546 | | |
5547 | 6.25M | const char *current_ptr() const { |
5548 | 6.25M | if (idx_ >= length_) return ""; |
5549 | 5.44M | return buf_ + idx_; |
5550 | 6.25M | } |
5551 | | |
5552 | 7.16M | size_t remaining() const { |
5553 | 7.16M | return (idx_ < length_) ? (length_ - idx_) : 0; |
5554 | 7.16M | } |
5555 | | |
5556 | | // Returns the full text of the current line (for diagnostic display). |
5557 | 1.05k | std::string current_line_text() const { |
5558 | | // Scan backward to find line start |
5559 | 1.05k | size_t line_start = idx_; |
5560 | 31.3k | while (line_start > 0 && buf_[line_start - 1] != '\n' && buf_[line_start - 1] != '\r') { |
5561 | 30.2k | line_start--; |
5562 | 30.2k | } |
5563 | | // Scan forward to find line end |
5564 | 1.05k | size_t line_end = idx_; |
5565 | 3.68M | while (line_end < length_ && buf_[line_end] != '\n' && buf_[line_end] != '\r') { |
5566 | 3.68M | line_end++; |
5567 | 3.68M | } |
5568 | 1.05k | return std::string(buf_ + line_start, line_end - line_start); |
5569 | 1.05k | } |
5570 | | |
5571 | | // Clang-style formatted error with file:line:col and caret. |
5572 | 1.05k | std::string format_error(const std::string &filename, const std::string &msg) const { |
5573 | 1.05k | std::stringstream line_ss, col_ss; |
5574 | 1.05k | line_ss << line_num_; |
5575 | 1.05k | col_ss << col_num_; |
5576 | 1.05k | std::string result; |
5577 | 1.05k | result += filename + ":" + line_ss.str() + ":" + col_ss.str() + ": error: " + msg + "\n"; |
5578 | 1.05k | std::string line_text = current_line_text(); |
5579 | 1.05k | result += line_text + "\n"; |
5580 | | // Build caret line preserving tab alignment |
5581 | 1.05k | std::string caret; |
5582 | 1.05k | size_t caret_pos = (col_num_ > 0) ? (col_num_ - 1) : 0; |
5583 | 31.5k | for (size_t i = 0; i < caret_pos && i < line_text.size(); i++) { |
5584 | 30.5k | caret += (line_text[i] == '\t') ? '\t' : ' '; |
5585 | 30.5k | } |
5586 | 1.05k | caret += "^"; |
5587 | 1.05k | result += caret + "\n"; |
5588 | 1.05k | return result; |
5589 | 1.05k | } |
5590 | | |
5591 | 0 | std::string format_error(const std::string &msg) const { |
5592 | 0 | return format_error("<input>", msg); |
5593 | 0 | } |
5594 | | |
5595 | | // Error stack |
5596 | 0 | void push_error(const std::string &msg) { |
5597 | 0 | errors_.push_back(msg); |
5598 | 0 | } |
5599 | | |
5600 | 0 | void push_formatted_error(const std::string &filename, const std::string &msg) { |
5601 | 0 | errors_.push_back(format_error(filename, msg)); |
5602 | 0 | } |
5603 | | |
5604 | 10.1k | bool has_errors() const { return !errors_.empty(); } |
5605 | | |
5606 | 0 | std::string get_errors() const { |
5607 | 0 | std::string result; |
5608 | 0 | for (size_t i = 0; i < errors_.size(); i++) { |
5609 | 0 | result += errors_[i]; |
5610 | 0 | } |
5611 | 0 | return result; |
5612 | 0 | } |
5613 | | |
5614 | 0 | const std::vector<std::string> &error_stack() const { return errors_; } |
5615 | | |
5616 | 0 | void clear_errors() { errors_.clear(); } |
5617 | | |
5618 | | private: |
5619 | | const char *buf_; |
5620 | | size_t length_; |
5621 | | size_t idx_; |
5622 | | size_t line_num_; |
5623 | | size_t col_num_; |
5624 | | std::vector<char> owned_buf_; |
5625 | | std::vector<std::string> errors_; |
5626 | | }; |
5627 | | |
5628 | | #ifdef TINYOBJLOADER_USE_MMAP |
5629 | | // RAII wrapper for memory-mapped file I/O. |
5630 | | // Opens a file and maps it into memory; the mapping is released on destruction. |
5631 | | // For empty files, data is set to "" and is_mapped remains false so close() |
5632 | | // will not attempt to unmap a string literal. |
5633 | | struct MappedFile { |
5634 | | const char *data; |
5635 | | size_t size; |
5636 | | bool is_mapped; // true when data points to an actual mapped region |
5637 | | #if defined(_WIN32) |
5638 | | HANDLE hFile; |
5639 | | HANDLE hMapping; |
5640 | | #else |
5641 | | void *mapped_ptr; |
5642 | | #endif |
5643 | | |
5644 | | MappedFile() : data(NULL), size(0), is_mapped(false) |
5645 | | #if defined(_WIN32) |
5646 | | , hFile(INVALID_HANDLE_VALUE), hMapping(NULL) |
5647 | | #else |
5648 | | , mapped_ptr(NULL) |
5649 | | #endif |
5650 | | {} |
5651 | | |
5652 | | // Opens and maps the file. Returns true on success. |
5653 | | bool open(const char *filepath) { |
5654 | | #if defined(_WIN32) |
5655 | | std::wstring wfilepath = LongPathW(UTF8ToWchar(std::string(filepath))); |
5656 | | hFile = CreateFileW(wfilepath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, |
5657 | | OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
5658 | | if (hFile == INVALID_HANDLE_VALUE) return false; |
5659 | | LARGE_INTEGER fileSize; |
5660 | | if (!GetFileSizeEx(hFile, &fileSize)) { close(); return false; } |
5661 | | if (fileSize.QuadPart < 0) { close(); return false; } |
5662 | | unsigned long long fsize = static_cast<unsigned long long>(fileSize.QuadPart); |
5663 | | if (fsize > static_cast<unsigned long long>((std::numeric_limits<size_t>::max)())) { |
5664 | | close(); |
5665 | | return false; |
5666 | | } |
5667 | | size = static_cast<size_t>(fsize); |
5668 | | if (size == 0) { data = ""; return true; } // valid but empty; is_mapped stays false |
5669 | | hMapping = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL); |
5670 | | if (hMapping == NULL) { close(); return false; } |
5671 | | data = static_cast<const char *>(MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0)); |
5672 | | if (!data) { close(); return false; } |
5673 | | is_mapped = true; |
5674 | | return true; |
5675 | | #else |
5676 | | int fd = ::open(filepath, O_RDONLY); |
5677 | | if (fd == -1) return false; |
5678 | | struct stat sb; |
5679 | | if (fstat(fd, &sb) != 0) { ::close(fd); return false; } |
5680 | | if (sb.st_size < 0) { ::close(fd); return false; } |
5681 | | if (static_cast<unsigned long long>(sb.st_size) > |
5682 | | static_cast<unsigned long long>((std::numeric_limits<size_t>::max)())) { |
5683 | | ::close(fd); |
5684 | | return false; |
5685 | | } |
5686 | | size = static_cast<size_t>(sb.st_size); |
5687 | | if (size == 0) { ::close(fd); data = ""; return true; } // valid but empty |
5688 | | mapped_ptr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); |
5689 | | ::close(fd); |
5690 | | if (mapped_ptr == MAP_FAILED) { mapped_ptr = NULL; size = 0; return false; } |
5691 | | data = static_cast<const char *>(mapped_ptr); |
5692 | | is_mapped = true; |
5693 | | return true; |
5694 | | #endif |
5695 | | } |
5696 | | |
5697 | | void close() { |
5698 | | #if defined(_WIN32) |
5699 | | if (is_mapped && data) { UnmapViewOfFile(data); } |
5700 | | data = NULL; |
5701 | | is_mapped = false; |
5702 | | if (hMapping != NULL) { CloseHandle(hMapping); hMapping = NULL; } |
5703 | | if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; } |
5704 | | #else |
5705 | | if (is_mapped && mapped_ptr && mapped_ptr != MAP_FAILED) { munmap(mapped_ptr, size); } |
5706 | | mapped_ptr = NULL; |
5707 | | data = NULL; |
5708 | | is_mapped = false; |
5709 | | #endif |
5710 | | size = 0; |
5711 | | } |
5712 | | |
5713 | | ~MappedFile() { close(); } |
5714 | | |
5715 | | private: |
5716 | | MappedFile(const MappedFile &); // non-copyable |
5717 | | MappedFile &operator=(const MappedFile &); // non-copyable |
5718 | | }; |
5719 | | #endif // TINYOBJLOADER_USE_MMAP |
5720 | | |
5721 | | |
5722 | | struct vertex_index_t { |
5723 | | int v_idx, vt_idx, vn_idx; |
5724 | 190k | vertex_index_t() : v_idx(-1), vt_idx(-1), vn_idx(-1) {} |
5725 | 188k | explicit vertex_index_t(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} |
5726 | | vertex_index_t(int vidx, int vtidx, int vnidx) |
5727 | 0 | : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {} |
5728 | | }; |
5729 | | |
5730 | | // Internal data structure for face representation |
5731 | | // index + smoothing group. |
5732 | | struct face_t { |
5733 | | unsigned int |
5734 | | smoothing_group_id; // smoothing group id. 0 = smoothing groupd is off. |
5735 | | int pad_; |
5736 | | std::vector<vertex_index_t> vertex_indices; // face vertex indices. |
5737 | | |
5738 | 458k | face_t() : smoothing_group_id(0), pad_(0) {} |
5739 | | }; |
5740 | | |
5741 | | // Internal data structure for line representation |
5742 | | struct __line_t { |
5743 | | // l v1/vt1 v2/vt2 ... |
5744 | | // In the specification, line primitrive does not have normal index, but |
5745 | | // TinyObjLoader allow it |
5746 | | std::vector<vertex_index_t> vertex_indices; |
5747 | | }; |
5748 | | |
5749 | | // Internal data structure for points representation |
5750 | | struct __points_t { |
5751 | | // p v1 v2 ... |
5752 | | // In the specification, point primitrive does not have normal index and |
5753 | | // texture coord index, but TinyObjLoader allow it. |
5754 | | std::vector<vertex_index_t> vertex_indices; |
5755 | | }; |
5756 | | |
5757 | | struct tag_sizes { |
5758 | 574k | tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {} |
5759 | | int num_ints; |
5760 | | int num_reals; |
5761 | | int num_strings; |
5762 | | }; |
5763 | | |
5764 | | struct obj_shape { |
5765 | | std::vector<real_t> v; |
5766 | | std::vector<real_t> vn; |
5767 | | std::vector<real_t> vt; |
5768 | | }; |
5769 | | |
5770 | | // |
5771 | | // Manages group of primitives(face, line, points, ...) |
5772 | | struct PrimGroup { |
5773 | | std::vector<face_t> faceGroup; |
5774 | | std::vector<__line_t> lineGroup; |
5775 | | std::vector<__points_t> pointsGroup; |
5776 | | |
5777 | 19.7k | void clear() { |
5778 | 19.7k | faceGroup.clear(); |
5779 | 19.7k | lineGroup.clear(); |
5780 | 19.7k | pointsGroup.clear(); |
5781 | 19.7k | } |
5782 | | |
5783 | 19.7k | bool IsEmpty() const { |
5784 | 19.7k | return faceGroup.empty() && lineGroup.empty() && pointsGroup.empty(); |
5785 | 19.7k | } |
5786 | | |
5787 | | // TODO(syoyo): bspline, surface, ... |
5788 | | }; |
5789 | | |
5790 | | // See |
5791 | | // http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf |
5792 | 594k | #define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) |
5793 | | #define IS_DIGIT(x) \ |
5794 | | (static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10)) |
5795 | 11.5k | #define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) |
5796 | | |
5797 | | template <typename T> |
5798 | 1.88k | static inline std::string toString(const T &t) { |
5799 | 1.88k | std::stringstream ss; |
5800 | 1.88k | ss << t; |
5801 | 1.88k | return ss.str(); |
5802 | 1.88k | } |
5803 | | |
5804 | 0 | static inline std::string removeUtf8Bom(const std::string& input) { |
5805 | 0 | // UTF-8 BOM = 0xEF,0xBB,0xBF |
5806 | 0 | if (input.size() >= 3 && |
5807 | 0 | static_cast<unsigned char>(input[0]) == 0xEF && |
5808 | 0 | static_cast<unsigned char>(input[1]) == 0xBB && |
5809 | 0 | static_cast<unsigned char>(input[2]) == 0xBF) { |
5810 | 0 | return input.substr(3); // Skip BOM |
5811 | 0 | } |
5812 | 0 | return input; |
5813 | 0 | } |
5814 | | |
5815 | | // Trim trailing spaces and tabs from a string. |
5816 | 132k | static inline std::string trimTrailingWhitespace(const std::string &s) { |
5817 | 132k | size_t end = s.find_last_not_of(" \t"); |
5818 | 132k | if (end == std::string::npos) return ""; |
5819 | 126k | return s.substr(0, end + 1); |
5820 | 132k | } |
5821 | | |
5822 | | struct warning_context { |
5823 | | std::string *warn; |
5824 | | size_t line_number; |
5825 | | std::string filename; |
5826 | | }; |
5827 | | |
5828 | | // Safely convert size_t to int, clamping at INT_MAX to prevent overflow. |
5829 | 582k | static inline int size_to_int(size_t sz) { |
5830 | 582k | return sz > static_cast<size_t>(INT_MAX) ? INT_MAX : static_cast<int>(sz); |
5831 | 582k | } |
5832 | | |
5833 | | // Make index zero-base, and also support relative index. |
5834 | | static inline bool fixIndex(int idx, int n, int *ret, bool allow_zero, |
5835 | 188k | const warning_context &context) { |
5836 | 188k | if (!ret) { |
5837 | 0 | return false; |
5838 | 0 | } |
5839 | | |
5840 | 188k | if (idx > 0) { |
5841 | 187k | (*ret) = idx - 1; |
5842 | 187k | return true; |
5843 | 187k | } |
5844 | | |
5845 | 1.93k | if (idx == 0) { |
5846 | | // zero is not allowed according to the spec. |
5847 | 1.88k | if (context.warn) { |
5848 | 1.88k | (*context.warn) += |
5849 | 1.88k | context.filename + ":" + toString(context.line_number) + |
5850 | 1.88k | ": warning: zero value index found (will have a value of -1 for " |
5851 | 1.88k | "normal and tex indices)\n"; |
5852 | 1.88k | } |
5853 | | |
5854 | 1.88k | (*ret) = idx - 1; |
5855 | 1.88k | return allow_zero; |
5856 | 1.88k | } |
5857 | | |
5858 | 47 | if (idx < 0) { |
5859 | 47 | (*ret) = n + idx; // negative value = relative |
5860 | 47 | if ((*ret) < 0) { |
5861 | 40 | return false; // invalid relative index |
5862 | 40 | } |
5863 | 7 | return true; |
5864 | 47 | } |
5865 | | |
5866 | 0 | return false; // never reach here. |
5867 | 47 | } |
5868 | | |
5869 | 264 | static inline std::string parseString(const char **token) { |
5870 | 264 | std::string s; |
5871 | 264 | (*token) += strspn((*token), " \t"); |
5872 | 264 | size_t e = strcspn((*token), " \t\r"); |
5873 | 264 | s = std::string((*token), &(*token)[e]); |
5874 | 264 | (*token) += e; |
5875 | 264 | return s; |
5876 | 264 | } |
5877 | | |
5878 | 223 | static inline int parseInt(const char **token) { |
5879 | 223 | (*token) += strspn((*token), " \t"); |
5880 | 223 | int i = atoi((*token)); |
5881 | 223 | (*token) += strcspn((*token), " \t\r"); |
5882 | 223 | return i; |
5883 | 223 | } |
5884 | | |
5885 | | #ifndef TINYOBJLOADER_DISABLE_FAST_FLOAT |
5886 | | |
5887 | | // ---- fast_float-based float parser (bit-exact with strtod, ~3x faster) ---- |
5888 | | |
5889 | | namespace detail_fp { |
5890 | | |
5891 | | // Case-insensitive prefix match. Returns pointer past matched prefix, or NULL. |
5892 | | static inline const char *match_iprefix(const char *p, const char *end, |
5893 | 129 | const char *prefix) { |
5894 | 177 | while (*prefix) { |
5895 | 177 | if (p == end) return NULL; |
5896 | 160 | char c = *p; |
5897 | 160 | char e = *prefix; |
5898 | 160 | if (c >= 'A' && c <= 'Z') c += 32; |
5899 | 160 | if (e >= 'A' && e <= 'Z') e += 32; |
5900 | 160 | if (c != e) return NULL; |
5901 | 48 | ++p; |
5902 | 48 | ++prefix; |
5903 | 48 | } |
5904 | 0 | return p; |
5905 | 129 | } |
5906 | | |
5907 | | // Try to parse nan/inf. Returns true if matched, sets *result and *end_ptr. |
5908 | | static inline bool tryParseNanInf(const char *first, const char *last, |
5909 | 43 | double *result, const char **end_ptr) { |
5910 | 43 | if (first >= last) return false; |
5911 | | |
5912 | 43 | const char *p = first; |
5913 | 43 | bool negative = false; |
5914 | | |
5915 | 43 | if (*p == '-') { |
5916 | 1 | negative = true; |
5917 | 1 | ++p; |
5918 | 42 | } else if (*p == '+') { |
5919 | 0 | ++p; |
5920 | 0 | } |
5921 | | |
5922 | 43 | if (p >= last) return false; |
5923 | | |
5924 | | // Try "nan" |
5925 | 43 | const char *after = match_iprefix(p, last, "nan"); |
5926 | 43 | if (after) { |
5927 | 0 | *result = 0.0; // nan -> 0.0 for OBJ |
5928 | 0 | *end_ptr = after; |
5929 | 0 | return true; |
5930 | 0 | } |
5931 | | |
5932 | | // Try "infinity" first (longer match), then "inf" |
5933 | 43 | after = match_iprefix(p, last, "infinity"); |
5934 | 43 | if (after) { |
5935 | 0 | *result = negative ? std::numeric_limits<double>::lowest() |
5936 | 0 | : (std::numeric_limits<double>::max)(); |
5937 | 0 | *end_ptr = after; |
5938 | 0 | return true; |
5939 | 0 | } |
5940 | | |
5941 | 43 | after = match_iprefix(p, last, "inf"); |
5942 | 43 | if (after) { |
5943 | 0 | *result = negative ? std::numeric_limits<double>::lowest() |
5944 | 0 | : (std::numeric_limits<double>::max)(); |
5945 | 0 | *end_ptr = after; |
5946 | 0 | return true; |
5947 | 0 | } |
5948 | | |
5949 | 43 | return false; |
5950 | 43 | } |
5951 | | |
5952 | | } // namespace detail_fp |
5953 | | |
5954 | | // Tries to parse a floating point number located at s. |
5955 | | // Uses fast_float::from_chars for bit-exact, high-performance parsing. |
5956 | | // Handles OBJ quirks: leading '+', nan/inf with replacement values. |
5957 | | // |
5958 | | // s_end should be a location in the string where reading should absolutely |
5959 | | // stop. For example at the end of the string, to prevent buffer overflows. |
5960 | | // |
5961 | | // If the parsing is a success, result is set to the parsed value and true |
5962 | | // is returned. |
5963 | | // |
5964 | 5.66k | static bool tryParseDouble(const char *s, const char *s_end, double *result) { |
5965 | 5.66k | if (!s || !s_end || !result || s >= s_end) { |
5966 | 524 | return false; |
5967 | 524 | } |
5968 | | |
5969 | | // Check for nan/inf (starts with [nNiI] or [+-] followed by [nNiI]) |
5970 | 5.14k | const char *p = s; |
5971 | 5.14k | if (p < s_end && (*p == '+' || *p == '-')) ++p; |
5972 | 5.14k | if (p < s_end) { |
5973 | 4.78k | char fc = *p; |
5974 | 4.78k | if (fc >= 'A' && fc <= 'Z') fc += 32; |
5975 | 4.78k | if (fc == 'n' || fc == 'i') { |
5976 | 43 | const char *end_ptr; |
5977 | 43 | if (detail_fp::tryParseNanInf(s, s_end, result, &end_ptr)) { |
5978 | 0 | return true; |
5979 | 0 | } |
5980 | 43 | } |
5981 | 4.78k | } |
5982 | | |
5983 | | // Use allow_leading_plus so fast_float handles '+' natively. |
5984 | 5.14k | double tmp; |
5985 | 5.14k | auto r = fast_float::from_chars(s, s_end, tmp, |
5986 | 5.14k | fast_float::chars_format::general | |
5987 | 5.14k | fast_float::chars_format::allow_leading_plus); |
5988 | 5.14k | if (r.ec == tinyobj_ff::ff_errc::ok) { |
5989 | 2.81k | *result = tmp; |
5990 | 2.81k | return true; |
5991 | 2.81k | } |
5992 | | // On error (invalid_argument, result_out_of_range), *result is unchanged. |
5993 | | |
5994 | 2.32k | return false; |
5995 | 5.14k | } |
5996 | | |
5997 | 1.95k | static inline real_t parseReal(const char **token, double default_value = 0.0) { |
5998 | 1.95k | (*token) += strspn((*token), " \t"); |
5999 | 1.95k | const char *end = (*token) + strcspn((*token), " \t\r"); |
6000 | 1.95k | double val = default_value; |
6001 | 1.95k | tryParseDouble((*token), end, &val); |
6002 | 1.95k | real_t f = static_cast<real_t>(val); |
6003 | 1.95k | (*token) = end; |
6004 | 1.95k | return f; |
6005 | 1.95k | } |
6006 | | |
6007 | 0 | static inline bool parseReal(const char **token, real_t *out) { |
6008 | 0 | (*token) += strspn((*token), " \t"); |
6009 | 0 | const char *end = (*token) + strcspn((*token), " \t\r"); |
6010 | 0 | double val; |
6011 | 0 | bool ret = tryParseDouble((*token), end, &val); |
6012 | 0 | if (ret) { |
6013 | 0 | real_t f = static_cast<real_t>(val); |
6014 | 0 | (*out) = f; |
6015 | 0 | } |
6016 | 0 | (*token) = end; |
6017 | 0 | return ret; |
6018 | 0 | } |
6019 | | |
6020 | | #else // TINYOBJLOADER_DISABLE_FAST_FLOAT |
6021 | | |
6022 | | // ---- Legacy hand-written float parser (fallback) ---- |
6023 | | |
6024 | | // Tries to parse a floating point number located at s. |
6025 | | // |
6026 | | // s_end should be a location in the string where reading should absolutely |
6027 | | // stop. For example at the end of the string, to prevent buffer overflows. |
6028 | | // |
6029 | | // Parses the following EBNF grammar: |
6030 | | // sign = "+" | "-" ; |
6031 | | // END = ? anything not in digit ? |
6032 | | // digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; |
6033 | | // integer = [sign] , digit , {digit} ; |
6034 | | // decimal = integer , ["." , integer] ; |
6035 | | // float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; |
6036 | | // |
6037 | | // Valid strings are for example: |
6038 | | // -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 |
6039 | | // |
6040 | | // If the parsing is a success, result is set to the parsed value and true |
6041 | | // is returned. |
6042 | | // |
6043 | | // The function is greedy and will parse until any of the following happens: |
6044 | | // - a non-conforming character is encountered. |
6045 | | // - s_end is reached. |
6046 | | // |
6047 | | // The following situations triggers a failure: |
6048 | | // - s >= s_end. |
6049 | | // - parse failure. |
6050 | | // |
6051 | | static bool tryParseDouble(const char *s, const char *s_end, double *result) { |
6052 | | if (s >= s_end) { |
6053 | | return false; |
6054 | | } |
6055 | | |
6056 | | double mantissa = 0.0; |
6057 | | // This exponent is base 2 rather than 10. |
6058 | | // However the exponent we parse is supposed to be one of ten, |
6059 | | // thus we must take care to convert the exponent/and or the |
6060 | | // mantissa to a * 2^E, where a is the mantissa and E is the |
6061 | | // exponent. |
6062 | | // To get the final double we will use ldexp, it requires the |
6063 | | // exponent to be in base 2. |
6064 | | int exponent = 0; |
6065 | | |
6066 | | // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED |
6067 | | // TO JUMP OVER DEFINITIONS. |
6068 | | char sign = '+'; |
6069 | | char exp_sign = '+'; |
6070 | | char const *curr = s; |
6071 | | |
6072 | | // How many characters were read in a loop. |
6073 | | int read = 0; |
6074 | | // Tells whether a loop terminated due to reaching s_end. |
6075 | | bool end_not_reached = false; |
6076 | | bool leading_decimal_dots = false; |
6077 | | |
6078 | | /* |
6079 | | BEGIN PARSING. |
6080 | | */ |
6081 | | |
6082 | | // Find out what sign we've got. |
6083 | | if (*curr == '+' || *curr == '-') { |
6084 | | sign = *curr; |
6085 | | curr++; |
6086 | | if ((curr != s_end) && (*curr == '.')) { |
6087 | | // accept. Somethig like `.7e+2`, `-.5234` |
6088 | | leading_decimal_dots = true; |
6089 | | } |
6090 | | } else if (IS_DIGIT(*curr)) { /* Pass through. */ |
6091 | | } else if (*curr == '.') { |
6092 | | // accept. Somethig like `.7e+2`, `-.5234` |
6093 | | leading_decimal_dots = true; |
6094 | | } else { |
6095 | | goto fail; |
6096 | | } |
6097 | | |
6098 | | // Read the integer part. |
6099 | | end_not_reached = (curr != s_end); |
6100 | | if (!leading_decimal_dots) { |
6101 | | while (end_not_reached && IS_DIGIT(*curr)) { |
6102 | | mantissa *= 10; |
6103 | | mantissa += static_cast<int>(*curr - 0x30); |
6104 | | curr++; |
6105 | | read++; |
6106 | | end_not_reached = (curr != s_end); |
6107 | | } |
6108 | | |
6109 | | // We must make sure we actually got something. |
6110 | | if (read == 0) goto fail; |
6111 | | } |
6112 | | |
6113 | | // We allow numbers of form "#", "###" etc. |
6114 | | if (!end_not_reached) goto assemble; |
6115 | | |
6116 | | // Read the decimal part. |
6117 | | if (*curr == '.') { |
6118 | | curr++; |
6119 | | read = 1; |
6120 | | end_not_reached = (curr != s_end); |
6121 | | while (end_not_reached && IS_DIGIT(*curr)) { |
6122 | | static const double pow_lut[] = { |
6123 | | 1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, |
6124 | | }; |
6125 | | const int lut_entries = sizeof pow_lut / sizeof pow_lut[0]; |
6126 | | |
6127 | | // NOTE: Don't use powf here, it will absolutely murder precision. |
6128 | | mantissa += static_cast<int>(*curr - 0x30) * |
6129 | | (read < lut_entries ? pow_lut[read] : std::pow(10.0, -read)); |
6130 | | read++; |
6131 | | curr++; |
6132 | | end_not_reached = (curr != s_end); |
6133 | | } |
6134 | | } else if (*curr == 'e' || *curr == 'E') { |
6135 | | } else { |
6136 | | goto assemble; |
6137 | | } |
6138 | | |
6139 | | if (!end_not_reached) goto assemble; |
6140 | | |
6141 | | // Read the exponent part. |
6142 | | if (*curr == 'e' || *curr == 'E') { |
6143 | | curr++; |
6144 | | // Figure out if a sign is present and if it is. |
6145 | | end_not_reached = (curr != s_end); |
6146 | | if (end_not_reached && (*curr == '+' || *curr == '-')) { |
6147 | | exp_sign = *curr; |
6148 | | curr++; |
6149 | | } else if (IS_DIGIT(*curr)) { /* Pass through. */ |
6150 | | } else { |
6151 | | // Empty E is not allowed. |
6152 | | goto fail; |
6153 | | } |
6154 | | |
6155 | | read = 0; |
6156 | | end_not_reached = (curr != s_end); |
6157 | | while (end_not_reached && IS_DIGIT(*curr)) { |
6158 | | // To avoid annoying MSVC's min/max macro definiton, |
6159 | | // Use hardcoded int max value |
6160 | | if (exponent > |
6161 | | ((2147483647 - 9) / 10)) { // (INT_MAX - 9) / 10, guards both multiply and add |
6162 | | // Integer overflow |
6163 | | goto fail; |
6164 | | } |
6165 | | exponent *= 10; |
6166 | | exponent += static_cast<int>(*curr - 0x30); |
6167 | | curr++; |
6168 | | read++; |
6169 | | end_not_reached = (curr != s_end); |
6170 | | } |
6171 | | exponent *= (exp_sign == '+' ? 1 : -1); |
6172 | | if (read == 0) goto fail; |
6173 | | } |
6174 | | |
6175 | | assemble: |
6176 | | *result = (sign == '+' ? 1 : -1) * |
6177 | | (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent) |
6178 | | : mantissa); |
6179 | | return true; |
6180 | | fail: |
6181 | | return false; |
6182 | | } |
6183 | | |
6184 | | static inline real_t parseReal(const char **token, double default_value = 0.0) { |
6185 | | (*token) += strspn((*token), " \t"); |
6186 | | const char *end = (*token) + strcspn((*token), " \t\r"); |
6187 | | double val = default_value; |
6188 | | tryParseDouble((*token), end, &val); |
6189 | | real_t f = static_cast<real_t>(val); |
6190 | | (*token) = end; |
6191 | | return f; |
6192 | | } |
6193 | | |
6194 | | static inline bool parseReal(const char **token, real_t *out) { |
6195 | | (*token) += strspn((*token), " \t"); |
6196 | | const char *end = (*token) + strcspn((*token), " \t\r"); |
6197 | | double val; |
6198 | | bool ret = tryParseDouble((*token), end, &val); |
6199 | | if (ret) { |
6200 | | real_t f = static_cast<real_t>(val); |
6201 | | (*out) = f; |
6202 | | } |
6203 | | (*token) = end; |
6204 | | return ret; |
6205 | | } |
6206 | | |
6207 | | #endif // TINYOBJLOADER_DISABLE_FAST_FLOAT |
6208 | | |
6209 | | static inline void parseReal2(real_t *x, real_t *y, const char **token, |
6210 | | const double default_x = 0.0, |
6211 | 268 | const double default_y = 0.0) { |
6212 | 268 | (*x) = parseReal(token, default_x); |
6213 | 268 | (*y) = parseReal(token, default_y); |
6214 | 268 | } |
6215 | | |
6216 | | static inline void parseReal3(real_t *x, real_t *y, real_t *z, |
6217 | | const char **token, const double default_x = 0.0, |
6218 | | const double default_y = 0.0, |
6219 | 419 | const double default_z = 0.0) { |
6220 | 419 | (*x) = parseReal(token, default_x); |
6221 | 419 | (*y) = parseReal(token, default_y); |
6222 | 419 | (*z) = parseReal(token, default_z); |
6223 | 419 | } |
6224 | | |
6225 | | #if 0 // not used |
6226 | | static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w, |
6227 | | const char **token, const double default_x = 0.0, |
6228 | | const double default_y = 0.0, |
6229 | | const double default_z = 0.0, |
6230 | | const double default_w = 1.0) { |
6231 | | (*x) = parseReal(token, default_x); |
6232 | | (*y) = parseReal(token, default_y); |
6233 | | (*z) = parseReal(token, default_z); |
6234 | | (*w) = parseReal(token, default_w); |
6235 | | } |
6236 | | #endif |
6237 | | |
6238 | | // Extension: parse vertex with colors(6 items) |
6239 | | // Return 3: xyz, 4: xyzw, 6: xyzrgb |
6240 | | // `r`: red(case 6) or [w](case 4) |
6241 | | static inline int parseVertexWithColor(real_t *x, real_t *y, real_t *z, |
6242 | | real_t *r, real_t *g, real_t *b, |
6243 | | const char **token, |
6244 | | const double default_x = 0.0, |
6245 | | const double default_y = 0.0, |
6246 | 0 | const double default_z = 0.0) { |
6247 | 0 | // TODO: Check error |
6248 | 0 | (*x) = parseReal(token, default_x); |
6249 | 0 | (*y) = parseReal(token, default_y); |
6250 | 0 | (*z) = parseReal(token, default_z); |
6251 | 0 |
|
6252 | 0 | // - 4 components(x, y, z, w) ot 6 components |
6253 | 0 | bool has_r = parseReal(token, r); |
6254 | 0 |
|
6255 | 0 | if (!has_r) { |
6256 | 0 | (*r) = (*g) = (*b) = 1.0; |
6257 | 0 | return 3; |
6258 | 0 | } |
6259 | 0 |
|
6260 | 0 | bool has_g = parseReal(token, g); |
6261 | 0 |
|
6262 | 0 | if (!has_g) { |
6263 | 0 | (*g) = (*b) = 1.0; |
6264 | 0 | return 4; |
6265 | 0 | } |
6266 | 0 |
|
6267 | 0 | bool has_b = parseReal(token, b); |
6268 | 0 |
|
6269 | 0 | if (!has_b) { |
6270 | 0 | (*r) = (*g) = (*b) = 1.0; |
6271 | 0 | return 3; // treated as xyz |
6272 | 0 | } |
6273 | 0 |
|
6274 | 0 | return 6; |
6275 | 0 | } |
6276 | | |
6277 | 854 | static inline bool parseOnOff(const char **token, bool default_value = true) { |
6278 | 854 | (*token) += strspn((*token), " \t"); |
6279 | 854 | const char *end = (*token) + strcspn((*token), " \t\r"); |
6280 | | |
6281 | 854 | bool ret = default_value; |
6282 | 854 | if ((0 == strncmp((*token), "on", 2))) { |
6283 | 138 | ret = true; |
6284 | 716 | } else if ((0 == strncmp((*token), "off", 3))) { |
6285 | 185 | ret = false; |
6286 | 185 | } |
6287 | | |
6288 | 854 | (*token) = end; |
6289 | 854 | return ret; |
6290 | 854 | } |
6291 | | |
6292 | | static inline texture_type_t parseTextureType( |
6293 | 46 | const char **token, texture_type_t default_value = TEXTURE_TYPE_NONE) { |
6294 | 46 | (*token) += strspn((*token), " \t"); |
6295 | 46 | const char *end = (*token) + strcspn((*token), " \t\r"); |
6296 | 46 | texture_type_t ty = default_value; |
6297 | | |
6298 | 46 | if ((0 == strncmp((*token), "cube_top", strlen("cube_top")))) { |
6299 | 0 | ty = TEXTURE_TYPE_CUBE_TOP; |
6300 | 46 | } else if ((0 == strncmp((*token), "cube_bottom", strlen("cube_bottom")))) { |
6301 | 0 | ty = TEXTURE_TYPE_CUBE_BOTTOM; |
6302 | 46 | } else if ((0 == strncmp((*token), "cube_left", strlen("cube_left")))) { |
6303 | 4 | ty = TEXTURE_TYPE_CUBE_LEFT; |
6304 | 42 | } else if ((0 == strncmp((*token), "cube_right", strlen("cube_right")))) { |
6305 | 5 | ty = TEXTURE_TYPE_CUBE_RIGHT; |
6306 | 37 | } else if ((0 == strncmp((*token), "cube_front", strlen("cube_front")))) { |
6307 | 0 | ty = TEXTURE_TYPE_CUBE_FRONT; |
6308 | 37 | } else if ((0 == strncmp((*token), "cube_back", strlen("cube_back")))) { |
6309 | 0 | ty = TEXTURE_TYPE_CUBE_BACK; |
6310 | 37 | } else if ((0 == strncmp((*token), "sphere", strlen("sphere")))) { |
6311 | 7 | ty = TEXTURE_TYPE_SPHERE; |
6312 | 7 | } |
6313 | | |
6314 | 46 | (*token) = end; |
6315 | 46 | return ty; |
6316 | 46 | } |
6317 | | |
6318 | 0 | static tag_sizes parseTagTriple(const char **token) { |
6319 | 0 | tag_sizes ts; |
6320 | 0 |
|
6321 | 0 | (*token) += strspn((*token), " \t"); |
6322 | 0 | ts.num_ints = atoi((*token)); |
6323 | 0 | (*token) += strcspn((*token), "/ \t\r"); |
6324 | 0 | if ((*token)[0] != '/') { |
6325 | 0 | return ts; |
6326 | 0 | } |
6327 | 0 |
|
6328 | 0 | (*token)++; // Skip '/' |
6329 | 0 |
|
6330 | 0 | (*token) += strspn((*token), " \t"); |
6331 | 0 | ts.num_reals = atoi((*token)); |
6332 | 0 | (*token) += strcspn((*token), "/ \t\r"); |
6333 | 0 | if ((*token)[0] != '/') { |
6334 | 0 | return ts; |
6335 | 0 | } |
6336 | 0 | (*token)++; // Skip '/' |
6337 | 0 |
|
6338 | 0 | ts.num_strings = parseInt(token); |
6339 | 0 |
|
6340 | 0 | return ts; |
6341 | 0 | } |
6342 | | |
6343 | | // Parse triples with index offsets: i, i/j/k, i//k, i/j |
6344 | | static bool parseTriple(const char **token, int vsize, int vnsize, int vtsize, |
6345 | 0 | vertex_index_t *ret, const warning_context &context) { |
6346 | 0 | if (!ret) { |
6347 | 0 | return false; |
6348 | 0 | } |
6349 | 0 |
|
6350 | 0 | vertex_index_t vi(-1); |
6351 | 0 |
|
6352 | 0 | if (!fixIndex(atoi((*token)), vsize, &vi.v_idx, false, context)) { |
6353 | 0 | return false; |
6354 | 0 | } |
6355 | 0 |
|
6356 | 0 | (*token) += strcspn((*token), "/ \t\r"); |
6357 | 0 | if ((*token)[0] != '/') { |
6358 | 0 | (*ret) = vi; |
6359 | 0 | return true; |
6360 | 0 | } |
6361 | 0 | (*token)++; |
6362 | 0 |
|
6363 | 0 | // i//k |
6364 | 0 | if ((*token)[0] == '/') { |
6365 | 0 | (*token)++; |
6366 | 0 | if (!fixIndex(atoi((*token)), vnsize, &vi.vn_idx, true, context)) { |
6367 | 0 | return false; |
6368 | 0 | } |
6369 | 0 | (*token) += strcspn((*token), "/ \t\r"); |
6370 | 0 | (*ret) = vi; |
6371 | 0 | return true; |
6372 | 0 | } |
6373 | 0 |
|
6374 | 0 | // i/j/k or i/j |
6375 | 0 | if (!fixIndex(atoi((*token)), vtsize, &vi.vt_idx, true, context)) { |
6376 | 0 | return false; |
6377 | 0 | } |
6378 | 0 |
|
6379 | 0 | (*token) += strcspn((*token), "/ \t\r"); |
6380 | 0 | if ((*token)[0] != '/') { |
6381 | 0 | (*ret) = vi; |
6382 | 0 | return true; |
6383 | 0 | } |
6384 | 0 |
|
6385 | 0 | // i/j/k |
6386 | 0 | (*token)++; // skip '/' |
6387 | 0 | if (!fixIndex(atoi((*token)), vnsize, &vi.vn_idx, true, context)) { |
6388 | 0 | return false; |
6389 | 0 | } |
6390 | 0 | (*token) += strcspn((*token), "/ \t\r"); |
6391 | 0 |
|
6392 | 0 | (*ret) = vi; |
6393 | 0 |
|
6394 | 0 | return true; |
6395 | 0 | } |
6396 | | |
6397 | | // Parse raw triples: i, i/j/k, i//k, i/j |
6398 | 0 | static vertex_index_t parseRawTriple(const char **token) { |
6399 | 0 | vertex_index_t vi(static_cast<int>(0)); // 0 is an invalid index in OBJ |
6400 | 0 |
|
6401 | 0 | vi.v_idx = atoi((*token)); |
6402 | 0 | (*token) += strcspn((*token), "/ \t\r"); |
6403 | 0 | if ((*token)[0] != '/') { |
6404 | 0 | return vi; |
6405 | 0 | } |
6406 | 0 | (*token)++; |
6407 | 0 |
|
6408 | 0 | // i//k |
6409 | 0 | if ((*token)[0] == '/') { |
6410 | 0 | (*token)++; |
6411 | 0 | vi.vn_idx = atoi((*token)); |
6412 | 0 | (*token) += strcspn((*token), "/ \t\r"); |
6413 | 0 | return vi; |
6414 | 0 | } |
6415 | 0 |
|
6416 | 0 | // i/j/k or i/j |
6417 | 0 | vi.vt_idx = atoi((*token)); |
6418 | 0 | (*token) += strcspn((*token), "/ \t\r"); |
6419 | 0 | if ((*token)[0] != '/') { |
6420 | 0 | return vi; |
6421 | 0 | } |
6422 | 0 |
|
6423 | 0 | // i/j/k |
6424 | 0 | (*token)++; // skip '/' |
6425 | 0 | vi.vn_idx = atoi((*token)); |
6426 | 0 | (*token) += strcspn((*token), "/ \t\r"); |
6427 | 0 | return vi; |
6428 | 0 | } |
6429 | | |
6430 | | // --- Stream-based parse functions --- |
6431 | | |
6432 | 1.98M | static inline std::string sr_parseString(StreamReader &sr) { |
6433 | 1.98M | sr.skip_space(); |
6434 | 1.98M | std::string s; |
6435 | 13.0M | while (!sr.eof()) { |
6436 | 12.7M | char c = sr.peek(); |
6437 | 12.7M | if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break; |
6438 | 11.0M | s += c; |
6439 | 11.0M | sr.advance(1); |
6440 | 11.0M | } |
6441 | 1.98M | return s; |
6442 | 1.98M | } |
6443 | | |
6444 | 3.22M | static inline int sr_parseInt(StreamReader &sr) { |
6445 | 3.22M | sr.skip_space(); |
6446 | 3.22M | const char *start = sr.current_ptr(); |
6447 | 3.22M | size_t rem = sr.remaining(); |
6448 | 3.22M | size_t len = 0; |
6449 | 4.41M | while (len < rem) { |
6450 | 4.04M | char c = start[len]; |
6451 | 4.04M | if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break; |
6452 | 1.19M | len++; |
6453 | 1.19M | } |
6454 | 3.22M | int i = 0; |
6455 | 3.22M | if (len > 0) { |
6456 | 2.78k | char tmp[64]; |
6457 | 2.78k | size_t copy_len = len < 63 ? len : 63; |
6458 | 2.78k | if (copy_len != len) { |
6459 | 11 | sr.advance(len); |
6460 | 11 | return 0; |
6461 | 11 | } |
6462 | 2.77k | memcpy(tmp, start, copy_len); |
6463 | 2.77k | tmp[copy_len] = '\0'; |
6464 | 2.77k | errno = 0; |
6465 | 2.77k | char *endptr = NULL; |
6466 | 2.77k | long val = strtol(tmp, &endptr, 10); |
6467 | 2.77k | const bool has_error = |
6468 | 2.77k | (errno == ERANGE || endptr == tmp || |
6469 | 1.84k | val > (std::numeric_limits<int>::max)() || |
6470 | 1.83k | val < (std::numeric_limits<int>::min)()); |
6471 | 2.77k | if (!has_error) { |
6472 | 1.83k | i = static_cast<int>(val); |
6473 | 1.83k | } |
6474 | 2.77k | } |
6475 | 3.22M | sr.advance(len); |
6476 | 3.22M | return i; |
6477 | 3.22M | } |
6478 | | |
6479 | 2.24M | static inline real_t sr_parseReal(StreamReader &sr, double default_value = 0.0) { |
6480 | 2.24M | sr.skip_space(); |
6481 | 2.24M | const char *start = sr.current_ptr(); |
6482 | 2.24M | size_t rem = sr.remaining(); |
6483 | 2.24M | size_t len = 0; |
6484 | 2.25M | while (len < rem) { |
6485 | 1.81M | char c = start[len]; |
6486 | 1.81M | if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break; |
6487 | 9.48k | len++; |
6488 | 9.48k | } |
6489 | 2.24M | double val = default_value; |
6490 | 2.24M | if (len > 0) { |
6491 | 641 | tryParseDouble(start, start + len, &val); |
6492 | 641 | } |
6493 | 2.24M | sr.advance(len); |
6494 | 2.24M | return static_cast<real_t>(val); |
6495 | 2.24M | } |
6496 | | |
6497 | 4.33k | static inline bool sr_parseReal(StreamReader &sr, real_t *out) { |
6498 | 4.33k | sr.skip_space(); |
6499 | 4.33k | const char *start = sr.current_ptr(); |
6500 | 4.33k | size_t rem = sr.remaining(); |
6501 | 4.33k | size_t len = 0; |
6502 | 5.08k | while (len < rem) { |
6503 | 4.74k | char c = start[len]; |
6504 | 4.74k | if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break; |
6505 | 750 | len++; |
6506 | 750 | } |
6507 | 4.33k | if (len == 0) return false; |
6508 | 538 | double val; |
6509 | 538 | bool ret = tryParseDouble(start, start + len, &val); |
6510 | 538 | if (ret) { |
6511 | 299 | (*out) = static_cast<real_t>(val); |
6512 | 299 | } |
6513 | 538 | sr.advance(len); |
6514 | 538 | return ret; |
6515 | 4.33k | } |
6516 | | |
6517 | | static inline void sr_parseReal2(real_t *x, real_t *y, StreamReader &sr, |
6518 | | const double default_x = 0.0, |
6519 | 136 | const double default_y = 0.0) { |
6520 | 136 | (*x) = sr_parseReal(sr, default_x); |
6521 | 136 | (*y) = sr_parseReal(sr, default_y); |
6522 | 136 | } |
6523 | | |
6524 | | static inline void sr_parseReal3(real_t *x, real_t *y, real_t *z, |
6525 | | StreamReader &sr, |
6526 | | const double default_x = 0.0, |
6527 | | const double default_y = 0.0, |
6528 | 0 | const double default_z = 0.0) { |
6529 | 0 | (*x) = sr_parseReal(sr, default_x); |
6530 | 0 | (*y) = sr_parseReal(sr, default_y); |
6531 | 0 | (*z) = sr_parseReal(sr, default_z); |
6532 | 0 | } |
6533 | | |
6534 | | static inline int sr_parseVertexWithColor(real_t *x, real_t *y, real_t *z, |
6535 | | real_t *r, real_t *g, real_t *b, |
6536 | | StreamReader &sr, |
6537 | | const double default_x = 0.0, |
6538 | | const double default_y = 0.0, |
6539 | 0 | const double default_z = 0.0) { |
6540 | 0 | (*x) = sr_parseReal(sr, default_x); |
6541 | 0 | (*y) = sr_parseReal(sr, default_y); |
6542 | 0 | (*z) = sr_parseReal(sr, default_z); |
6543 | 0 |
|
6544 | 0 | bool has_r = sr_parseReal(sr, r); |
6545 | 0 | if (!has_r) { |
6546 | 0 | (*r) = (*g) = (*b) = 1.0; |
6547 | 0 | return 3; |
6548 | 0 | } |
6549 | 0 |
|
6550 | 0 | bool has_g = sr_parseReal(sr, g); |
6551 | 0 | if (!has_g) { |
6552 | 0 | (*g) = (*b) = 1.0; |
6553 | 0 | return 4; |
6554 | 0 | } |
6555 | 0 |
|
6556 | 0 | bool has_b = sr_parseReal(sr, b); |
6557 | 0 | if (!has_b) { |
6558 | 0 | (*r) = (*g) = (*b) = 1.0; |
6559 | 0 | return 3; |
6560 | 0 | } |
6561 | 0 |
|
6562 | 0 | return 6; |
6563 | 0 | } |
6564 | | |
6565 | | // --- Error-reporting overloads --- |
6566 | | // These overloads push clang-style diagnostics into `err` when parsing fails |
6567 | | // and return false so callers can early-return on unrecoverable parse errors. |
6568 | | // The original signatures are preserved above for backward compatibility. |
6569 | | |
6570 | | static inline bool sr_parseInt(StreamReader &sr, int *out, std::string *err, |
6571 | 170 | const std::string &filename) { |
6572 | 170 | sr.skip_space(); |
6573 | 170 | const char *start = sr.current_ptr(); |
6574 | 170 | size_t rem = sr.remaining(); |
6575 | 170 | size_t len = 0; |
6576 | 314 | while (len < rem) { |
6577 | 312 | char c = start[len]; |
6578 | 312 | if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break; |
6579 | 144 | len++; |
6580 | 144 | } |
6581 | 170 | if (len == 0) { |
6582 | 43 | if (err) { |
6583 | 43 | (*err) += sr.format_error(filename, "expected integer value"); |
6584 | 43 | } |
6585 | 43 | *out = 0; |
6586 | 43 | return false; |
6587 | 43 | } |
6588 | 127 | char tmp[64]; |
6589 | 127 | size_t copy_len = len < 63 ? len : 63; |
6590 | 127 | memcpy(tmp, start, copy_len); |
6591 | 127 | tmp[copy_len] = '\0'; |
6592 | 127 | if (copy_len != len) { |
6593 | 0 | if (err) { |
6594 | 0 | (*err) += sr.format_error(filename, "integer value too long"); |
6595 | 0 | } |
6596 | 0 | *out = 0; |
6597 | 0 | sr.advance(len); |
6598 | 0 | return false; |
6599 | 0 | } |
6600 | 127 | errno = 0; |
6601 | 127 | char *endptr = NULL; |
6602 | 127 | long val = strtol(tmp, &endptr, 10); |
6603 | 127 | if (errno == ERANGE || val > (std::numeric_limits<int>::max)() || |
6604 | 127 | val < (std::numeric_limits<int>::min)()) { |
6605 | 0 | if (err) { |
6606 | 0 | (*err) += sr.format_error(filename, |
6607 | 0 | "integer value out of range, got '" + std::string(tmp) + "'"); |
6608 | 0 | } |
6609 | 0 | *out = 0; |
6610 | 0 | sr.advance(len); |
6611 | 0 | return false; |
6612 | 0 | } |
6613 | 127 | if (endptr == tmp || (*endptr != '\0' && *endptr != ' ' && *endptr != '\t')) { |
6614 | 115 | if (err) { |
6615 | 115 | (*err) += sr.format_error(filename, |
6616 | 115 | "expected integer, got '" + std::string(tmp) + "'"); |
6617 | 115 | } |
6618 | 115 | *out = 0; |
6619 | 115 | sr.advance(len); |
6620 | 115 | return false; |
6621 | 115 | } |
6622 | 12 | *out = static_cast<int>(val); |
6623 | 12 | sr.advance(len); |
6624 | 12 | return true; |
6625 | 127 | } |
6626 | | |
6627 | | static inline bool sr_parseReal(StreamReader &sr, real_t *out, |
6628 | | double default_value, |
6629 | | std::string *err, |
6630 | 14.4k | const std::string &filename) { |
6631 | 14.4k | sr.skip_space(); |
6632 | 14.4k | const char *start = sr.current_ptr(); |
6633 | 14.4k | size_t rem = sr.remaining(); |
6634 | 14.4k | size_t len = 0; |
6635 | 45.1k | while (len < rem) { |
6636 | 44.1k | char c = start[len]; |
6637 | 44.1k | if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break; |
6638 | 30.7k | len++; |
6639 | 30.7k | } |
6640 | 14.4k | if (len == 0) { |
6641 | | // No token to parse — not necessarily an error (e.g. optional component). |
6642 | 11.9k | *out = static_cast<real_t>(default_value); |
6643 | 11.9k | return true; |
6644 | 11.9k | } |
6645 | 2.52k | double val; |
6646 | 2.52k | if (!tryParseDouble(start, start + len, &val)) { |
6647 | 485 | if (err) { |
6648 | 485 | char tmp[64]; |
6649 | 485 | size_t copy_len = len < 63 ? len : 63; |
6650 | 485 | memcpy(tmp, start, copy_len); |
6651 | 485 | tmp[copy_len] = '\0'; |
6652 | 485 | (*err) += sr.format_error(filename, |
6653 | 485 | "expected number, got '" + std::string(tmp) + "'"); |
6654 | 485 | } |
6655 | 485 | *out = static_cast<real_t>(default_value); |
6656 | 485 | sr.advance(len); |
6657 | 485 | return false; |
6658 | 485 | } |
6659 | 2.04k | *out = static_cast<real_t>(val); |
6660 | 2.04k | sr.advance(len); |
6661 | 2.04k | return true; |
6662 | 2.52k | } |
6663 | | |
6664 | | static inline bool sr_parseReal2(real_t *x, real_t *y, StreamReader &sr, |
6665 | | std::string *err, |
6666 | | const std::string &filename, |
6667 | | const double default_x = 0.0, |
6668 | 1.05k | const double default_y = 0.0) { |
6669 | 1.05k | if (!sr_parseReal(sr, x, default_x, err, filename)) return false; |
6670 | 1.05k | if (!sr_parseReal(sr, y, default_y, err, filename)) return false; |
6671 | 1.05k | return true; |
6672 | 1.05k | } |
6673 | | |
6674 | | static inline bool sr_parseReal3(real_t *x, real_t *y, real_t *z, |
6675 | | StreamReader &sr, |
6676 | | std::string *err, |
6677 | | const std::string &filename, |
6678 | | const double default_x = 0.0, |
6679 | | const double default_y = 0.0, |
6680 | 961 | const double default_z = 0.0) { |
6681 | 961 | if (!sr_parseReal(sr, x, default_x, err, filename)) return false; |
6682 | 926 | if (!sr_parseReal(sr, y, default_y, err, filename)) return false; |
6683 | 926 | if (!sr_parseReal(sr, z, default_z, err, filename)) return false; |
6684 | 926 | return true; |
6685 | 926 | } |
6686 | | |
6687 | | // Returns number of components parsed (3, 4, or 6) on success, -1 on error. |
6688 | | static inline int sr_parseVertexWithColor(real_t *x, real_t *y, real_t *z, |
6689 | | real_t *r, real_t *g, real_t *b, |
6690 | | StreamReader &sr, |
6691 | | std::string *err, |
6692 | | const std::string &filename, |
6693 | | const double default_x = 0.0, |
6694 | | const double default_y = 0.0, |
6695 | 3.39k | const double default_z = 0.0) { |
6696 | 3.39k | if (!sr_parseReal(sr, x, default_x, err, filename)) return -1; |
6697 | 3.03k | if (!sr_parseReal(sr, y, default_y, err, filename)) return -1; |
6698 | 3.01k | if (!sr_parseReal(sr, z, default_z, err, filename)) return -1; |
6699 | | |
6700 | 3.00k | bool has_r = sr_parseReal(sr, r); |
6701 | 3.00k | if (!has_r) { |
6702 | 2.75k | (*r) = (*g) = (*b) = 1.0; |
6703 | 2.75k | return 3; |
6704 | 2.75k | } |
6705 | | |
6706 | 249 | bool has_g = sr_parseReal(sr, g); |
6707 | 249 | if (!has_g) { |
6708 | 219 | (*g) = (*b) = 1.0; |
6709 | 219 | return 4; |
6710 | 219 | } |
6711 | | |
6712 | 30 | bool has_b = sr_parseReal(sr, b); |
6713 | 30 | if (!has_b) { |
6714 | 11 | (*r) = (*g) = (*b) = 1.0; |
6715 | 11 | return 3; |
6716 | 11 | } |
6717 | | |
6718 | 19 | return 6; |
6719 | 30 | } |
6720 | | |
6721 | | static inline int sr_parseIntNoSkip(StreamReader &sr); |
6722 | | |
6723 | | // Advance past remaining characters in a tag triple field (stops at '/', whitespace, or line end). |
6724 | 576k | static inline void sr_skipTagField(StreamReader &sr) { |
6725 | 582k | while (!sr.eof() && !sr.at_line_end() && !IS_SPACE(sr.peek()) && |
6726 | 8.17k | sr.peek() != '/') { |
6727 | 5.69k | sr.advance(1); |
6728 | 5.69k | } |
6729 | 576k | } |
6730 | | |
6731 | 574k | static tag_sizes sr_parseTagTriple(StreamReader &sr) { |
6732 | 574k | tag_sizes ts; |
6733 | | |
6734 | 574k | sr.skip_space(); |
6735 | 574k | ts.num_ints = sr_parseIntNoSkip(sr); |
6736 | 574k | sr_skipTagField(sr); |
6737 | 574k | if (!sr.eof() && sr.peek() == '/') { |
6738 | 1.71k | sr.advance(1); |
6739 | 1.71k | sr.skip_space(); |
6740 | 1.71k | ts.num_reals = sr_parseIntNoSkip(sr); |
6741 | 1.71k | sr_skipTagField(sr); |
6742 | 1.71k | if (!sr.eof() && sr.peek() == '/') { |
6743 | 767 | sr.advance(1); |
6744 | 767 | ts.num_strings = sr_parseInt(sr); |
6745 | 767 | } |
6746 | 1.71k | } |
6747 | 574k | return ts; |
6748 | 574k | } |
6749 | | |
6750 | 765k | static inline int sr_parseIntNoSkip(StreamReader &sr) { |
6751 | 765k | const char *start = sr.current_ptr(); |
6752 | 765k | size_t rem = sr.remaining(); |
6753 | 765k | size_t len = 0; |
6754 | 765k | if (len < rem && (start[len] == '+' || start[len] == '-')) len++; |
6755 | 974k | while (len < rem && start[len] >= '0' && start[len] <= '9') len++; |
6756 | 765k | int i = 0; |
6757 | 765k | if (len > 0) { |
6758 | 191k | char tmp[64]; |
6759 | 191k | size_t copy_len = len < 63 ? len : 63; |
6760 | 191k | if (copy_len != len) { |
6761 | 0 | sr.advance(len); |
6762 | 0 | return 0; |
6763 | 0 | } |
6764 | 191k | memcpy(tmp, start, copy_len); |
6765 | 191k | tmp[copy_len] = '\0'; |
6766 | 191k | errno = 0; |
6767 | 191k | char *endptr = NULL; |
6768 | 191k | long val = strtol(tmp, &endptr, 10); |
6769 | 191k | if (errno == 0 && endptr != tmp && *endptr == '\0' && |
6770 | 191k | val <= (std::numeric_limits<int>::max)() && |
6771 | 191k | val >= (std::numeric_limits<int>::min)()) { |
6772 | 191k | i = static_cast<int>(val); |
6773 | 191k | } |
6774 | 191k | } |
6775 | 765k | sr.advance(len); |
6776 | 765k | return i; |
6777 | 765k | } |
6778 | | |
6779 | 188k | static inline void sr_skipUntil(StreamReader &sr, const char *delims) { |
6780 | 284k | while (!sr.eof()) { |
6781 | 284k | char c = sr.peek(); |
6782 | 1.14M | for (const char *d = delims; *d; d++) { |
6783 | 1.05M | if (c == *d) return; |
6784 | 1.05M | } |
6785 | 96.0k | sr.advance(1); |
6786 | 96.0k | } |
6787 | 188k | } |
6788 | | |
6789 | | static bool sr_parseTriple(StreamReader &sr, int vsize, int vnsize, int vtsize, |
6790 | 186k | vertex_index_t *ret, const warning_context &context) { |
6791 | 186k | if (!ret) return false; |
6792 | | |
6793 | 186k | vertex_index_t vi(-1); |
6794 | | |
6795 | 186k | sr.skip_space(); |
6796 | 186k | if (!fixIndex(sr_parseIntNoSkip(sr), vsize, &vi.v_idx, false, context)) { |
6797 | 399 | return false; |
6798 | 399 | } |
6799 | | |
6800 | 186k | sr_skipUntil(sr, "/ \t\r\n"); |
6801 | 186k | if (sr.eof() || sr.peek() != '/') { |
6802 | 184k | (*ret) = vi; |
6803 | 184k | return true; |
6804 | 184k | } |
6805 | 1.89k | sr.advance(1); |
6806 | | |
6807 | | // i//k |
6808 | 1.89k | if (!sr.eof() && sr.peek() == '/') { |
6809 | 318 | sr.advance(1); |
6810 | 318 | if (!fixIndex(sr_parseIntNoSkip(sr), vnsize, &vi.vn_idx, true, context)) { |
6811 | 1 | return false; |
6812 | 1 | } |
6813 | 317 | sr_skipUntil(sr, "/ \t\r\n"); |
6814 | 317 | (*ret) = vi; |
6815 | 317 | return true; |
6816 | 318 | } |
6817 | | |
6818 | | // i/j/k or i/j |
6819 | 1.58k | if (!fixIndex(sr_parseIntNoSkip(sr), vtsize, &vi.vt_idx, true, context)) { |
6820 | 3 | return false; |
6821 | 3 | } |
6822 | | |
6823 | 1.57k | sr_skipUntil(sr, "/ \t\r\n"); |
6824 | 1.57k | if (sr.eof() || sr.peek() != '/') { |
6825 | 1.15k | (*ret) = vi; |
6826 | 1.15k | return true; |
6827 | 1.15k | } |
6828 | | |
6829 | | // i/j/k |
6830 | 426 | sr.advance(1); |
6831 | 426 | if (!fixIndex(sr_parseIntNoSkip(sr), vnsize, &vi.vn_idx, true, context)) { |
6832 | 1 | return false; |
6833 | 1 | } |
6834 | 425 | sr_skipUntil(sr, "/ \t\r\n"); |
6835 | | |
6836 | 425 | (*ret) = vi; |
6837 | 425 | return true; |
6838 | 426 | } |
6839 | | |
6840 | 0 | static vertex_index_t sr_parseRawTriple(StreamReader &sr) { |
6841 | 0 | vertex_index_t vi(static_cast<int>(0)); |
6842 | |
|
6843 | 0 | sr.skip_space(); |
6844 | 0 | vi.v_idx = sr_parseIntNoSkip(sr); |
6845 | 0 | sr_skipUntil(sr, "/ \t\r\n"); |
6846 | 0 | if (sr.eof() || sr.peek() != '/') return vi; |
6847 | 0 | sr.advance(1); |
6848 | | |
6849 | | // i//k |
6850 | 0 | if (!sr.eof() && sr.peek() == '/') { |
6851 | 0 | sr.advance(1); |
6852 | 0 | vi.vn_idx = sr_parseIntNoSkip(sr); |
6853 | 0 | sr_skipUntil(sr, "/ \t\r\n"); |
6854 | 0 | return vi; |
6855 | 0 | } |
6856 | | |
6857 | | // i/j/k or i/j |
6858 | 0 | vi.vt_idx = sr_parseIntNoSkip(sr); |
6859 | 0 | sr_skipUntil(sr, "/ \t\r\n"); |
6860 | 0 | if (sr.eof() || sr.peek() != '/') return vi; |
6861 | | |
6862 | 0 | sr.advance(1); |
6863 | 0 | vi.vn_idx = sr_parseIntNoSkip(sr); |
6864 | 0 | sr_skipUntil(sr, "/ \t\r\n"); |
6865 | 0 | return vi; |
6866 | 0 | } |
6867 | | |
6868 | | bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt, |
6869 | 4.90k | const char *linebuf) { |
6870 | | // @todo { write more robust lexer and parser. } |
6871 | 4.90k | bool found_texname = false; |
6872 | 4.90k | std::string texture_name; |
6873 | | |
6874 | 4.90k | const char *token = linebuf; // Assume line ends with NULL |
6875 | | |
6876 | 11.5k | while (!IS_NEW_LINE((*token))) { |
6877 | 6.64k | token += strspn(token, " \t"); // skip space |
6878 | 6.64k | if ((0 == strncmp(token, "-blendu", 7)) && IS_SPACE((token[7]))) { |
6879 | 407 | token += 8; |
6880 | 407 | texopt->blendu = parseOnOff(&token, /* default */ true); |
6881 | 6.24k | } else if ((0 == strncmp(token, "-blendv", 7)) && IS_SPACE((token[7]))) { |
6882 | 209 | token += 8; |
6883 | 209 | texopt->blendv = parseOnOff(&token, /* default */ true); |
6884 | 6.03k | } else if ((0 == strncmp(token, "-clamp", 6)) && IS_SPACE((token[6]))) { |
6885 | 238 | token += 7; |
6886 | 238 | texopt->clamp = parseOnOff(&token, /* default */ true); |
6887 | 5.79k | } else if ((0 == strncmp(token, "-boost", 6)) && IS_SPACE((token[6]))) { |
6888 | 145 | token += 7; |
6889 | 145 | texopt->sharpness = parseReal(&token, 1.0); |
6890 | 5.64k | } else if ((0 == strncmp(token, "-bm", 3)) && IS_SPACE((token[3]))) { |
6891 | 20 | token += 4; |
6892 | 20 | texopt->bump_multiplier = parseReal(&token, 1.0); |
6893 | 5.62k | } else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) { |
6894 | 5 | token += 3; |
6895 | 5 | parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]), |
6896 | 5 | &(texopt->origin_offset[2]), &token); |
6897 | 5.62k | } else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) { |
6898 | 373 | token += 3; |
6899 | 373 | parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]), |
6900 | 373 | &token, 1.0, 1.0, 1.0); |
6901 | 5.25k | } else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) { |
6902 | 41 | token += 3; |
6903 | 41 | parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]), |
6904 | 41 | &(texopt->turbulence[2]), &token); |
6905 | 5.21k | } else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) { |
6906 | 46 | token += 5; |
6907 | 46 | texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE); |
6908 | 5.16k | } else if ((0 == strncmp(token, "-texres", 7)) && IS_SPACE((token[7]))) { |
6909 | 223 | token += 7; |
6910 | | // TODO(syoyo): Check if arg is int type. |
6911 | 223 | texopt->texture_resolution = parseInt(&token); |
6912 | 4.94k | } else if ((0 == strncmp(token, "-imfchan", 8)) && IS_SPACE((token[8]))) { |
6913 | 94 | token += 9; |
6914 | 94 | token += strspn(token, " \t"); |
6915 | 94 | const char *end = token + strcspn(token, " \t\r"); |
6916 | 94 | if ((end - token) == 1) { // Assume one char for -imfchan |
6917 | 77 | texopt->imfchan = (*token); |
6918 | 77 | } |
6919 | 94 | token = end; |
6920 | 4.84k | } else if ((0 == strncmp(token, "-mm", 3)) && IS_SPACE((token[3]))) { |
6921 | 268 | token += 4; |
6922 | 268 | parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); |
6923 | 4.57k | } else if ((0 == strncmp(token, "-colorspace", 11)) && |
6924 | 425 | IS_SPACE((token[11]))) { |
6925 | 264 | token += 12; |
6926 | 264 | texopt->colorspace = parseString(&token); |
6927 | 4.31k | } else { |
6928 | | // Assume texture filename |
6929 | | #if 0 |
6930 | | size_t len = strcspn(token, " \t\r"); // untile next space |
6931 | | texture_name = std::string(token, token + len); |
6932 | | token += len; |
6933 | | |
6934 | | token += strspn(token, " \t"); // skip space |
6935 | | #else |
6936 | | // Read filename until line end to parse filename containing whitespace |
6937 | | // TODO(syoyo): Support parsing texture option flag after the filename. |
6938 | 4.31k | texture_name = std::string(token); |
6939 | 4.31k | token += texture_name.length(); |
6940 | 4.31k | #endif |
6941 | | |
6942 | 4.31k | found_texname = true; |
6943 | 4.31k | } |
6944 | 6.64k | } |
6945 | | |
6946 | 4.90k | if (found_texname) { |
6947 | 4.31k | (*texname) = texture_name; |
6948 | 4.31k | return true; |
6949 | 4.31k | } else { |
6950 | 588 | return false; |
6951 | 588 | } |
6952 | 4.90k | } |
6953 | | |
6954 | 557k | static void InitTexOpt(texture_option_t *texopt, const bool is_bump) { |
6955 | 557k | if (is_bump) { |
6956 | 42.8k | texopt->imfchan = 'l'; |
6957 | 514k | } else { |
6958 | 514k | texopt->imfchan = 'm'; |
6959 | 514k | } |
6960 | 557k | texopt->bump_multiplier = static_cast<real_t>(1.0); |
6961 | 557k | texopt->clamp = false; |
6962 | 557k | texopt->blendu = true; |
6963 | 557k | texopt->blendv = true; |
6964 | 557k | texopt->sharpness = static_cast<real_t>(1.0); |
6965 | 557k | texopt->brightness = static_cast<real_t>(0.0); |
6966 | 557k | texopt->contrast = static_cast<real_t>(1.0); |
6967 | 557k | texopt->origin_offset[0] = static_cast<real_t>(0.0); |
6968 | 557k | texopt->origin_offset[1] = static_cast<real_t>(0.0); |
6969 | 557k | texopt->origin_offset[2] = static_cast<real_t>(0.0); |
6970 | 557k | texopt->scale[0] = static_cast<real_t>(1.0); |
6971 | 557k | texopt->scale[1] = static_cast<real_t>(1.0); |
6972 | 557k | texopt->scale[2] = static_cast<real_t>(1.0); |
6973 | 557k | texopt->turbulence[0] = static_cast<real_t>(0.0); |
6974 | 557k | texopt->turbulence[1] = static_cast<real_t>(0.0); |
6975 | 557k | texopt->turbulence[2] = static_cast<real_t>(0.0); |
6976 | 557k | texopt->texture_resolution = -1; |
6977 | 557k | texopt->type = TEXTURE_TYPE_NONE; |
6978 | 557k | } |
6979 | | |
6980 | 42.8k | static void InitMaterial(material_t *material) { |
6981 | 42.8k | InitTexOpt(&material->ambient_texopt, /* is_bump */ false); |
6982 | 42.8k | InitTexOpt(&material->diffuse_texopt, /* is_bump */ false); |
6983 | 42.8k | InitTexOpt(&material->specular_texopt, /* is_bump */ false); |
6984 | 42.8k | InitTexOpt(&material->specular_highlight_texopt, /* is_bump */ false); |
6985 | 42.8k | InitTexOpt(&material->bump_texopt, /* is_bump */ true); |
6986 | 42.8k | InitTexOpt(&material->displacement_texopt, /* is_bump */ false); |
6987 | 42.8k | InitTexOpt(&material->alpha_texopt, /* is_bump */ false); |
6988 | 42.8k | InitTexOpt(&material->reflection_texopt, /* is_bump */ false); |
6989 | 42.8k | InitTexOpt(&material->roughness_texopt, /* is_bump */ false); |
6990 | 42.8k | InitTexOpt(&material->metallic_texopt, /* is_bump */ false); |
6991 | 42.8k | InitTexOpt(&material->sheen_texopt, /* is_bump */ false); |
6992 | 42.8k | InitTexOpt(&material->emissive_texopt, /* is_bump */ false); |
6993 | 42.8k | InitTexOpt(&material->normal_texopt, |
6994 | 42.8k | /* is_bump */ false); // @fixme { is_bump will be true? } |
6995 | 42.8k | material->name = ""; |
6996 | 42.8k | material->ambient_texname = ""; |
6997 | 42.8k | material->diffuse_texname = ""; |
6998 | 42.8k | material->specular_texname = ""; |
6999 | 42.8k | material->specular_highlight_texname = ""; |
7000 | 42.8k | material->bump_texname = ""; |
7001 | 42.8k | material->displacement_texname = ""; |
7002 | 42.8k | material->reflection_texname = ""; |
7003 | 42.8k | material->alpha_texname = ""; |
7004 | 171k | for (int i = 0; i < 3; i++) { |
7005 | 128k | material->ambient[i] = static_cast<real_t>(0.0); |
7006 | 128k | material->diffuse[i] = static_cast<real_t>(0.0); |
7007 | 128k | material->specular[i] = static_cast<real_t>(0.0); |
7008 | 128k | material->transmittance[i] = static_cast<real_t>(0.0); |
7009 | 128k | material->emission[i] = static_cast<real_t>(0.0); |
7010 | 128k | } |
7011 | 42.8k | material->illum = 0; |
7012 | 42.8k | material->dissolve = static_cast<real_t>(1.0); |
7013 | 42.8k | material->shininess = static_cast<real_t>(1.0); |
7014 | 42.8k | material->ior = static_cast<real_t>(1.0); |
7015 | | |
7016 | 42.8k | material->roughness = static_cast<real_t>(0.0); |
7017 | 42.8k | material->metallic = static_cast<real_t>(0.0); |
7018 | 42.8k | material->sheen = static_cast<real_t>(0.0); |
7019 | 42.8k | material->clearcoat_thickness = static_cast<real_t>(0.0); |
7020 | 42.8k | material->clearcoat_roughness = static_cast<real_t>(0.0); |
7021 | 42.8k | material->anisotropy_rotation = static_cast<real_t>(0.0); |
7022 | 42.8k | material->anisotropy = static_cast<real_t>(0.0); |
7023 | 42.8k | material->roughness_texname = ""; |
7024 | 42.8k | material->metallic_texname = ""; |
7025 | 42.8k | material->sheen_texname = ""; |
7026 | 42.8k | material->emissive_texname = ""; |
7027 | 42.8k | material->normal_texname = ""; |
7028 | | |
7029 | 42.8k | material->unknown_parameter.clear(); |
7030 | 42.8k | } |
7031 | | |
7032 | | // code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html |
7033 | | template <typename T> |
7034 | 25.5M | static int pnpoly(int nvert, T *vertx, T *verty, T testx, T testy) { |
7035 | 25.5M | int i, j, c = 0; |
7036 | 102M | for (i = 0, j = nvert - 1; i < nvert; j = i++) { |
7037 | 76.6M | if (((verty[i] > testy) != (verty[j] > testy)) && |
7038 | 5.01k | (testx < |
7039 | 5.01k | (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + |
7040 | 5.01k | vertx[i])) |
7041 | 1.31k | c = !c; |
7042 | 76.6M | } |
7043 | 25.5M | return c; |
7044 | 25.5M | } |
7045 | | |
7046 | | struct TinyObjPoint { |
7047 | | real_t x, y, z; |
7048 | 0 | TinyObjPoint() : x(0), y(0), z(0) {} |
7049 | 0 | TinyObjPoint(real_t x_, real_t y_, real_t z_) : x(x_), y(y_), z(z_) {} |
7050 | | }; |
7051 | | |
7052 | 0 | inline TinyObjPoint cross(const TinyObjPoint &v1, const TinyObjPoint &v2) { |
7053 | 0 | return TinyObjPoint(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, |
7054 | 0 | v1.x * v2.y - v1.y * v2.x); |
7055 | 0 | } |
7056 | | |
7057 | 0 | inline real_t dot(const TinyObjPoint &v1, const TinyObjPoint &v2) { |
7058 | 0 | return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z); |
7059 | 0 | } |
7060 | | |
7061 | 0 | inline real_t GetLength(TinyObjPoint &e) { |
7062 | 0 | return std::sqrt(e.x * e.x + e.y * e.y + e.z * e.z); |
7063 | 0 | } |
7064 | | |
7065 | 0 | inline TinyObjPoint Normalize(TinyObjPoint e) { |
7066 | 0 | real_t len = GetLength(e); |
7067 | 0 | if (len <= real_t(0)) return TinyObjPoint(real_t(0), real_t(0), real_t(0)); |
7068 | 0 | real_t inv_length = real_t(1) / len; |
7069 | 0 | return TinyObjPoint(e.x * inv_length, e.y * inv_length, e.z * inv_length); |
7070 | 0 | } |
7071 | | |
7072 | | inline TinyObjPoint WorldToLocal(const TinyObjPoint &a, const TinyObjPoint &u, |
7073 | 0 | const TinyObjPoint &v, const TinyObjPoint &w) { |
7074 | 0 | return TinyObjPoint(dot(a, u), dot(a, v), dot(a, w)); |
7075 | 0 | } |
7076 | | |
7077 | | // TODO(syoyo): refactor function. |
7078 | | static bool exportGroupsToShape(shape_t *shape, const PrimGroup &prim_group, |
7079 | | const std::vector<tag_t> &tags, |
7080 | | const int material_id, const std::string &name, |
7081 | | bool triangulate, const std::vector<real_t> &v, |
7082 | 19.7k | std::string *warn) { |
7083 | 19.7k | if (prim_group.IsEmpty()) { |
7084 | 10.1k | return false; |
7085 | 10.1k | } |
7086 | | |
7087 | 9.52k | shape->name = name; |
7088 | | |
7089 | | // polygon |
7090 | 9.52k | if (!prim_group.faceGroup.empty()) { |
7091 | | // Flatten vertices and indices |
7092 | 460k | for (size_t i = 0; i < prim_group.faceGroup.size(); i++) { |
7093 | 457k | const face_t &face = prim_group.faceGroup[i]; |
7094 | | |
7095 | 457k | size_t npolys = face.vertex_indices.size(); |
7096 | | |
7097 | 457k | if (npolys < 3) { |
7098 | | // Face must have 3+ vertices. |
7099 | 452k | if (warn) { |
7100 | 452k | (*warn) += "Degenerated face found\n."; |
7101 | 452k | } |
7102 | 452k | continue; |
7103 | 452k | } |
7104 | | |
7105 | 4.71k | if (triangulate && npolys != 3) { |
7106 | 3.43k | if (npolys == 4) { |
7107 | 1.97k | vertex_index_t i0 = face.vertex_indices[0]; |
7108 | 1.97k | vertex_index_t i1 = face.vertex_indices[1]; |
7109 | 1.97k | vertex_index_t i2 = face.vertex_indices[2]; |
7110 | 1.97k | vertex_index_t i3 = face.vertex_indices[3]; |
7111 | | |
7112 | 1.97k | if (i0.v_idx < 0 || i1.v_idx < 0 || i2.v_idx < 0 || i3.v_idx < 0) { |
7113 | 0 | if (warn) { |
7114 | 0 | (*warn) += "Face with invalid vertex index found.\n"; |
7115 | 0 | } |
7116 | 0 | continue; |
7117 | 0 | } |
7118 | | |
7119 | 1.97k | size_t vi0 = size_t(i0.v_idx); |
7120 | 1.97k | size_t vi1 = size_t(i1.v_idx); |
7121 | 1.97k | size_t vi2 = size_t(i2.v_idx); |
7122 | 1.97k | size_t vi3 = size_t(i3.v_idx); |
7123 | | |
7124 | 1.97k | if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) || |
7125 | 1.31k | ((3 * vi2 + 2) >= v.size()) || ((3 * vi3 + 2) >= v.size())) { |
7126 | | // Invalid triangle. |
7127 | | // FIXME(syoyo): Is it ok to simply skip this invalid triangle? |
7128 | 1.25k | if (warn) { |
7129 | 1.25k | (*warn) += "Face with invalid vertex index found.\n"; |
7130 | 1.25k | } |
7131 | 1.25k | continue; |
7132 | 1.25k | } |
7133 | | |
7134 | 726 | real_t v0x = v[vi0 * 3 + 0]; |
7135 | 726 | real_t v0y = v[vi0 * 3 + 1]; |
7136 | 726 | real_t v0z = v[vi0 * 3 + 2]; |
7137 | 726 | real_t v1x = v[vi1 * 3 + 0]; |
7138 | 726 | real_t v1y = v[vi1 * 3 + 1]; |
7139 | 726 | real_t v1z = v[vi1 * 3 + 2]; |
7140 | 726 | real_t v2x = v[vi2 * 3 + 0]; |
7141 | 726 | real_t v2y = v[vi2 * 3 + 1]; |
7142 | 726 | real_t v2z = v[vi2 * 3 + 2]; |
7143 | 726 | real_t v3x = v[vi3 * 3 + 0]; |
7144 | 726 | real_t v3y = v[vi3 * 3 + 1]; |
7145 | 726 | real_t v3z = v[vi3 * 3 + 2]; |
7146 | | |
7147 | | // There are two candidates to split the quad into two triangles. |
7148 | | // |
7149 | | // Choose the shortest edge. |
7150 | | // TODO: Is it better to determine the edge to split by calculating |
7151 | | // the area of each triangle? |
7152 | | // |
7153 | | // +---+ |
7154 | | // |\ | |
7155 | | // | \ | |
7156 | | // | \| |
7157 | | // +---+ |
7158 | | // |
7159 | | // +---+ |
7160 | | // | /| |
7161 | | // | / | |
7162 | | // |/ | |
7163 | | // +---+ |
7164 | | |
7165 | 726 | real_t e02x = v2x - v0x; |
7166 | 726 | real_t e02y = v2y - v0y; |
7167 | 726 | real_t e02z = v2z - v0z; |
7168 | 726 | real_t e13x = v3x - v1x; |
7169 | 726 | real_t e13y = v3y - v1y; |
7170 | 726 | real_t e13z = v3z - v1z; |
7171 | | |
7172 | 726 | real_t sqr02 = e02x * e02x + e02y * e02y + e02z * e02z; |
7173 | 726 | real_t sqr13 = e13x * e13x + e13y * e13y + e13z * e13z; |
7174 | | |
7175 | 726 | index_t idx0, idx1, idx2, idx3; |
7176 | | |
7177 | 726 | idx0.vertex_index = i0.v_idx; |
7178 | 726 | idx0.normal_index = i0.vn_idx; |
7179 | 726 | idx0.texcoord_index = i0.vt_idx; |
7180 | 726 | idx1.vertex_index = i1.v_idx; |
7181 | 726 | idx1.normal_index = i1.vn_idx; |
7182 | 726 | idx1.texcoord_index = i1.vt_idx; |
7183 | 726 | idx2.vertex_index = i2.v_idx; |
7184 | 726 | idx2.normal_index = i2.vn_idx; |
7185 | 726 | idx2.texcoord_index = i2.vt_idx; |
7186 | 726 | idx3.vertex_index = i3.v_idx; |
7187 | 726 | idx3.normal_index = i3.vn_idx; |
7188 | 726 | idx3.texcoord_index = i3.vt_idx; |
7189 | | |
7190 | 726 | if (sqr02 < sqr13) { |
7191 | | // [0, 1, 2], [0, 2, 3] |
7192 | 158 | shape->mesh.indices.push_back(idx0); |
7193 | 158 | shape->mesh.indices.push_back(idx1); |
7194 | 158 | shape->mesh.indices.push_back(idx2); |
7195 | | |
7196 | 158 | shape->mesh.indices.push_back(idx0); |
7197 | 158 | shape->mesh.indices.push_back(idx2); |
7198 | 158 | shape->mesh.indices.push_back(idx3); |
7199 | 568 | } else { |
7200 | | // [0, 1, 3], [1, 2, 3] |
7201 | 568 | shape->mesh.indices.push_back(idx0); |
7202 | 568 | shape->mesh.indices.push_back(idx1); |
7203 | 568 | shape->mesh.indices.push_back(idx3); |
7204 | | |
7205 | 568 | shape->mesh.indices.push_back(idx1); |
7206 | 568 | shape->mesh.indices.push_back(idx2); |
7207 | 568 | shape->mesh.indices.push_back(idx3); |
7208 | 568 | } |
7209 | | |
7210 | | // Two triangle faces |
7211 | 726 | shape->mesh.num_face_vertices.push_back(3); |
7212 | 726 | shape->mesh.num_face_vertices.push_back(3); |
7213 | | |
7214 | 726 | shape->mesh.material_ids.push_back(material_id); |
7215 | 726 | shape->mesh.material_ids.push_back(material_id); |
7216 | | |
7217 | 726 | shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); |
7218 | 726 | shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); |
7219 | | |
7220 | 1.45k | } else { |
7221 | | #ifdef TINYOBJLOADER_USE_MAPBOX_EARCUT |
7222 | | // Validate all vertex indices before accessing the vertex array. |
7223 | | { |
7224 | | bool valid_poly = true; |
7225 | | for (size_t k = 0; k < npolys; ++k) { |
7226 | | size_t vi = size_t(face.vertex_indices[k].v_idx); |
7227 | | if ((3 * vi + 2) >= v.size()) { |
7228 | | valid_poly = false; |
7229 | | break; |
7230 | | } |
7231 | | } |
7232 | | if (!valid_poly) { |
7233 | | if (warn) { |
7234 | | (*warn) += "Face with invalid vertex index found.\n"; |
7235 | | } |
7236 | | continue; |
7237 | | } |
7238 | | } |
7239 | | |
7240 | | vertex_index_t i0 = face.vertex_indices[0]; |
7241 | | vertex_index_t i0_2 = i0; |
7242 | | |
7243 | | // TMW change: Find the normal axis of the polygon using Newell's |
7244 | | // method |
7245 | | TinyObjPoint n; |
7246 | | for (size_t k = 0; k < npolys; ++k) { |
7247 | | i0 = face.vertex_indices[k % npolys]; |
7248 | | size_t vi0 = size_t(i0.v_idx); |
7249 | | |
7250 | | size_t j = (k + 1) % npolys; |
7251 | | i0_2 = face.vertex_indices[j]; |
7252 | | size_t vi0_2 = size_t(i0_2.v_idx); |
7253 | | |
7254 | | real_t v0x = v[vi0 * 3 + 0]; |
7255 | | real_t v0y = v[vi0 * 3 + 1]; |
7256 | | real_t v0z = v[vi0 * 3 + 2]; |
7257 | | |
7258 | | real_t v0x_2 = v[vi0_2 * 3 + 0]; |
7259 | | real_t v0y_2 = v[vi0_2 * 3 + 1]; |
7260 | | real_t v0z_2 = v[vi0_2 * 3 + 2]; |
7261 | | |
7262 | | const TinyObjPoint point1(v0x, v0y, v0z); |
7263 | | const TinyObjPoint point2(v0x_2, v0y_2, v0z_2); |
7264 | | |
7265 | | TinyObjPoint a(point1.x - point2.x, point1.y - point2.y, |
7266 | | point1.z - point2.z); |
7267 | | TinyObjPoint b(point1.x + point2.x, point1.y + point2.y, |
7268 | | point1.z + point2.z); |
7269 | | |
7270 | | n.x += (a.y * b.z); |
7271 | | n.y += (a.z * b.x); |
7272 | | n.z += (a.x * b.y); |
7273 | | } |
7274 | | real_t length_n = GetLength(n); |
7275 | | // Check if zero length normal |
7276 | | if (length_n <= 0) { |
7277 | | continue; |
7278 | | } |
7279 | | // Negative is to flip the normal to the correct direction |
7280 | | real_t inv_length = -real_t(1.0) / length_n; |
7281 | | n.x *= inv_length; |
7282 | | n.y *= inv_length; |
7283 | | n.z *= inv_length; |
7284 | | |
7285 | | TinyObjPoint axis_w, axis_v, axis_u; |
7286 | | axis_w = n; |
7287 | | TinyObjPoint a; |
7288 | | if (std::fabs(axis_w.x) > real_t(0.9999999)) { |
7289 | | a = TinyObjPoint(0, 1, 0); |
7290 | | } else { |
7291 | | a = TinyObjPoint(1, 0, 0); |
7292 | | } |
7293 | | axis_v = Normalize(cross(axis_w, a)); |
7294 | | axis_u = cross(axis_w, axis_v); |
7295 | | using Point = std::array<real_t, 2>; |
7296 | | |
7297 | | // first polyline define the main polygon. |
7298 | | // following polylines define holes(not used in tinyobj). |
7299 | | std::vector<std::vector<Point> > polygon; |
7300 | | |
7301 | | std::vector<Point> polyline; |
7302 | | |
7303 | | // TMW change: Find best normal and project v0x and v0y to those |
7304 | | // coordinates, instead of picking a plane aligned with an axis (which |
7305 | | // can flip polygons). |
7306 | | |
7307 | | // Fill polygon data(facevarying vertices). |
7308 | | for (size_t k = 0; k < npolys; k++) { |
7309 | | i0 = face.vertex_indices[k]; |
7310 | | size_t vi0 = size_t(i0.v_idx); |
7311 | | |
7312 | | assert(((3 * vi0 + 2) < v.size())); |
7313 | | |
7314 | | real_t v0x = v[vi0 * 3 + 0]; |
7315 | | real_t v0y = v[vi0 * 3 + 1]; |
7316 | | real_t v0z = v[vi0 * 3 + 2]; |
7317 | | |
7318 | | TinyObjPoint polypoint(v0x, v0y, v0z); |
7319 | | TinyObjPoint loc = WorldToLocal(polypoint, axis_u, axis_v, axis_w); |
7320 | | |
7321 | | polyline.push_back({loc.x, loc.y}); |
7322 | | } |
7323 | | |
7324 | | polygon.push_back(polyline); |
7325 | | std::vector<uint32_t> indices = mapbox::earcut<uint32_t>(polygon); |
7326 | | // => result = 3 * faces, clockwise |
7327 | | |
7328 | | assert(indices.size() % 3 == 0); |
7329 | | |
7330 | | // Reconstruct vertex_index_t |
7331 | | for (size_t k = 0; k < indices.size() / 3; k++) { |
7332 | | { |
7333 | | index_t idx0, idx1, idx2; |
7334 | | idx0.vertex_index = face.vertex_indices[indices[3 * k + 0]].v_idx; |
7335 | | idx0.normal_index = |
7336 | | face.vertex_indices[indices[3 * k + 0]].vn_idx; |
7337 | | idx0.texcoord_index = |
7338 | | face.vertex_indices[indices[3 * k + 0]].vt_idx; |
7339 | | idx1.vertex_index = face.vertex_indices[indices[3 * k + 1]].v_idx; |
7340 | | idx1.normal_index = |
7341 | | face.vertex_indices[indices[3 * k + 1]].vn_idx; |
7342 | | idx1.texcoord_index = |
7343 | | face.vertex_indices[indices[3 * k + 1]].vt_idx; |
7344 | | idx2.vertex_index = face.vertex_indices[indices[3 * k + 2]].v_idx; |
7345 | | idx2.normal_index = |
7346 | | face.vertex_indices[indices[3 * k + 2]].vn_idx; |
7347 | | idx2.texcoord_index = |
7348 | | face.vertex_indices[indices[3 * k + 2]].vt_idx; |
7349 | | |
7350 | | shape->mesh.indices.push_back(idx0); |
7351 | | shape->mesh.indices.push_back(idx1); |
7352 | | shape->mesh.indices.push_back(idx2); |
7353 | | |
7354 | | shape->mesh.num_face_vertices.push_back(3); |
7355 | | shape->mesh.material_ids.push_back(material_id); |
7356 | | shape->mesh.smoothing_group_ids.push_back( |
7357 | | face.smoothing_group_id); |
7358 | | } |
7359 | | } |
7360 | | |
7361 | | #else // Built-in ear clipping triangulation |
7362 | 1.45k | vertex_index_t i0 = face.vertex_indices[0]; |
7363 | 1.45k | vertex_index_t i1(-1); |
7364 | 1.45k | vertex_index_t i2 = face.vertex_indices[1]; |
7365 | | |
7366 | | // find the two axes to work in |
7367 | 1.45k | size_t axes[2] = {1, 2}; |
7368 | 121k | for (size_t k = 0; k < npolys; ++k) { |
7369 | 120k | i0 = face.vertex_indices[(k + 0) % npolys]; |
7370 | 120k | i1 = face.vertex_indices[(k + 1) % npolys]; |
7371 | 120k | i2 = face.vertex_indices[(k + 2) % npolys]; |
7372 | 120k | size_t vi0 = size_t(i0.v_idx); |
7373 | 120k | size_t vi1 = size_t(i1.v_idx); |
7374 | 120k | size_t vi2 = size_t(i2.v_idx); |
7375 | | |
7376 | 120k | if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) || |
7377 | 119k | ((3 * vi2 + 2) >= v.size())) { |
7378 | | // Invalid triangle. |
7379 | | // FIXME(syoyo): Is it ok to simply skip this invalid triangle? |
7380 | 119k | continue; |
7381 | 119k | } |
7382 | 797 | real_t v0x = v[vi0 * 3 + 0]; |
7383 | 797 | real_t v0y = v[vi0 * 3 + 1]; |
7384 | 797 | real_t v0z = v[vi0 * 3 + 2]; |
7385 | 797 | real_t v1x = v[vi1 * 3 + 0]; |
7386 | 797 | real_t v1y = v[vi1 * 3 + 1]; |
7387 | 797 | real_t v1z = v[vi1 * 3 + 2]; |
7388 | 797 | real_t v2x = v[vi2 * 3 + 0]; |
7389 | 797 | real_t v2y = v[vi2 * 3 + 1]; |
7390 | 797 | real_t v2z = v[vi2 * 3 + 2]; |
7391 | 797 | real_t e0x = v1x - v0x; |
7392 | 797 | real_t e0y = v1y - v0y; |
7393 | 797 | real_t e0z = v1z - v0z; |
7394 | 797 | real_t e1x = v2x - v1x; |
7395 | 797 | real_t e1y = v2y - v1y; |
7396 | 797 | real_t e1z = v2z - v1z; |
7397 | 797 | real_t cx = std::fabs(e0y * e1z - e0z * e1y); |
7398 | 797 | real_t cy = std::fabs(e0z * e1x - e0x * e1z); |
7399 | 797 | real_t cz = std::fabs(e0x * e1y - e0y * e1x); |
7400 | 797 | const real_t epsilon = std::numeric_limits<real_t>::epsilon(); |
7401 | | // std::cout << "cx " << cx << ", cy " << cy << ", cz " << cz << |
7402 | | // "\n"; |
7403 | 797 | if (cx > epsilon || cy > epsilon || cz > epsilon) { |
7404 | | // std::cout << "corner\n"; |
7405 | | // found a corner |
7406 | 220 | if (cx > cy && cx > cz) { |
7407 | | // std::cout << "pattern0\n"; |
7408 | 215 | } else { |
7409 | | // std::cout << "axes[0] = 0\n"; |
7410 | 215 | axes[0] = 0; |
7411 | 215 | if (cz > cx && cz > cy) { |
7412 | | // std::cout << "axes[1] = 1\n"; |
7413 | 55 | axes[1] = 1; |
7414 | 55 | } |
7415 | 215 | } |
7416 | 220 | break; |
7417 | 220 | } |
7418 | 797 | } |
7419 | | |
7420 | 1.45k | face_t remainingFace = face; // copy |
7421 | 1.45k | size_t guess_vert = 0; |
7422 | 1.45k | vertex_index_t ind[3]; |
7423 | 1.45k | real_t vx[3]; |
7424 | 1.45k | real_t vy[3]; |
7425 | | |
7426 | | // How many iterations can we do without decreasing the remaining |
7427 | | // vertices. |
7428 | 1.45k | size_t remainingIterations = face.vertex_indices.size(); |
7429 | 1.45k | size_t previousRemainingVertices = |
7430 | 1.45k | remainingFace.vertex_indices.size(); |
7431 | | |
7432 | 119k | while (remainingFace.vertex_indices.size() > 3 && |
7433 | 118k | remainingIterations > 0) { |
7434 | | // std::cout << "remainingIterations " << remainingIterations << |
7435 | | // "\n"; |
7436 | | |
7437 | 118k | npolys = remainingFace.vertex_indices.size(); |
7438 | 118k | if (guess_vert >= npolys) { |
7439 | 104 | guess_vert -= npolys; |
7440 | 104 | } |
7441 | | |
7442 | 118k | if (previousRemainingVertices != npolys) { |
7443 | | // The number of remaining vertices decreased. Reset counters. |
7444 | 115k | previousRemainingVertices = npolys; |
7445 | 115k | remainingIterations = npolys; |
7446 | 115k | } else { |
7447 | | // We didn't consume a vertex on previous iteration, reduce the |
7448 | | // available iterations. |
7449 | 2.89k | remainingIterations--; |
7450 | 2.89k | } |
7451 | | |
7452 | 473k | for (size_t k = 0; k < 3; k++) { |
7453 | 354k | ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys]; |
7454 | 354k | size_t vi = size_t(ind[k].v_idx); |
7455 | 354k | if (((vi * 3 + axes[0]) >= v.size()) || |
7456 | 326k | ((vi * 3 + axes[1]) >= v.size())) { |
7457 | | // ??? |
7458 | 326k | vx[k] = static_cast<real_t>(0.0); |
7459 | 326k | vy[k] = static_cast<real_t>(0.0); |
7460 | 326k | } else { |
7461 | 28.2k | vx[k] = v[vi * 3 + axes[0]]; |
7462 | 28.2k | vy[k] = v[vi * 3 + axes[1]]; |
7463 | 28.2k | } |
7464 | 354k | } |
7465 | | |
7466 | | // |
7467 | | // area is calculated per face |
7468 | | // |
7469 | 118k | real_t e0x = vx[1] - vx[0]; |
7470 | 118k | real_t e0y = vy[1] - vy[0]; |
7471 | 118k | real_t e1x = vx[2] - vx[1]; |
7472 | 118k | real_t e1y = vy[2] - vy[1]; |
7473 | 118k | real_t cross = e0x * e1y - e0y * e1x; |
7474 | | // std::cout << "axes = " << axes[0] << ", " << axes[1] << "\n"; |
7475 | | // std::cout << "e0x, e0y, e1x, e1y " << e0x << ", " << e0y << ", " |
7476 | | // << e1x << ", " << e1y << "\n"; |
7477 | | |
7478 | 118k | real_t area = |
7479 | 118k | (vx[0] * vy[1] - vy[0] * vx[1]) * static_cast<real_t>(0.5); |
7480 | | // std::cout << "cross " << cross << ", area " << area << "\n"; |
7481 | | // if an internal angle |
7482 | 118k | if (cross * area < static_cast<real_t>(0.0)) { |
7483 | | // std::cout << "internal \n"; |
7484 | 1.16k | guess_vert += 1; |
7485 | | // std::cout << "guess vert : " << guess_vert << "\n"; |
7486 | 1.16k | continue; |
7487 | 1.16k | } |
7488 | | |
7489 | | // check all other verts in case they are inside this triangle |
7490 | 117k | bool overlap = false; |
7491 | 1.87G | for (size_t otherVert = 3; otherVert < npolys; ++otherVert) { |
7492 | 1.87G | size_t idx = (guess_vert + otherVert) % npolys; |
7493 | | |
7494 | 1.87G | if (idx >= remainingFace.vertex_indices.size()) { |
7495 | | // std::cout << "???0\n"; |
7496 | | // ??? |
7497 | 0 | continue; |
7498 | 0 | } |
7499 | | |
7500 | 1.87G | size_t ovi = size_t(remainingFace.vertex_indices[idx].v_idx); |
7501 | | |
7502 | 1.87G | if (((ovi * 3 + axes[0]) >= v.size()) || |
7503 | 1.84G | ((ovi * 3 + axes[1]) >= v.size())) { |
7504 | | // std::cout << "???1\n"; |
7505 | | // ??? |
7506 | 1.84G | continue; |
7507 | 1.84G | } |
7508 | 25.5M | real_t tx = v[ovi * 3 + axes[0]]; |
7509 | 25.5M | real_t ty = v[ovi * 3 + axes[1]]; |
7510 | 25.5M | if (pnpoly(3, vx, vy, tx, ty)) { |
7511 | | // std::cout << "overlap\n"; |
7512 | 360 | overlap = true; |
7513 | 360 | break; |
7514 | 360 | } |
7515 | 25.5M | } |
7516 | | |
7517 | 117k | if (overlap) { |
7518 | | // std::cout << "overlap2\n"; |
7519 | 360 | guess_vert += 1; |
7520 | 360 | continue; |
7521 | 360 | } |
7522 | | |
7523 | | // this triangle is an ear |
7524 | 116k | { |
7525 | 116k | index_t idx0, idx1, idx2; |
7526 | 116k | idx0.vertex_index = ind[0].v_idx; |
7527 | 116k | idx0.normal_index = ind[0].vn_idx; |
7528 | 116k | idx0.texcoord_index = ind[0].vt_idx; |
7529 | 116k | idx1.vertex_index = ind[1].v_idx; |
7530 | 116k | idx1.normal_index = ind[1].vn_idx; |
7531 | 116k | idx1.texcoord_index = ind[1].vt_idx; |
7532 | 116k | idx2.vertex_index = ind[2].v_idx; |
7533 | 116k | idx2.normal_index = ind[2].vn_idx; |
7534 | 116k | idx2.texcoord_index = ind[2].vt_idx; |
7535 | | |
7536 | 116k | shape->mesh.indices.push_back(idx0); |
7537 | 116k | shape->mesh.indices.push_back(idx1); |
7538 | 116k | shape->mesh.indices.push_back(idx2); |
7539 | | |
7540 | 116k | shape->mesh.num_face_vertices.push_back(3); |
7541 | 116k | shape->mesh.material_ids.push_back(material_id); |
7542 | 116k | shape->mesh.smoothing_group_ids.push_back( |
7543 | 116k | face.smoothing_group_id); |
7544 | 116k | } |
7545 | | |
7546 | | // remove v1 from the list |
7547 | 116k | size_t removed_vert_index = (guess_vert + 1) % npolys; |
7548 | 1.87G | while (removed_vert_index + 1 < npolys) { |
7549 | 1.87G | remainingFace.vertex_indices[removed_vert_index] = |
7550 | 1.87G | remainingFace.vertex_indices[removed_vert_index + 1]; |
7551 | 1.87G | removed_vert_index += 1; |
7552 | 1.87G | } |
7553 | 116k | remainingFace.vertex_indices.pop_back(); |
7554 | 116k | } |
7555 | | |
7556 | | // std::cout << "remainingFace.vi.size = " << |
7557 | | // remainingFace.vertex_indices.size() << "\n"; |
7558 | 1.45k | if (remainingFace.vertex_indices.size() == 3) { |
7559 | 1.37k | i0 = remainingFace.vertex_indices[0]; |
7560 | 1.37k | i1 = remainingFace.vertex_indices[1]; |
7561 | 1.37k | i2 = remainingFace.vertex_indices[2]; |
7562 | 1.37k | { |
7563 | 1.37k | index_t idx0, idx1, idx2; |
7564 | 1.37k | idx0.vertex_index = i0.v_idx; |
7565 | 1.37k | idx0.normal_index = i0.vn_idx; |
7566 | 1.37k | idx0.texcoord_index = i0.vt_idx; |
7567 | 1.37k | idx1.vertex_index = i1.v_idx; |
7568 | 1.37k | idx1.normal_index = i1.vn_idx; |
7569 | 1.37k | idx1.texcoord_index = i1.vt_idx; |
7570 | 1.37k | idx2.vertex_index = i2.v_idx; |
7571 | 1.37k | idx2.normal_index = i2.vn_idx; |
7572 | 1.37k | idx2.texcoord_index = i2.vt_idx; |
7573 | | |
7574 | 1.37k | shape->mesh.indices.push_back(idx0); |
7575 | 1.37k | shape->mesh.indices.push_back(idx1); |
7576 | 1.37k | shape->mesh.indices.push_back(idx2); |
7577 | | |
7578 | 1.37k | shape->mesh.num_face_vertices.push_back(3); |
7579 | 1.37k | shape->mesh.material_ids.push_back(material_id); |
7580 | 1.37k | shape->mesh.smoothing_group_ids.push_back( |
7581 | 1.37k | face.smoothing_group_id); |
7582 | 1.37k | } |
7583 | 1.37k | } |
7584 | 1.45k | #endif |
7585 | 1.45k | } // npolys |
7586 | 3.43k | } else { |
7587 | 5.12k | for (size_t k = 0; k < npolys; k++) { |
7588 | 3.84k | index_t idx; |
7589 | 3.84k | idx.vertex_index = face.vertex_indices[k].v_idx; |
7590 | 3.84k | idx.normal_index = face.vertex_indices[k].vn_idx; |
7591 | 3.84k | idx.texcoord_index = face.vertex_indices[k].vt_idx; |
7592 | 3.84k | shape->mesh.indices.push_back(idx); |
7593 | 3.84k | } |
7594 | | |
7595 | 1.28k | shape->mesh.num_face_vertices.push_back( |
7596 | 1.28k | static_cast<unsigned int>(npolys)); |
7597 | 1.28k | shape->mesh.material_ids.push_back(material_id); // per face |
7598 | 1.28k | shape->mesh.smoothing_group_ids.push_back( |
7599 | 1.28k | face.smoothing_group_id); // per face |
7600 | 1.28k | } |
7601 | 4.71k | } |
7602 | | |
7603 | 3.10k | shape->mesh.tags = tags; |
7604 | 3.10k | } |
7605 | | |
7606 | | // line |
7607 | 9.52k | if (!prim_group.lineGroup.empty()) { |
7608 | | // Flatten indices |
7609 | 196k | for (size_t i = 0; i < prim_group.lineGroup.size(); i++) { |
7610 | 197k | for (size_t j = 0; j < prim_group.lineGroup[i].vertex_indices.size(); |
7611 | 194k | j++) { |
7612 | 2.91k | const vertex_index_t &vi = prim_group.lineGroup[i].vertex_indices[j]; |
7613 | | |
7614 | 2.91k | index_t idx; |
7615 | 2.91k | idx.vertex_index = vi.v_idx; |
7616 | 2.91k | idx.normal_index = vi.vn_idx; |
7617 | 2.91k | idx.texcoord_index = vi.vt_idx; |
7618 | | |
7619 | 2.91k | shape->lines.indices.push_back(idx); |
7620 | 2.91k | } |
7621 | | |
7622 | 194k | shape->lines.num_line_vertices.push_back( |
7623 | 194k | int(prim_group.lineGroup[i].vertex_indices.size())); |
7624 | 194k | } |
7625 | 1.44k | } |
7626 | | |
7627 | | // points |
7628 | 9.52k | if (!prim_group.pointsGroup.empty()) { |
7629 | | // Flatten & convert indices |
7630 | 14.4k | for (size_t i = 0; i < prim_group.pointsGroup.size(); i++) { |
7631 | 44.3k | for (size_t j = 0; j < prim_group.pointsGroup[i].vertex_indices.size(); |
7632 | 35.5k | j++) { |
7633 | 35.5k | const vertex_index_t &vi = prim_group.pointsGroup[i].vertex_indices[j]; |
7634 | | |
7635 | 35.5k | index_t idx; |
7636 | 35.5k | idx.vertex_index = vi.v_idx; |
7637 | 35.5k | idx.normal_index = vi.vn_idx; |
7638 | 35.5k | idx.texcoord_index = vi.vt_idx; |
7639 | | |
7640 | 35.5k | shape->points.indices.push_back(idx); |
7641 | 35.5k | } |
7642 | 8.80k | } |
7643 | 5.66k | } |
7644 | | |
7645 | 9.52k | return true; |
7646 | 19.7k | } |
7647 | | |
7648 | | // Split a string with specified delimiter character and escape character. |
7649 | | // https://rosettacode.org/wiki/Tokenize_a_string_with_escaping#C.2B.2B |
7650 | | static void SplitString(const std::string &s, char delim, char escape, |
7651 | 8.25k | std::vector<std::string> &elems) { |
7652 | 8.25k | std::string token; |
7653 | | |
7654 | 8.25k | bool escaping = false; |
7655 | 18.0M | for (size_t i = 0; i < s.size(); ++i) { |
7656 | 18.0M | char ch = s[i]; |
7657 | 18.0M | if (escaping) { |
7658 | 19.3k | escaping = false; |
7659 | 18.0M | } else if (ch == escape) { |
7660 | 19.5k | if ((i + 1) < s.size()) { |
7661 | 19.4k | const char next = s[i + 1]; |
7662 | 19.4k | if ((next == delim) || (next == escape)) { |
7663 | 19.3k | escaping = true; |
7664 | 19.3k | continue; |
7665 | 19.3k | } |
7666 | 19.4k | } |
7667 | 18.0M | } else if (ch == delim) { |
7668 | 28.7k | if (!token.empty()) { |
7669 | 27.7k | elems.push_back(token); |
7670 | 27.7k | } |
7671 | 28.7k | token.clear(); |
7672 | 28.7k | continue; |
7673 | 28.7k | } |
7674 | 18.0M | token += ch; |
7675 | 18.0M | } |
7676 | | |
7677 | 8.25k | elems.push_back(token); |
7678 | 8.25k | } |
7679 | | |
7680 | 8.25k | static void RemoveEmptyTokens(std::vector<std::string> *tokens) { |
7681 | 8.25k | if (!tokens) return; |
7682 | | |
7683 | 8.25k | const std::vector<std::string> &src = *tokens; |
7684 | 8.25k | std::vector<std::string> filtered; |
7685 | 8.25k | filtered.reserve(src.size()); |
7686 | 44.2k | for (size_t i = 0; i < src.size(); i++) { |
7687 | 36.0k | if (!src[i].empty()) { |
7688 | 30.5k | filtered.push_back(src[i]); |
7689 | 30.5k | } |
7690 | 36.0k | } |
7691 | 8.25k | tokens->swap(filtered); |
7692 | 8.25k | } |
7693 | | |
7694 | | static std::string JoinPath(const std::string &dir, |
7695 | 0 | const std::string &filename) { |
7696 | 0 | if (dir.empty()) { |
7697 | 0 | return filename; |
7698 | 0 | } else { |
7699 | | // check '/' |
7700 | 0 | char lastChar = *dir.rbegin(); |
7701 | 0 | if (lastChar != '/') { |
7702 | 0 | return dir + std::string("/") + filename; |
7703 | 0 | } else { |
7704 | 0 | return dir + filename; |
7705 | 0 | } |
7706 | 0 | } |
7707 | 0 | } |
7708 | | |
7709 | | static bool LoadMtlInternal(std::map<std::string, int> *material_map, |
7710 | | std::vector<material_t> *materials, |
7711 | | StreamReader &sr, |
7712 | | std::string *warning, std::string *err, |
7713 | 1.60k | const std::string &filename = "<stream>") { |
7714 | 1.60k | if (sr.has_errors()) { |
7715 | 0 | if (err) { |
7716 | 0 | (*err) += sr.get_errors(); |
7717 | 0 | } |
7718 | 0 | return false; |
7719 | 0 | } |
7720 | | |
7721 | 1.60k | material_t material; |
7722 | 1.60k | InitMaterial(&material); |
7723 | | |
7724 | | // Issue 43. `d` wins against `Tr` since `Tr` is not in the MTL specification. |
7725 | 1.60k | bool has_d = false; |
7726 | 1.60k | bool has_tr = false; |
7727 | | |
7728 | | // has_kd is used to set a default diffuse value when map_Kd is present |
7729 | | // and Kd is not. |
7730 | 1.60k | bool has_kd = false; |
7731 | | |
7732 | 1.60k | std::stringstream warn_ss; |
7733 | | |
7734 | | // Handle BOM |
7735 | 1.60k | if (sr.remaining() >= 3 && |
7736 | 756 | static_cast<unsigned char>(sr.peek()) == 0xEF && |
7737 | 7 | static_cast<unsigned char>(sr.peek_at(1)) == 0xBB && |
7738 | 7 | static_cast<unsigned char>(sr.peek_at(2)) == 0xBF) { |
7739 | 7 | sr.advance(3); |
7740 | 7 | } |
7741 | | |
7742 | 207k | while (!sr.eof()) { |
7743 | 205k | sr.skip_space(); |
7744 | 205k | if (sr.at_line_end()) { sr.skip_line(); continue; } |
7745 | 165k | if (sr.peek() == '#') { sr.skip_line(); continue; } |
7746 | | |
7747 | 165k | size_t line_num = sr.line_num(); |
7748 | | |
7749 | | // new mtl |
7750 | 165k | if (sr.match("newmtl", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) { |
7751 | | // flush previous material. |
7752 | 41.2k | if (!material.name.empty()) { |
7753 | 40.9k | material_map->insert(std::pair<std::string, int>( |
7754 | 40.9k | material.name, static_cast<int>(materials->size()))); |
7755 | 40.9k | materials->push_back(material); |
7756 | 40.9k | } |
7757 | | |
7758 | 41.2k | InitMaterial(&material); |
7759 | | |
7760 | 41.2k | has_d = false; |
7761 | 41.2k | has_tr = false; |
7762 | 41.2k | has_kd = false; |
7763 | | |
7764 | 41.2k | sr.advance(7); |
7765 | 41.2k | { |
7766 | 41.2k | std::string namebuf = sr_parseString(sr); |
7767 | 41.2k | if (namebuf.empty()) { |
7768 | 98 | if (warning) { |
7769 | 98 | (*warning) += "empty material name in `newmtl`\n"; |
7770 | 98 | } |
7771 | 98 | } |
7772 | 41.2k | material.name = namebuf; |
7773 | 41.2k | } |
7774 | 41.2k | sr.skip_line(); |
7775 | 41.2k | continue; |
7776 | 41.2k | } |
7777 | | |
7778 | | // ambient |
7779 | 124k | if (sr.peek() == 'K' && sr.peek_at(1) == 'a' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
7780 | 6 | sr.advance(2); |
7781 | 6 | real_t r, g, b; |
7782 | 6 | if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false; |
7783 | 1 | material.ambient[0] = r; |
7784 | 1 | material.ambient[1] = g; |
7785 | 1 | material.ambient[2] = b; |
7786 | 1 | sr.skip_line(); |
7787 | 1 | continue; |
7788 | 6 | } |
7789 | | |
7790 | | // diffuse |
7791 | 124k | if (sr.peek() == 'K' && sr.peek_at(1) == 'd' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
7792 | 6 | sr.advance(2); |
7793 | 6 | real_t r, g, b; |
7794 | 6 | if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false; |
7795 | 1 | material.diffuse[0] = r; |
7796 | 1 | material.diffuse[1] = g; |
7797 | 1 | material.diffuse[2] = b; |
7798 | 1 | has_kd = true; |
7799 | 1 | sr.skip_line(); |
7800 | 1 | continue; |
7801 | 6 | } |
7802 | | |
7803 | | // specular |
7804 | 124k | if (sr.peek() == 'K' && sr.peek_at(1) == 's' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
7805 | 4 | sr.advance(2); |
7806 | 4 | real_t r, g, b; |
7807 | 4 | if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false; |
7808 | 0 | material.specular[0] = r; |
7809 | 0 | material.specular[1] = g; |
7810 | 0 | material.specular[2] = b; |
7811 | 0 | sr.skip_line(); |
7812 | 0 | continue; |
7813 | 4 | } |
7814 | | |
7815 | | // transmittance |
7816 | 124k | if ((sr.peek() == 'K' && sr.peek_at(1) == 't' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) || |
7817 | 124k | (sr.peek() == 'T' && sr.peek_at(1) == 'f' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t'))) { |
7818 | 10 | sr.advance(2); |
7819 | 10 | real_t r, g, b; |
7820 | 10 | if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false; |
7821 | 2 | material.transmittance[0] = r; |
7822 | 2 | material.transmittance[1] = g; |
7823 | 2 | material.transmittance[2] = b; |
7824 | 2 | sr.skip_line(); |
7825 | 2 | continue; |
7826 | 10 | } |
7827 | | |
7828 | | // ior(index of refraction) |
7829 | 124k | if (sr.peek() == 'N' && sr.peek_at(1) == 'i' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
7830 | 3 | sr.advance(2); |
7831 | 3 | if (!sr_parseReal(sr, &material.ior, 0.0, err, filename)) return false; |
7832 | 0 | sr.skip_line(); |
7833 | 0 | continue; |
7834 | 3 | } |
7835 | | |
7836 | | // emission |
7837 | 124k | if (sr.peek() == 'K' && sr.peek_at(1) == 'e' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
7838 | 14 | sr.advance(2); |
7839 | 14 | real_t r, g, b; |
7840 | 14 | if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false; |
7841 | 11 | material.emission[0] = r; |
7842 | 11 | material.emission[1] = g; |
7843 | 11 | material.emission[2] = b; |
7844 | 11 | sr.skip_line(); |
7845 | 11 | continue; |
7846 | 14 | } |
7847 | | |
7848 | | // shininess |
7849 | 124k | if (sr.peek() == 'N' && sr.peek_at(1) == 's' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
7850 | 6 | sr.advance(2); |
7851 | 6 | if (!sr_parseReal(sr, &material.shininess, 0.0, err, filename)) return false; |
7852 | 0 | sr.skip_line(); |
7853 | 0 | continue; |
7854 | 6 | } |
7855 | | |
7856 | | // illum model |
7857 | 124k | if (sr.match("illum", 5) && (sr.peek_at(5) == ' ' || sr.peek_at(5) == '\t')) { |
7858 | 6 | sr.advance(6); |
7859 | 6 | if (!sr_parseInt(sr, &material.illum, err, filename)) return false; |
7860 | 0 | sr.skip_line(); |
7861 | 0 | continue; |
7862 | 6 | } |
7863 | | |
7864 | | // dissolve |
7865 | 124k | if (sr.peek() == 'd' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) { |
7866 | 18 | sr.advance(1); |
7867 | 18 | if (!sr_parseReal(sr, &material.dissolve, 0.0, err, filename)) return false; |
7868 | | |
7869 | 4 | if (has_tr) { |
7870 | 0 | warn_ss << "Both `d` and `Tr` parameters defined for \"" |
7871 | 0 | << material.name |
7872 | 0 | << "\". Use the value of `d` for dissolve (line " << line_num |
7873 | 0 | << " in .mtl.)\n"; |
7874 | 0 | } |
7875 | 4 | has_d = true; |
7876 | 4 | sr.skip_line(); |
7877 | 4 | continue; |
7878 | 18 | } |
7879 | 124k | if (sr.peek() == 'T' && sr.peek_at(1) == 'r' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
7880 | 7 | sr.advance(2); |
7881 | 7 | if (has_d) { |
7882 | 0 | warn_ss << "Both `d` and `Tr` parameters defined for \"" |
7883 | 0 | << material.name |
7884 | 0 | << "\". Use the value of `d` for dissolve (line " << line_num |
7885 | 0 | << " in .mtl.)\n"; |
7886 | 7 | } else { |
7887 | 7 | real_t tr_val; |
7888 | 7 | if (!sr_parseReal(sr, &tr_val, 0.0, err, filename)) return false; |
7889 | 1 | material.dissolve = static_cast<real_t>(1.0) - tr_val; |
7890 | 1 | } |
7891 | 1 | has_tr = true; |
7892 | 1 | sr.skip_line(); |
7893 | 1 | continue; |
7894 | 7 | } |
7895 | | |
7896 | | // PBR: roughness |
7897 | 124k | if (sr.peek() == 'P' && sr.peek_at(1) == 'r' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
7898 | 6 | sr.advance(2); |
7899 | 6 | if (!sr_parseReal(sr, &material.roughness, 0.0, err, filename)) return false; |
7900 | 0 | sr.skip_line(); |
7901 | 0 | continue; |
7902 | 6 | } |
7903 | | |
7904 | | // PBR: metallic |
7905 | 124k | if (sr.peek() == 'P' && sr.peek_at(1) == 'm' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
7906 | 4 | sr.advance(2); |
7907 | 4 | if (!sr_parseReal(sr, &material.metallic, 0.0, err, filename)) return false; |
7908 | 0 | sr.skip_line(); |
7909 | 0 | continue; |
7910 | 4 | } |
7911 | | |
7912 | | // PBR: sheen |
7913 | 124k | if (sr.peek() == 'P' && sr.peek_at(1) == 's' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
7914 | 21 | sr.advance(2); |
7915 | 21 | if (!sr_parseReal(sr, &material.sheen, 0.0, err, filename)) return false; |
7916 | 15 | sr.skip_line(); |
7917 | 15 | continue; |
7918 | 21 | } |
7919 | | |
7920 | | // PBR: clearcoat thickness |
7921 | 124k | if (sr.peek() == 'P' && sr.peek_at(1) == 'c' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
7922 | 2 | sr.advance(2); |
7923 | 2 | if (!sr_parseReal(sr, &material.clearcoat_thickness, 0.0, err, filename)) return false; |
7924 | 0 | sr.skip_line(); |
7925 | 0 | continue; |
7926 | 2 | } |
7927 | | |
7928 | | // PBR: clearcoat roughness |
7929 | 124k | if (sr.match("Pcr", 3) && (sr.peek_at(3) == ' ' || sr.peek_at(3) == '\t')) { |
7930 | 6 | sr.advance(4); |
7931 | 6 | if (!sr_parseReal(sr, &material.clearcoat_roughness, 0.0, err, filename)) return false; |
7932 | 3 | sr.skip_line(); |
7933 | 3 | continue; |
7934 | 6 | } |
7935 | | |
7936 | | // PBR: anisotropy |
7937 | 124k | if (sr.match("aniso", 5) && (sr.peek_at(5) == ' ' || sr.peek_at(5) == '\t')) { |
7938 | 4 | sr.advance(6); |
7939 | 4 | if (!sr_parseReal(sr, &material.anisotropy, 0.0, err, filename)) return false; |
7940 | 0 | sr.skip_line(); |
7941 | 0 | continue; |
7942 | 4 | } |
7943 | | |
7944 | | // PBR: anisotropy rotation |
7945 | 124k | if (sr.match("anisor", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) { |
7946 | 5 | sr.advance(7); |
7947 | 5 | if (!sr_parseReal(sr, &material.anisotropy_rotation, 0.0, err, filename)) return false; |
7948 | 0 | sr.skip_line(); |
7949 | 0 | continue; |
7950 | 5 | } |
7951 | | |
7952 | | // For texture directives, read rest of line and delegate to |
7953 | | // ParseTextureNameAndOption (which uses the old const char* parse functions). |
7954 | | |
7955 | | // ambient or ambient occlusion texture |
7956 | 124k | if (sr.match("map_Ka", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) { |
7957 | 52 | sr.advance(7); |
7958 | 52 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
7959 | 52 | ParseTextureNameAndOption(&(material.ambient_texname), |
7960 | 52 | &(material.ambient_texopt), line_rest.c_str()); |
7961 | 52 | sr.skip_line(); |
7962 | 52 | continue; |
7963 | 52 | } |
7964 | | |
7965 | | // diffuse texture |
7966 | 124k | if (sr.match("map_Kd", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) { |
7967 | 770 | sr.advance(7); |
7968 | 770 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
7969 | 770 | ParseTextureNameAndOption(&(material.diffuse_texname), |
7970 | 770 | &(material.diffuse_texopt), line_rest.c_str()); |
7971 | 770 | if (!has_kd) { |
7972 | 769 | material.diffuse[0] = static_cast<real_t>(0.6); |
7973 | 769 | material.diffuse[1] = static_cast<real_t>(0.6); |
7974 | 769 | material.diffuse[2] = static_cast<real_t>(0.6); |
7975 | 769 | } |
7976 | 770 | sr.skip_line(); |
7977 | 770 | continue; |
7978 | 770 | } |
7979 | | |
7980 | | // specular texture |
7981 | 123k | if (sr.match("map_Ks", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) { |
7982 | 294 | sr.advance(7); |
7983 | 294 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
7984 | 294 | ParseTextureNameAndOption(&(material.specular_texname), |
7985 | 294 | &(material.specular_texopt), line_rest.c_str()); |
7986 | 294 | sr.skip_line(); |
7987 | 294 | continue; |
7988 | 294 | } |
7989 | | |
7990 | | // specular highlight texture |
7991 | 123k | if (sr.match("map_Ns", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) { |
7992 | 36 | sr.advance(7); |
7993 | 36 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
7994 | 36 | ParseTextureNameAndOption(&(material.specular_highlight_texname), |
7995 | 36 | &(material.specular_highlight_texopt), line_rest.c_str()); |
7996 | 36 | sr.skip_line(); |
7997 | 36 | continue; |
7998 | 36 | } |
7999 | | |
8000 | | // bump texture |
8001 | 122k | if ((sr.match("map_bump", 8) || sr.match("map_Bump", 8)) && |
8002 | 474 | (sr.peek_at(8) == ' ' || sr.peek_at(8) == '\t')) { |
8003 | 179 | sr.advance(9); |
8004 | 179 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
8005 | 179 | ParseTextureNameAndOption(&(material.bump_texname), |
8006 | 179 | &(material.bump_texopt), line_rest.c_str()); |
8007 | 179 | sr.skip_line(); |
8008 | 179 | continue; |
8009 | 179 | } |
8010 | | |
8011 | | // bump texture (short form) |
8012 | 122k | if (sr.match("bump", 4) && (sr.peek_at(4) == ' ' || sr.peek_at(4) == '\t')) { |
8013 | 463 | sr.advance(5); |
8014 | 463 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
8015 | 463 | ParseTextureNameAndOption(&(material.bump_texname), |
8016 | 463 | &(material.bump_texopt), line_rest.c_str()); |
8017 | 463 | sr.skip_line(); |
8018 | 463 | continue; |
8019 | 463 | } |
8020 | | |
8021 | | // alpha texture |
8022 | 122k | if (sr.match("map_d", 5) && (sr.peek_at(5) == ' ' || sr.peek_at(5) == '\t')) { |
8023 | 369 | sr.advance(6); |
8024 | 369 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
8025 | 369 | ParseTextureNameAndOption(&(material.alpha_texname), |
8026 | 369 | &(material.alpha_texopt), line_rest.c_str()); |
8027 | 369 | sr.skip_line(); |
8028 | 369 | continue; |
8029 | 369 | } |
8030 | | |
8031 | | // displacement texture |
8032 | 121k | if ((sr.match("map_disp", 8) || sr.match("map_Disp", 8)) && |
8033 | 300 | (sr.peek_at(8) == ' ' || sr.peek_at(8) == '\t')) { |
8034 | 237 | sr.advance(9); |
8035 | 237 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
8036 | 237 | ParseTextureNameAndOption(&(material.displacement_texname), |
8037 | 237 | &(material.displacement_texopt), line_rest.c_str()); |
8038 | 237 | sr.skip_line(); |
8039 | 237 | continue; |
8040 | 237 | } |
8041 | | |
8042 | | // displacement texture (short form) |
8043 | 121k | if (sr.match("disp", 4) && (sr.peek_at(4) == ' ' || sr.peek_at(4) == '\t')) { |
8044 | 539 | sr.advance(5); |
8045 | 539 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
8046 | 539 | ParseTextureNameAndOption(&(material.displacement_texname), |
8047 | 539 | &(material.displacement_texopt), line_rest.c_str()); |
8048 | 539 | sr.skip_line(); |
8049 | 539 | continue; |
8050 | 539 | } |
8051 | | |
8052 | | // reflection map |
8053 | 121k | if (sr.match("refl", 4) && (sr.peek_at(4) == ' ' || sr.peek_at(4) == '\t')) { |
8054 | 795 | sr.advance(5); |
8055 | 795 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
8056 | 795 | ParseTextureNameAndOption(&(material.reflection_texname), |
8057 | 795 | &(material.reflection_texopt), line_rest.c_str()); |
8058 | 795 | sr.skip_line(); |
8059 | 795 | continue; |
8060 | 795 | } |
8061 | | |
8062 | | // PBR: roughness texture |
8063 | 120k | if (sr.match("map_Pr", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) { |
8064 | 390 | sr.advance(7); |
8065 | 390 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
8066 | 390 | ParseTextureNameAndOption(&(material.roughness_texname), |
8067 | 390 | &(material.roughness_texopt), line_rest.c_str()); |
8068 | 390 | sr.skip_line(); |
8069 | 390 | continue; |
8070 | 390 | } |
8071 | | |
8072 | | // PBR: metallic texture |
8073 | 120k | if (sr.match("map_Pm", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) { |
8074 | 17 | sr.advance(7); |
8075 | 17 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
8076 | 17 | ParseTextureNameAndOption(&(material.metallic_texname), |
8077 | 17 | &(material.metallic_texopt), line_rest.c_str()); |
8078 | 17 | sr.skip_line(); |
8079 | 17 | continue; |
8080 | 17 | } |
8081 | | |
8082 | | // PBR: sheen texture |
8083 | 120k | if (sr.match("map_Ps", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) { |
8084 | 64 | sr.advance(7); |
8085 | 64 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
8086 | 64 | ParseTextureNameAndOption(&(material.sheen_texname), |
8087 | 64 | &(material.sheen_texopt), line_rest.c_str()); |
8088 | 64 | sr.skip_line(); |
8089 | 64 | continue; |
8090 | 64 | } |
8091 | | |
8092 | | // PBR: emissive texture |
8093 | 119k | if (sr.match("map_Ke", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) { |
8094 | 64 | sr.advance(7); |
8095 | 64 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
8096 | 64 | ParseTextureNameAndOption(&(material.emissive_texname), |
8097 | 64 | &(material.emissive_texopt), line_rest.c_str()); |
8098 | 64 | sr.skip_line(); |
8099 | 64 | continue; |
8100 | 64 | } |
8101 | | |
8102 | | // PBR: normal map texture |
8103 | 119k | if (sr.match("norm", 4) && (sr.peek_at(4) == ' ' || sr.peek_at(4) == '\t')) { |
8104 | 634 | sr.advance(5); |
8105 | 634 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
8106 | 634 | ParseTextureNameAndOption(&(material.normal_texname), |
8107 | 634 | &(material.normal_texopt), line_rest.c_str()); |
8108 | 634 | sr.skip_line(); |
8109 | 634 | continue; |
8110 | 634 | } |
8111 | | |
8112 | | // unknown parameter |
8113 | 119k | { |
8114 | 119k | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
8115 | 119k | const char *_lp = line_rest.c_str(); |
8116 | 119k | const char *_space = strchr(_lp, ' '); |
8117 | 119k | if (!_space) { |
8118 | 112k | _space = strchr(_lp, '\t'); |
8119 | 112k | } |
8120 | 119k | if (_space) { |
8121 | 10.7k | std::ptrdiff_t len = _space - _lp; |
8122 | 10.7k | std::string key(_lp, static_cast<size_t>(len)); |
8123 | 10.7k | std::string value = _space + 1; |
8124 | 10.7k | material.unknown_parameter.insert( |
8125 | 10.7k | std::pair<std::string, std::string>(key, value)); |
8126 | 10.7k | } |
8127 | 119k | } |
8128 | 119k | sr.skip_line(); |
8129 | 119k | } |
8130 | | // flush last material (only if it was actually defined). |
8131 | 1.51k | if (!material.name.empty()) { |
8132 | 164 | material_map->insert(std::pair<std::string, int>( |
8133 | 164 | material.name, static_cast<int>(materials->size()))); |
8134 | 164 | materials->push_back(material); |
8135 | 164 | } |
8136 | | |
8137 | 1.51k | if (warning) { |
8138 | 1.51k | (*warning) += warn_ss.str(); |
8139 | 1.51k | } |
8140 | | |
8141 | 1.51k | return true; |
8142 | 1.60k | } |
8143 | | |
8144 | | void LoadMtl(std::map<std::string, int> *material_map, |
8145 | | std::vector<material_t> *materials, std::istream *inStream, |
8146 | 0 | std::string *warning, std::string *err) { |
8147 | 0 | StreamReader sr(*inStream); |
8148 | 0 | LoadMtlInternal(material_map, materials, sr, warning, err); |
8149 | 0 | } |
8150 | | |
8151 | | |
8152 | | bool MaterialFileReader::operator()(const std::string &matId, |
8153 | | std::vector<material_t> *materials, |
8154 | | std::map<std::string, int> *matMap, |
8155 | 0 | std::string *warn, std::string *err) { |
8156 | 0 | if (!m_mtlBaseDir.empty()) { |
8157 | | #ifdef _WIN32 |
8158 | | char sep = ';'; |
8159 | | #else |
8160 | 0 | char sep = ':'; |
8161 | 0 | #endif |
8162 | | |
8163 | | // https://stackoverflow.com/questions/5167625/splitting-a-c-stdstring-using-tokens-e-g |
8164 | 0 | std::vector<std::string> paths; |
8165 | 0 | std::istringstream f(m_mtlBaseDir); |
8166 | |
|
8167 | 0 | std::string s; |
8168 | 0 | while (getline(f, s, sep)) { |
8169 | 0 | paths.push_back(s); |
8170 | 0 | } |
8171 | |
|
8172 | 0 | for (size_t i = 0; i < paths.size(); i++) { |
8173 | 0 | std::string filepath = JoinPath(paths[i], matId); |
8174 | |
|
8175 | | #ifdef TINYOBJLOADER_USE_MMAP |
8176 | | { |
8177 | | MappedFile mf; |
8178 | | if (!mf.open(filepath.c_str())) continue; |
8179 | | if (mf.size > TINYOBJLOADER_STREAM_READER_MAX_BYTES) { |
8180 | | if (err) { |
8181 | | std::stringstream ss; |
8182 | | ss << "input stream too large (" << mf.size |
8183 | | << " bytes exceeds limit " |
8184 | | << TINYOBJLOADER_STREAM_READER_MAX_BYTES << " bytes)\n"; |
8185 | | (*err) += ss.str(); |
8186 | | } |
8187 | | return false; |
8188 | | } |
8189 | | StreamReader sr(mf.data, mf.size); |
8190 | | return LoadMtlInternal(matMap, materials, sr, warn, err, filepath); |
8191 | | } |
8192 | | #else // !TINYOBJLOADER_USE_MMAP |
8193 | | #ifdef _WIN32 |
8194 | | std::ifstream matIStream(LongPathW(UTF8ToWchar(filepath)).c_str()); |
8195 | | #else |
8196 | 0 | std::ifstream matIStream(filepath.c_str()); |
8197 | 0 | #endif |
8198 | 0 | if (matIStream) { |
8199 | 0 | StreamReader mtl_sr(matIStream); |
8200 | 0 | return LoadMtlInternal(matMap, materials, mtl_sr, warn, err, filepath); |
8201 | 0 | } |
8202 | 0 | #endif // TINYOBJLOADER_USE_MMAP |
8203 | 0 | } |
8204 | | |
8205 | 0 | std::stringstream ss; |
8206 | 0 | ss << "Material file [ " << matId |
8207 | 0 | << " ] not found in a path : " << m_mtlBaseDir << "\n"; |
8208 | 0 | if (warn) { |
8209 | 0 | (*warn) += ss.str(); |
8210 | 0 | } |
8211 | 0 | return false; |
8212 | |
|
8213 | 0 | } else { |
8214 | 0 | std::string filepath = matId; |
8215 | |
|
8216 | | #ifdef TINYOBJLOADER_USE_MMAP |
8217 | | { |
8218 | | MappedFile mf; |
8219 | | if (mf.open(filepath.c_str())) { |
8220 | | if (mf.size > TINYOBJLOADER_STREAM_READER_MAX_BYTES) { |
8221 | | if (err) { |
8222 | | std::stringstream ss; |
8223 | | ss << "input stream too large (" << mf.size |
8224 | | << " bytes exceeds limit " |
8225 | | << TINYOBJLOADER_STREAM_READER_MAX_BYTES << " bytes)\n"; |
8226 | | (*err) += ss.str(); |
8227 | | } |
8228 | | return false; |
8229 | | } |
8230 | | StreamReader sr(mf.data, mf.size); |
8231 | | return LoadMtlInternal(matMap, materials, sr, warn, err, filepath); |
8232 | | } |
8233 | | } |
8234 | | #else // !TINYOBJLOADER_USE_MMAP |
8235 | | #ifdef _WIN32 |
8236 | | std::ifstream matIStream(LongPathW(UTF8ToWchar(filepath)).c_str()); |
8237 | | #else |
8238 | 0 | std::ifstream matIStream(filepath.c_str()); |
8239 | 0 | #endif |
8240 | 0 | if (matIStream) { |
8241 | 0 | StreamReader mtl_sr(matIStream); |
8242 | 0 | return LoadMtlInternal(matMap, materials, mtl_sr, warn, err, filepath); |
8243 | 0 | } |
8244 | 0 | #endif // TINYOBJLOADER_USE_MMAP |
8245 | | |
8246 | 0 | std::stringstream ss; |
8247 | 0 | ss << "Material file [ " << filepath |
8248 | 0 | << " ] not found in a path : " << m_mtlBaseDir << "\n"; |
8249 | 0 | if (warn) { |
8250 | 0 | (*warn) += ss.str(); |
8251 | 0 | } |
8252 | |
|
8253 | 0 | return false; |
8254 | 0 | } |
8255 | 0 | } |
8256 | | |
8257 | | bool MaterialStreamReader::operator()(const std::string &matId, |
8258 | | std::vector<material_t> *materials, |
8259 | | std::map<std::string, int> *matMap, |
8260 | 1.60k | std::string *warn, std::string *err) { |
8261 | 1.60k | (void)matId; |
8262 | 1.60k | if (!m_inStream) { |
8263 | 0 | std::stringstream ss; |
8264 | 0 | ss << "Material stream in error state. \n"; |
8265 | 0 | if (warn) { |
8266 | 0 | (*warn) += ss.str(); |
8267 | 0 | } |
8268 | 0 | return false; |
8269 | 0 | } |
8270 | | |
8271 | 1.60k | StreamReader mtl_sr(m_inStream); |
8272 | 1.60k | return LoadMtlInternal(matMap, materials, mtl_sr, warn, err, "<stream>"); |
8273 | 1.60k | } |
8274 | | |
8275 | | static bool LoadObjInternal(attrib_t *attrib, std::vector<shape_t> *shapes, |
8276 | | std::vector<material_t> *materials, |
8277 | | std::string *warn, std::string *err, |
8278 | | StreamReader &sr, |
8279 | | MaterialReader *readMatFn, bool triangulate, |
8280 | | bool default_vcols_fallback, |
8281 | 8.54k | const std::string &filename = "<stream>") { |
8282 | 8.54k | if (sr.has_errors()) { |
8283 | 0 | if (err) { |
8284 | 0 | (*err) += sr.get_errors(); |
8285 | 0 | } |
8286 | 0 | return false; |
8287 | 0 | } |
8288 | | |
8289 | 8.54k | std::vector<real_t> v; |
8290 | 8.54k | std::vector<real_t> vertex_weights; |
8291 | 8.54k | std::vector<real_t> vn; |
8292 | 8.54k | std::vector<real_t> vt; |
8293 | 8.54k | std::vector<real_t> vt_w; // optional [w] component in `vt` |
8294 | 8.54k | std::vector<real_t> vc; |
8295 | 8.54k | std::vector<skin_weight_t> vw; |
8296 | 8.54k | std::vector<tag_t> tags; |
8297 | 8.54k | PrimGroup prim_group; |
8298 | 8.54k | std::string name; |
8299 | | |
8300 | | // material |
8301 | 8.54k | std::set<std::string> material_filenames; |
8302 | 8.54k | std::map<std::string, int> material_map; |
8303 | 8.54k | int material = -1; |
8304 | | |
8305 | 8.54k | unsigned int current_smoothing_id = 0; |
8306 | | |
8307 | 8.54k | int greatest_v_idx = -1; |
8308 | 8.54k | int greatest_vn_idx = -1; |
8309 | 8.54k | int greatest_vt_idx = -1; |
8310 | | |
8311 | 8.54k | shape_t shape; |
8312 | | |
8313 | 8.54k | bool found_all_colors = true; |
8314 | | |
8315 | | // Handle BOM |
8316 | 8.54k | if (sr.remaining() >= 3 && |
8317 | 8.31k | static_cast<unsigned char>(sr.peek()) == 0xEF && |
8318 | 107 | static_cast<unsigned char>(sr.peek_at(1)) == 0xBB && |
8319 | 93 | static_cast<unsigned char>(sr.peek_at(2)) == 0xBF) { |
8320 | 78 | sr.advance(3); |
8321 | 78 | } |
8322 | | |
8323 | 8.54k | warning_context context; |
8324 | 8.54k | context.warn = warn; |
8325 | 8.54k | context.filename = filename; |
8326 | | |
8327 | 1.69M | while (!sr.eof()) { |
8328 | 1.68M | sr.skip_space(); |
8329 | 1.68M | if (sr.at_line_end()) { sr.skip_line(); continue; } |
8330 | 1.60M | if (sr.peek() == '#') { sr.skip_line(); continue; } |
8331 | | |
8332 | 1.60M | size_t line_num = sr.line_num(); |
8333 | | |
8334 | | // vertex |
8335 | 1.60M | if (sr.peek() == 'v' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) { |
8336 | 3.39k | sr.advance(2); |
8337 | 3.39k | real_t x, y, z; |
8338 | 3.39k | real_t r, g, b; |
8339 | | |
8340 | 3.39k | int num_components = sr_parseVertexWithColor(&x, &y, &z, &r, &g, &b, sr, err, filename); |
8341 | 3.39k | if (num_components < 0) return false; |
8342 | 3.00k | found_all_colors &= (num_components == 6); |
8343 | | |
8344 | 3.00k | v.push_back(x); |
8345 | 3.00k | v.push_back(y); |
8346 | 3.00k | v.push_back(z); |
8347 | | |
8348 | 3.00k | vertex_weights.push_back(r); |
8349 | | |
8350 | 3.00k | if ((num_components == 6) || default_vcols_fallback) { |
8351 | 3.00k | vc.push_back(r); |
8352 | 3.00k | vc.push_back(g); |
8353 | 3.00k | vc.push_back(b); |
8354 | 3.00k | } |
8355 | | |
8356 | 3.00k | sr.skip_line(); |
8357 | 3.00k | continue; |
8358 | 3.39k | } |
8359 | | |
8360 | | // normal |
8361 | 1.59M | if (sr.peek() == 'v' && sr.peek_at(1) == 'n' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
8362 | 921 | sr.advance(3); |
8363 | 921 | real_t x, y, z; |
8364 | 921 | if (!sr_parseReal3(&x, &y, &z, sr, err, filename)) return false; |
8365 | 911 | vn.push_back(x); |
8366 | 911 | vn.push_back(y); |
8367 | 911 | vn.push_back(z); |
8368 | 911 | sr.skip_line(); |
8369 | 911 | continue; |
8370 | 921 | } |
8371 | | |
8372 | | // texcoord |
8373 | 1.59M | if (sr.peek() == 'v' && sr.peek_at(1) == 't' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
8374 | 1.05k | sr.advance(3); |
8375 | 1.05k | real_t x, y; |
8376 | 1.05k | if (!sr_parseReal2(&x, &y, sr, err, filename)) return false; |
8377 | 1.05k | vt.push_back(x); |
8378 | 1.05k | vt.push_back(y); |
8379 | | |
8380 | | // Parse optional w component |
8381 | 1.05k | real_t w = static_cast<real_t>(0.0); |
8382 | 1.05k | sr_parseReal(sr, &w); |
8383 | 1.05k | vt_w.push_back(w); |
8384 | | |
8385 | 1.05k | sr.skip_line(); |
8386 | 1.05k | continue; |
8387 | 1.05k | } |
8388 | | |
8389 | | // skin weight. tinyobj extension |
8390 | 1.59M | if (sr.peek() == 'v' && sr.peek_at(1) == 'w' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
8391 | 164 | sr.advance(3); |
8392 | | |
8393 | 164 | int vid; |
8394 | 164 | if (!sr_parseInt(sr, &vid, err, filename)) return false; |
8395 | | |
8396 | 12 | skin_weight_t sw; |
8397 | 12 | sw.vertex_id = vid; |
8398 | | |
8399 | 12 | size_t vw_loop_max = sr.remaining() + 1; |
8400 | 12 | size_t vw_loop_iter = 0; |
8401 | 145 | while (!sr.at_line_end() && sr.peek() != '#' && |
8402 | 136 | vw_loop_iter < vw_loop_max) { |
8403 | 136 | real_t j, w; |
8404 | 136 | sr_parseReal2(&j, &w, sr, -1.0); |
8405 | | |
8406 | 136 | if (j < static_cast<real_t>(0)) { |
8407 | 3 | if (err) { |
8408 | 3 | (*err) += sr.format_error(filename, |
8409 | 3 | "failed to parse `vw' line: joint_id is negative"); |
8410 | 3 | } |
8411 | 3 | return false; |
8412 | 3 | } |
8413 | | |
8414 | 133 | joint_and_weight_t jw; |
8415 | 133 | jw.joint_id = int(j); |
8416 | 133 | jw.weight = w; |
8417 | | |
8418 | 133 | sw.weightValues.push_back(jw); |
8419 | 133 | sr.skip_space_and_cr(); |
8420 | 133 | vw_loop_iter++; |
8421 | 133 | } |
8422 | | |
8423 | 9 | vw.push_back(sw); |
8424 | 9 | sr.skip_line(); |
8425 | 9 | continue; |
8426 | 12 | } |
8427 | | |
8428 | 1.59M | context.line_number = line_num; |
8429 | | |
8430 | | // line |
8431 | 1.59M | if (sr.peek() == 'l' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) { |
8432 | 255k | sr.advance(2); |
8433 | | |
8434 | 255k | __line_t line; |
8435 | | |
8436 | 255k | size_t l_loop_max = sr.remaining() + 1; |
8437 | 255k | size_t l_loop_iter = 0; |
8438 | 259k | while (!sr.at_line_end() && sr.peek() != '#' && |
8439 | 4.21k | l_loop_iter < l_loop_max) { |
8440 | 4.21k | vertex_index_t vi; |
8441 | 4.21k | if (!sr_parseTriple(sr, size_to_int(v.size() / 3), |
8442 | 4.21k | size_to_int(vn.size() / 3), |
8443 | 4.21k | size_to_int(vt.size() / 2), &vi, context)) { |
8444 | 70 | if (err) { |
8445 | 70 | (*err) += sr.format_error(filename, |
8446 | 70 | "failed to parse `l' line (invalid vertex index)"); |
8447 | 70 | } |
8448 | 70 | return false; |
8449 | 70 | } |
8450 | | |
8451 | 4.14k | line.vertex_indices.push_back(vi); |
8452 | 4.14k | sr.skip_space_and_cr(); |
8453 | 4.14k | l_loop_iter++; |
8454 | 4.14k | } |
8455 | | |
8456 | 255k | prim_group.lineGroup.push_back(line); |
8457 | 255k | sr.skip_line(); |
8458 | 255k | continue; |
8459 | 255k | } |
8460 | | |
8461 | | // points |
8462 | 1.33M | if (sr.peek() == 'p' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) { |
8463 | 183k | sr.advance(2); |
8464 | | |
8465 | 183k | __points_t pts; |
8466 | | |
8467 | 183k | size_t p_loop_max = sr.remaining() + 1; |
8468 | 183k | size_t p_loop_iter = 0; |
8469 | 224k | while (!sr.at_line_end() && sr.peek() != '#' && |
8470 | 40.6k | p_loop_iter < p_loop_max) { |
8471 | 40.6k | vertex_index_t vi; |
8472 | 40.6k | if (!sr_parseTriple(sr, size_to_int(v.size() / 3), |
8473 | 40.6k | size_to_int(vn.size() / 3), |
8474 | 40.6k | size_to_int(vt.size() / 2), &vi, context)) { |
8475 | 56 | if (err) { |
8476 | 56 | (*err) += sr.format_error(filename, |
8477 | 56 | "failed to parse `p' line (invalid vertex index)"); |
8478 | 56 | } |
8479 | 56 | return false; |
8480 | 56 | } |
8481 | | |
8482 | 40.6k | pts.vertex_indices.push_back(vi); |
8483 | 40.6k | sr.skip_space_and_cr(); |
8484 | 40.6k | p_loop_iter++; |
8485 | 40.6k | } |
8486 | | |
8487 | 183k | prim_group.pointsGroup.push_back(pts); |
8488 | 183k | sr.skip_line(); |
8489 | 183k | continue; |
8490 | 183k | } |
8491 | | |
8492 | | // face |
8493 | 1.15M | if (sr.peek() == 'f' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) { |
8494 | 458k | sr.advance(2); |
8495 | 458k | sr.skip_space(); |
8496 | | |
8497 | 458k | face_t face; |
8498 | | |
8499 | 458k | face.smoothing_group_id = current_smoothing_id; |
8500 | 458k | face.vertex_indices.reserve(3); |
8501 | | |
8502 | 458k | size_t f_loop_max = sr.remaining() + 1; |
8503 | 458k | size_t f_loop_iter = 0; |
8504 | 599k | while (!sr.at_line_end() && sr.peek() != '#' && |
8505 | 141k | f_loop_iter < f_loop_max) { |
8506 | 141k | vertex_index_t vi; |
8507 | 141k | if (!sr_parseTriple(sr, size_to_int(v.size() / 3), |
8508 | 141k | size_to_int(vn.size() / 3), |
8509 | 141k | size_to_int(vt.size() / 2), &vi, context)) { |
8510 | 278 | if (err) { |
8511 | 278 | (*err) += sr.format_error(filename, |
8512 | 278 | "failed to parse `f' line (invalid vertex index)"); |
8513 | 278 | } |
8514 | 278 | return false; |
8515 | 278 | } |
8516 | | |
8517 | 141k | greatest_v_idx = greatest_v_idx > vi.v_idx ? greatest_v_idx : vi.v_idx; |
8518 | 141k | greatest_vn_idx = |
8519 | 141k | greatest_vn_idx > vi.vn_idx ? greatest_vn_idx : vi.vn_idx; |
8520 | 141k | greatest_vt_idx = |
8521 | 141k | greatest_vt_idx > vi.vt_idx ? greatest_vt_idx : vi.vt_idx; |
8522 | | |
8523 | 141k | face.vertex_indices.push_back(vi); |
8524 | 141k | sr.skip_space_and_cr(); |
8525 | 141k | f_loop_iter++; |
8526 | 141k | } |
8527 | | |
8528 | 457k | prim_group.faceGroup.push_back(face); |
8529 | 457k | sr.skip_line(); |
8530 | 457k | continue; |
8531 | 458k | } |
8532 | | |
8533 | | // use mtl |
8534 | 697k | if (sr.match("usemtl", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) { |
8535 | 83 | sr.advance(6); |
8536 | 83 | std::string namebuf = sr_parseString(sr); |
8537 | | |
8538 | 83 | int newMaterialId = -1; |
8539 | 83 | std::map<std::string, int>::const_iterator it = |
8540 | 83 | material_map.find(namebuf); |
8541 | 83 | if (it != material_map.end()) { |
8542 | 0 | newMaterialId = it->second; |
8543 | 83 | } else { |
8544 | 83 | if (warn) { |
8545 | 83 | (*warn) += "material [ '" + namebuf + "' ] not found in .mtl\n"; |
8546 | 83 | } |
8547 | 83 | } |
8548 | | |
8549 | 83 | if (newMaterialId != material) { |
8550 | 0 | exportGroupsToShape(&shape, prim_group, tags, material, name, |
8551 | 0 | triangulate, v, warn); |
8552 | 0 | prim_group.faceGroup.clear(); |
8553 | 0 | material = newMaterialId; |
8554 | 0 | } |
8555 | | |
8556 | 83 | sr.skip_line(); |
8557 | 83 | continue; |
8558 | 83 | } |
8559 | | |
8560 | | // load mtl |
8561 | 697k | if (sr.match("mtllib", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) { |
8562 | 8.25k | if (readMatFn) { |
8563 | 8.25k | sr.advance(7); |
8564 | | |
8565 | 8.25k | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
8566 | 8.25k | std::vector<std::string> filenames; |
8567 | 8.25k | SplitString(line_rest, ' ', '\\', filenames); |
8568 | 8.25k | RemoveEmptyTokens(&filenames); |
8569 | | |
8570 | 8.25k | if (filenames.empty()) { |
8571 | 5.48k | if (warn) { |
8572 | 5.48k | std::stringstream ss; |
8573 | 5.48k | ss << "Looks like empty filename for mtllib. Use default " |
8574 | 5.48k | "material (line " |
8575 | 5.48k | << line_num << ".)\n"; |
8576 | | |
8577 | 5.48k | (*warn) += ss.str(); |
8578 | 5.48k | } |
8579 | 5.48k | } else { |
8580 | 2.76k | bool found = false; |
8581 | 6.20k | for (size_t s = 0; s < filenames.size(); s++) { |
8582 | 4.95k | if (material_filenames.count(filenames[s]) > 0) { |
8583 | 3.35k | found = true; |
8584 | 3.35k | continue; |
8585 | 3.35k | } |
8586 | | |
8587 | 1.60k | std::string warn_mtl; |
8588 | 1.60k | std::string err_mtl; |
8589 | 1.60k | bool ok = (*readMatFn)(filenames[s].c_str(), materials, |
8590 | 1.60k | &material_map, &warn_mtl, &err_mtl); |
8591 | 1.60k | if (warn && (!warn_mtl.empty())) { |
8592 | 12 | (*warn) += warn_mtl; |
8593 | 12 | } |
8594 | | |
8595 | 1.60k | if (err && (!err_mtl.empty())) { |
8596 | 90 | (*err) += err_mtl; |
8597 | 90 | } |
8598 | | |
8599 | 1.60k | if (ok) { |
8600 | 1.51k | found = true; |
8601 | 1.51k | material_filenames.insert(filenames[s]); |
8602 | 1.51k | break; |
8603 | 1.51k | } |
8604 | 1.60k | } |
8605 | | |
8606 | 2.76k | if (!found) { |
8607 | 77 | if (warn) { |
8608 | 77 | (*warn) += |
8609 | 77 | "Failed to load material file(s). Use default " |
8610 | 77 | "material.\n"; |
8611 | 77 | } |
8612 | 77 | } |
8613 | 2.76k | } |
8614 | 8.25k | } |
8615 | | |
8616 | 8.25k | sr.skip_line(); |
8617 | 8.25k | continue; |
8618 | 8.25k | } |
8619 | | |
8620 | | // group name |
8621 | 689k | if (sr.peek() == 'g' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) { |
8622 | | // flush previous face group. |
8623 | 3.11k | bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, |
8624 | 3.11k | triangulate, v, warn); |
8625 | 3.11k | (void)ret; |
8626 | | |
8627 | 3.11k | if (shape.mesh.indices.size() > 0) { |
8628 | 758 | shapes->push_back(shape); |
8629 | 758 | } |
8630 | | |
8631 | 3.11k | shape = shape_t(); |
8632 | | |
8633 | | // material = -1; |
8634 | 3.11k | prim_group.clear(); |
8635 | | |
8636 | 3.11k | std::vector<std::string> names; |
8637 | | |
8638 | 3.11k | size_t g_loop_max = sr.remaining() + 1; |
8639 | 3.11k | size_t g_loop_iter = 0; |
8640 | 538k | while (!sr.at_line_end() && sr.peek() != '#' && |
8641 | 535k | g_loop_iter < g_loop_max) { |
8642 | 535k | std::string str = sr_parseString(sr); |
8643 | 535k | names.push_back(str); |
8644 | 535k | sr.skip_space_and_cr(); |
8645 | 535k | g_loop_iter++; |
8646 | 535k | } |
8647 | | |
8648 | | // names[0] must be 'g' |
8649 | | |
8650 | 3.11k | if (names.size() < 2) { |
8651 | | // 'g' with empty names |
8652 | 1.84k | if (warn) { |
8653 | 1.84k | std::stringstream ss; |
8654 | 1.84k | ss << "Empty group name. line: " << line_num << "\n"; |
8655 | 1.84k | (*warn) += ss.str(); |
8656 | 1.84k | name = ""; |
8657 | 1.84k | } |
8658 | 1.84k | } else { |
8659 | 1.27k | std::stringstream ss; |
8660 | 1.27k | ss << names[1]; |
8661 | | |
8662 | 532k | for (size_t i = 2; i < names.size(); i++) { |
8663 | 530k | ss << " " << names[i]; |
8664 | 530k | } |
8665 | | |
8666 | 1.27k | name = ss.str(); |
8667 | 1.27k | } |
8668 | | |
8669 | 3.11k | sr.skip_line(); |
8670 | 3.11k | continue; |
8671 | 3.11k | } |
8672 | | |
8673 | | // object name |
8674 | 686k | if (sr.peek() == 'o' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) { |
8675 | | // flush previous face group. |
8676 | 9.00k | bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, |
8677 | 9.00k | triangulate, v, warn); |
8678 | 9.00k | (void)ret; |
8679 | | |
8680 | 9.00k | if (shape.mesh.indices.size() > 0 || shape.lines.indices.size() > 0 || |
8681 | 7.92k | shape.points.indices.size() > 0) { |
8682 | 6.49k | shapes->push_back(shape); |
8683 | 6.49k | } |
8684 | | |
8685 | | // material = -1; |
8686 | 9.00k | prim_group.clear(); |
8687 | 9.00k | shape = shape_t(); |
8688 | | |
8689 | 9.00k | sr.advance(2); |
8690 | 9.00k | std::string rest = sr.read_line(); |
8691 | 9.00k | name = rest; |
8692 | | |
8693 | 9.00k | sr.skip_line(); |
8694 | 9.00k | continue; |
8695 | 9.00k | } |
8696 | | |
8697 | 677k | if (sr.peek() == 't' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) { |
8698 | 574k | const int max_tag_nums = 8192; |
8699 | 574k | tag_t tag; |
8700 | | |
8701 | 574k | sr.advance(2); |
8702 | | |
8703 | 574k | tag.name = sr_parseString(sr); |
8704 | | |
8705 | 574k | tag_sizes ts = sr_parseTagTriple(sr); |
8706 | | |
8707 | 574k | if (ts.num_ints < 0) { |
8708 | 232 | ts.num_ints = 0; |
8709 | 232 | } |
8710 | 574k | if (ts.num_ints > max_tag_nums) { |
8711 | 236 | ts.num_ints = max_tag_nums; |
8712 | 236 | } |
8713 | | |
8714 | 574k | if (ts.num_reals < 0) { |
8715 | 195 | ts.num_reals = 0; |
8716 | 195 | } |
8717 | 574k | if (ts.num_reals > max_tag_nums) { |
8718 | 242 | ts.num_reals = max_tag_nums; |
8719 | 242 | } |
8720 | | |
8721 | 574k | if (ts.num_strings < 0) { |
8722 | 351 | ts.num_strings = 0; |
8723 | 351 | } |
8724 | 574k | if (ts.num_strings > max_tag_nums) { |
8725 | 91 | ts.num_strings = max_tag_nums; |
8726 | 91 | } |
8727 | | |
8728 | 574k | tag.intValues.resize(static_cast<size_t>(ts.num_ints)); |
8729 | | |
8730 | 3.79M | for (size_t i = 0; i < static_cast<size_t>(ts.num_ints); ++i) { |
8731 | 3.22M | tag.intValues[i] = sr_parseInt(sr); |
8732 | 3.22M | } |
8733 | | |
8734 | 574k | tag.floatValues.resize(static_cast<size_t>(ts.num_reals)); |
8735 | 2.81M | for (size_t i = 0; i < static_cast<size_t>(ts.num_reals); ++i) { |
8736 | 2.24M | tag.floatValues[i] = sr_parseReal(sr); |
8737 | 2.24M | } |
8738 | | |
8739 | 574k | tag.stringValues.resize(static_cast<size_t>(ts.num_strings)); |
8740 | 1.40M | for (size_t i = 0; i < static_cast<size_t>(ts.num_strings); ++i) { |
8741 | 832k | tag.stringValues[i] = sr_parseString(sr); |
8742 | 832k | } |
8743 | | |
8744 | 574k | tags.push_back(tag); |
8745 | | |
8746 | 574k | sr.skip_line(); |
8747 | 574k | continue; |
8748 | 574k | } |
8749 | | |
8750 | 102k | if (sr.peek() == 's' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) { |
8751 | | // smoothing group id |
8752 | 1.94k | sr.advance(2); |
8753 | 1.94k | sr.skip_space(); |
8754 | | |
8755 | 1.94k | if (sr.at_line_end()) { |
8756 | 438 | sr.skip_line(); |
8757 | 438 | continue; |
8758 | 438 | } |
8759 | | |
8760 | 1.50k | if (sr.peek() == '\r') { |
8761 | 0 | sr.skip_line(); |
8762 | 0 | continue; |
8763 | 0 | } |
8764 | | |
8765 | 1.50k | if (sr.remaining() >= 3 && sr.match("off", 3)) { |
8766 | 334 | current_smoothing_id = 0; |
8767 | 1.17k | } else { |
8768 | 1.17k | int smGroupId = sr_parseInt(sr); |
8769 | 1.17k | if (smGroupId < 0) { |
8770 | 233 | current_smoothing_id = 0; |
8771 | 941 | } else { |
8772 | 941 | current_smoothing_id = static_cast<unsigned int>(smGroupId); |
8773 | 941 | } |
8774 | 1.17k | } |
8775 | | |
8776 | 1.50k | sr.skip_line(); |
8777 | 1.50k | continue; |
8778 | 1.50k | } |
8779 | | |
8780 | | // Ignore unknown command. |
8781 | 100k | sr.skip_line(); |
8782 | 100k | } |
8783 | | |
8784 | | // not all vertices have colors, no default colors desired? -> clear colors |
8785 | 7.58k | if (!found_all_colors && !default_vcols_fallback) { |
8786 | 0 | vc.clear(); |
8787 | 0 | } |
8788 | | |
8789 | 7.58k | if (greatest_v_idx >= size_to_int(v.size() / 3)) { |
8790 | 677 | if (warn) { |
8791 | 677 | std::stringstream ss; |
8792 | 677 | ss << "Vertex indices out of bounds (line " << sr.line_num() << ".)\n\n"; |
8793 | 677 | (*warn) += ss.str(); |
8794 | 677 | } |
8795 | 677 | } |
8796 | 7.58k | if (greatest_vn_idx >= size_to_int(vn.size() / 3)) { |
8797 | 53 | if (warn) { |
8798 | 53 | std::stringstream ss; |
8799 | 53 | ss << "Vertex normal indices out of bounds (line " << sr.line_num() |
8800 | 53 | << ".)\n\n"; |
8801 | 53 | (*warn) += ss.str(); |
8802 | 53 | } |
8803 | 53 | } |
8804 | 7.58k | if (greatest_vt_idx >= size_to_int(vt.size() / 2)) { |
8805 | 62 | if (warn) { |
8806 | 62 | std::stringstream ss; |
8807 | 62 | ss << "Vertex texcoord indices out of bounds (line " << sr.line_num() |
8808 | 62 | << ".)\n\n"; |
8809 | 62 | (*warn) += ss.str(); |
8810 | 62 | } |
8811 | 62 | } |
8812 | | |
8813 | 7.58k | bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, |
8814 | 7.58k | triangulate, v, warn); |
8815 | 7.58k | if (ret || shape.mesh.indices.size()) { |
8816 | 1.13k | shapes->push_back(shape); |
8817 | 1.13k | } |
8818 | 7.58k | prim_group.clear(); |
8819 | | |
8820 | 7.58k | attrib->vertices.swap(v); |
8821 | 7.58k | attrib->vertex_weights.swap(vertex_weights); |
8822 | 7.58k | attrib->normals.swap(vn); |
8823 | 7.58k | attrib->texcoords.swap(vt); |
8824 | 7.58k | attrib->texcoord_ws.swap(vt_w); |
8825 | 7.58k | attrib->colors.swap(vc); |
8826 | 7.58k | attrib->skin_weights.swap(vw); |
8827 | | |
8828 | 7.58k | return true; |
8829 | 8.54k | } |
8830 | | |
8831 | | bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes, |
8832 | | std::vector<material_t> *materials, std::string *warn, |
8833 | | std::string *err, const char *filename, const char *mtl_basedir, |
8834 | 0 | bool triangulate, bool default_vcols_fallback) { |
8835 | 0 | attrib->vertices.clear(); |
8836 | 0 | attrib->vertex_weights.clear(); |
8837 | 0 | attrib->normals.clear(); |
8838 | 0 | attrib->texcoords.clear(); |
8839 | 0 | attrib->texcoord_ws.clear(); |
8840 | 0 | attrib->colors.clear(); |
8841 | 0 | attrib->skin_weights.clear(); |
8842 | 0 | shapes->clear(); |
8843 | |
|
8844 | 0 | std::string baseDir = mtl_basedir ? mtl_basedir : ""; |
8845 | 0 | if (!baseDir.empty()) { |
8846 | 0 | #ifndef _WIN32 |
8847 | 0 | const char dirsep = '/'; |
8848 | | #else |
8849 | | const char dirsep = '\\'; |
8850 | | #endif |
8851 | 0 | if (baseDir[baseDir.length() - 1] != dirsep) baseDir += dirsep; |
8852 | 0 | } |
8853 | 0 | MaterialFileReader matFileReader(baseDir); |
8854 | |
|
8855 | | #ifdef TINYOBJLOADER_USE_MMAP |
8856 | | { |
8857 | | MappedFile mf; |
8858 | | if (!mf.open(filename)) { |
8859 | | if (err) { |
8860 | | std::stringstream ss; |
8861 | | ss << "Cannot open file [" << filename << "]\n"; |
8862 | | (*err) = ss.str(); |
8863 | | } |
8864 | | return false; |
8865 | | } |
8866 | | if (mf.size > TINYOBJLOADER_STREAM_READER_MAX_BYTES) { |
8867 | | if (err) { |
8868 | | std::stringstream ss; |
8869 | | ss << "input stream too large (" << mf.size |
8870 | | << " bytes exceeds limit " |
8871 | | << TINYOBJLOADER_STREAM_READER_MAX_BYTES << " bytes)\n"; |
8872 | | (*err) += ss.str(); |
8873 | | } |
8874 | | return false; |
8875 | | } |
8876 | | StreamReader sr(mf.data, mf.size); |
8877 | | return LoadObjInternal(attrib, shapes, materials, warn, err, sr, |
8878 | | &matFileReader, triangulate, default_vcols_fallback, |
8879 | | filename); |
8880 | | } |
8881 | | #else // !TINYOBJLOADER_USE_MMAP |
8882 | | #ifdef _WIN32 |
8883 | | std::ifstream ifs(LongPathW(UTF8ToWchar(filename)).c_str()); |
8884 | | #else |
8885 | 0 | std::ifstream ifs(filename); |
8886 | 0 | #endif |
8887 | 0 | if (!ifs) { |
8888 | 0 | if (err) { |
8889 | 0 | std::stringstream ss; |
8890 | 0 | ss << "Cannot open file [" << filename << "]\n"; |
8891 | 0 | (*err) = ss.str(); |
8892 | 0 | } |
8893 | 0 | return false; |
8894 | 0 | } |
8895 | 0 | { |
8896 | 0 | StreamReader sr(ifs); |
8897 | 0 | return LoadObjInternal(attrib, shapes, materials, warn, err, sr, |
8898 | 0 | &matFileReader, triangulate, default_vcols_fallback, |
8899 | 0 | filename); |
8900 | 0 | } |
8901 | 0 | #endif // TINYOBJLOADER_USE_MMAP |
8902 | 0 | } |
8903 | | |
8904 | | bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes, |
8905 | | std::vector<material_t> *materials, std::string *warn, |
8906 | | std::string *err, std::istream *inStream, |
8907 | | MaterialReader *readMatFn /*= NULL*/, bool triangulate, |
8908 | 8.54k | bool default_vcols_fallback) { |
8909 | 8.54k | attrib->vertices.clear(); |
8910 | 8.54k | attrib->vertex_weights.clear(); |
8911 | 8.54k | attrib->normals.clear(); |
8912 | 8.54k | attrib->texcoords.clear(); |
8913 | 8.54k | attrib->texcoord_ws.clear(); |
8914 | 8.54k | attrib->colors.clear(); |
8915 | 8.54k | attrib->skin_weights.clear(); |
8916 | 8.54k | shapes->clear(); |
8917 | | |
8918 | 8.54k | StreamReader sr(*inStream); |
8919 | 8.54k | return LoadObjInternal(attrib, shapes, materials, warn, err, sr, |
8920 | 8.54k | readMatFn, triangulate, default_vcols_fallback); |
8921 | 8.54k | } |
8922 | | |
8923 | | |
8924 | | static bool LoadObjWithCallbackInternal(StreamReader &sr, |
8925 | | const callback_t &callback, |
8926 | | void *user_data, |
8927 | | MaterialReader *readMatFn, |
8928 | | std::string *warn, |
8929 | 0 | std::string *err) { |
8930 | 0 | if (sr.has_errors()) { |
8931 | 0 | if (err) { |
8932 | 0 | (*err) += sr.get_errors(); |
8933 | 0 | } |
8934 | 0 | return false; |
8935 | 0 | } |
8936 | | |
8937 | | // material |
8938 | 0 | std::set<std::string> material_filenames; |
8939 | 0 | std::map<std::string, int> material_map; |
8940 | 0 | int material_id = -1; |
8941 | |
|
8942 | 0 | std::vector<index_t> indices; |
8943 | 0 | std::vector<material_t> materials; |
8944 | 0 | std::vector<std::string> names; |
8945 | 0 | names.reserve(2); |
8946 | 0 | std::vector<const char *> names_out; |
8947 | | |
8948 | | // Handle BOM |
8949 | 0 | if (sr.remaining() >= 3 && |
8950 | 0 | static_cast<unsigned char>(sr.peek()) == 0xEF && |
8951 | 0 | static_cast<unsigned char>(sr.peek_at(1)) == 0xBB && |
8952 | 0 | static_cast<unsigned char>(sr.peek_at(2)) == 0xBF) { |
8953 | 0 | sr.advance(3); |
8954 | 0 | } |
8955 | |
|
8956 | 0 | while (!sr.eof()) { |
8957 | 0 | sr.skip_space(); |
8958 | 0 | if (sr.at_line_end()) { sr.skip_line(); continue; } |
8959 | 0 | if (sr.peek() == '#') { sr.skip_line(); continue; } |
8960 | | |
8961 | | // vertex |
8962 | 0 | if (sr.peek() == 'v' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) { |
8963 | 0 | sr.advance(2); |
8964 | 0 | real_t x, y, z; |
8965 | 0 | real_t r, g, b; |
8966 | |
|
8967 | 0 | int num_components = sr_parseVertexWithColor(&x, &y, &z, &r, &g, &b, sr, err, std::string()); |
8968 | 0 | if (num_components < 0) { |
8969 | 0 | return false; |
8970 | 0 | } |
8971 | 0 | if (callback.vertex_cb) { |
8972 | 0 | callback.vertex_cb(user_data, x, y, z, r); |
8973 | 0 | } |
8974 | 0 | if (callback.vertex_color_cb) { |
8975 | 0 | bool found_color = (num_components == 6); |
8976 | 0 | callback.vertex_color_cb(user_data, x, y, z, r, g, b, found_color); |
8977 | 0 | } |
8978 | 0 | sr.skip_line(); |
8979 | 0 | continue; |
8980 | 0 | } |
8981 | | |
8982 | | // normal |
8983 | 0 | if (sr.peek() == 'v' && sr.peek_at(1) == 'n' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
8984 | 0 | sr.advance(3); |
8985 | 0 | real_t x, y, z; |
8986 | 0 | sr_parseReal3(&x, &y, &z, sr); |
8987 | 0 | if (callback.normal_cb) { |
8988 | 0 | callback.normal_cb(user_data, x, y, z); |
8989 | 0 | } |
8990 | 0 | sr.skip_line(); |
8991 | 0 | continue; |
8992 | 0 | } |
8993 | | |
8994 | | // texcoord |
8995 | 0 | if (sr.peek() == 'v' && sr.peek_at(1) == 't' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) { |
8996 | 0 | sr.advance(3); |
8997 | 0 | real_t x, y, z; |
8998 | 0 | sr_parseReal3(&x, &y, &z, sr); |
8999 | 0 | if (callback.texcoord_cb) { |
9000 | 0 | callback.texcoord_cb(user_data, x, y, z); |
9001 | 0 | } |
9002 | 0 | sr.skip_line(); |
9003 | 0 | continue; |
9004 | 0 | } |
9005 | | |
9006 | | // face |
9007 | 0 | if (sr.peek() == 'f' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) { |
9008 | 0 | sr.advance(2); |
9009 | 0 | sr.skip_space(); |
9010 | |
|
9011 | 0 | indices.clear(); |
9012 | 0 | size_t cf_loop_max = sr.remaining() + 1; |
9013 | 0 | size_t cf_loop_iter = 0; |
9014 | 0 | while (!sr.at_line_end() && sr.peek() != '#' && |
9015 | 0 | cf_loop_iter < cf_loop_max) { |
9016 | 0 | vertex_index_t vi = sr_parseRawTriple(sr); |
9017 | |
|
9018 | 0 | index_t idx; |
9019 | 0 | idx.vertex_index = vi.v_idx; |
9020 | 0 | idx.normal_index = vi.vn_idx; |
9021 | 0 | idx.texcoord_index = vi.vt_idx; |
9022 | |
|
9023 | 0 | indices.push_back(idx); |
9024 | 0 | sr.skip_space_and_cr(); |
9025 | 0 | cf_loop_iter++; |
9026 | 0 | } |
9027 | |
|
9028 | 0 | if (callback.index_cb && indices.size() > 0) { |
9029 | 0 | callback.index_cb(user_data, &indices.at(0), |
9030 | 0 | static_cast<int>(indices.size())); |
9031 | 0 | } |
9032 | |
|
9033 | 0 | sr.skip_line(); |
9034 | 0 | continue; |
9035 | 0 | } |
9036 | | |
9037 | | // use mtl |
9038 | 0 | if (sr.match("usemtl", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) { |
9039 | 0 | sr.advance(6); |
9040 | 0 | std::string namebuf = sr_parseString(sr); |
9041 | |
|
9042 | 0 | int newMaterialId = -1; |
9043 | 0 | std::map<std::string, int>::const_iterator it = |
9044 | 0 | material_map.find(namebuf); |
9045 | 0 | if (it != material_map.end()) { |
9046 | 0 | newMaterialId = it->second; |
9047 | 0 | } else { |
9048 | 0 | if (warn && (!callback.usemtl_cb)) { |
9049 | 0 | (*warn) += "material [ " + namebuf + " ] not found in .mtl\n"; |
9050 | 0 | } |
9051 | 0 | } |
9052 | |
|
9053 | 0 | if (newMaterialId != material_id) { |
9054 | 0 | material_id = newMaterialId; |
9055 | 0 | } |
9056 | |
|
9057 | 0 | if (callback.usemtl_cb) { |
9058 | 0 | callback.usemtl_cb(user_data, namebuf.c_str(), material_id); |
9059 | 0 | } |
9060 | |
|
9061 | 0 | sr.skip_line(); |
9062 | 0 | continue; |
9063 | 0 | } |
9064 | | |
9065 | | // load mtl |
9066 | 0 | if (sr.match("mtllib", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) { |
9067 | 0 | if (readMatFn) { |
9068 | 0 | sr.advance(7); |
9069 | |
|
9070 | 0 | std::string line_rest = trimTrailingWhitespace(sr.read_line()); |
9071 | 0 | std::vector<std::string> filenames; |
9072 | 0 | SplitString(line_rest, ' ', '\\', filenames); |
9073 | 0 | RemoveEmptyTokens(&filenames); |
9074 | |
|
9075 | 0 | if (filenames.empty()) { |
9076 | 0 | if (warn) { |
9077 | 0 | (*warn) += |
9078 | 0 | "Looks like empty filename for mtllib. Use default " |
9079 | 0 | "material. \n"; |
9080 | 0 | } |
9081 | 0 | } else { |
9082 | 0 | bool found = false; |
9083 | 0 | for (size_t s = 0; s < filenames.size(); s++) { |
9084 | 0 | if (material_filenames.count(filenames[s]) > 0) { |
9085 | 0 | found = true; |
9086 | 0 | continue; |
9087 | 0 | } |
9088 | | |
9089 | 0 | std::string warn_mtl; |
9090 | 0 | std::string err_mtl; |
9091 | 0 | bool ok = (*readMatFn)(filenames[s].c_str(), &materials, |
9092 | 0 | &material_map, &warn_mtl, &err_mtl); |
9093 | |
|
9094 | 0 | if (warn && (!warn_mtl.empty())) { |
9095 | 0 | (*warn) += warn_mtl; |
9096 | 0 | } |
9097 | |
|
9098 | 0 | if (err && (!err_mtl.empty())) { |
9099 | 0 | (*err) += err_mtl; |
9100 | 0 | } |
9101 | |
|
9102 | 0 | if (ok) { |
9103 | 0 | found = true; |
9104 | 0 | material_filenames.insert(filenames[s]); |
9105 | 0 | break; |
9106 | 0 | } |
9107 | 0 | } |
9108 | |
|
9109 | 0 | if (!found) { |
9110 | 0 | if (warn) { |
9111 | 0 | (*warn) += |
9112 | 0 | "Failed to load material file(s). Use default " |
9113 | 0 | "material.\n"; |
9114 | 0 | } |
9115 | 0 | } else { |
9116 | 0 | if (callback.mtllib_cb && !materials.empty()) { |
9117 | 0 | callback.mtllib_cb(user_data, &materials.at(0), |
9118 | 0 | static_cast<int>(materials.size())); |
9119 | 0 | } |
9120 | 0 | } |
9121 | 0 | } |
9122 | 0 | } |
9123 | |
|
9124 | 0 | sr.skip_line(); |
9125 | 0 | continue; |
9126 | 0 | } |
9127 | | |
9128 | | // group name |
9129 | 0 | if (sr.peek() == 'g' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) { |
9130 | 0 | names.clear(); |
9131 | |
|
9132 | 0 | size_t cg_loop_max = sr.remaining() + 1; |
9133 | 0 | size_t cg_loop_iter = 0; |
9134 | 0 | while (!sr.at_line_end() && sr.peek() != '#' && |
9135 | 0 | cg_loop_iter < cg_loop_max) { |
9136 | 0 | std::string str = sr_parseString(sr); |
9137 | 0 | names.push_back(str); |
9138 | 0 | sr.skip_space_and_cr(); |
9139 | 0 | cg_loop_iter++; |
9140 | 0 | } |
9141 | |
|
9142 | 0 | assert(names.size() > 0); |
9143 | | |
9144 | 0 | if (callback.group_cb) { |
9145 | 0 | if (names.size() > 1) { |
9146 | 0 | names_out.resize(names.size() - 1); |
9147 | 0 | for (size_t j = 0; j < names_out.size(); j++) { |
9148 | 0 | names_out[j] = names[j + 1].c_str(); |
9149 | 0 | } |
9150 | 0 | callback.group_cb(user_data, &names_out.at(0), |
9151 | 0 | static_cast<int>(names_out.size())); |
9152 | |
|
9153 | 0 | } else { |
9154 | 0 | callback.group_cb(user_data, NULL, 0); |
9155 | 0 | } |
9156 | 0 | } |
9157 | |
|
9158 | 0 | sr.skip_line(); |
9159 | 0 | continue; |
9160 | 0 | } |
9161 | | |
9162 | | // object name |
9163 | 0 | if (sr.peek() == 'o' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) { |
9164 | 0 | sr.advance(2); |
9165 | 0 | std::string object_name = sr.read_line(); |
9166 | |
|
9167 | 0 | if (callback.object_cb) { |
9168 | 0 | callback.object_cb(user_data, object_name.c_str()); |
9169 | 0 | } |
9170 | |
|
9171 | 0 | sr.skip_line(); |
9172 | 0 | continue; |
9173 | 0 | } |
9174 | | |
9175 | | #if 0 // @todo |
9176 | | if (sr.peek() == 't' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) { |
9177 | | tag_t tag; |
9178 | | |
9179 | | sr.advance(2); |
9180 | | tag.name = sr_parseString(sr); |
9181 | | |
9182 | | tag_sizes ts = sr_parseTagTriple(sr); |
9183 | | |
9184 | | tag.intValues.resize(static_cast<size_t>(ts.num_ints)); |
9185 | | |
9186 | | for (size_t i = 0; i < static_cast<size_t>(ts.num_ints); ++i) { |
9187 | | tag.intValues[i] = sr_parseInt(sr); |
9188 | | } |
9189 | | |
9190 | | tag.floatValues.resize(static_cast<size_t>(ts.num_reals)); |
9191 | | for (size_t i = 0; i < static_cast<size_t>(ts.num_reals); ++i) { |
9192 | | tag.floatValues[i] = sr_parseReal(sr); |
9193 | | } |
9194 | | |
9195 | | tag.stringValues.resize(static_cast<size_t>(ts.num_strings)); |
9196 | | for (size_t i = 0; i < static_cast<size_t>(ts.num_strings); ++i) { |
9197 | | tag.stringValues[i] = sr_parseString(sr); |
9198 | | } |
9199 | | |
9200 | | tags.push_back(tag); |
9201 | | } |
9202 | | #endif |
9203 | | |
9204 | | // Ignore unknown command. |
9205 | 0 | sr.skip_line(); |
9206 | 0 | } |
9207 | | |
9208 | 0 | return true; |
9209 | 0 | } |
9210 | | |
9211 | | bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, |
9212 | | void *user_data /*= NULL*/, |
9213 | | MaterialReader *readMatFn /*= NULL*/, |
9214 | | std::string *warn, /* = NULL*/ |
9215 | 0 | std::string *err /*= NULL*/) { |
9216 | 0 | StreamReader sr(inStream); |
9217 | 0 | return LoadObjWithCallbackInternal(sr, callback, user_data, readMatFn, |
9218 | 0 | warn, err); |
9219 | 0 | } |
9220 | | |
9221 | | bool ObjReader::ParseFromFile(const std::string &filename, |
9222 | 0 | const ObjReaderConfig &config) { |
9223 | 0 | std::string mtl_search_path; |
9224 | |
|
9225 | 0 | if (config.mtl_search_path.empty()) { |
9226 | | // |
9227 | | // split at last '/'(for unixish system) or '\\'(for windows) to get |
9228 | | // the base directory of .obj file |
9229 | | // |
9230 | 0 | size_t pos = filename.find_last_of("/\\"); |
9231 | 0 | if (pos != std::string::npos) { |
9232 | 0 | mtl_search_path = filename.substr(0, pos); |
9233 | 0 | } |
9234 | 0 | } else { |
9235 | 0 | mtl_search_path = config.mtl_search_path; |
9236 | 0 | } |
9237 | |
|
9238 | 0 | valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_, |
9239 | 0 | filename.c_str(), mtl_search_path.c_str(), |
9240 | 0 | config.triangulate, config.vertex_color); |
9241 | |
|
9242 | 0 | return valid_; |
9243 | 0 | } |
9244 | | |
9245 | | bool ObjReader::ParseFromString(const std::string &obj_text, |
9246 | | const std::string &mtl_text, |
9247 | 8.54k | const ObjReaderConfig &config) { |
9248 | 8.54k | std::stringbuf obj_buf(obj_text); |
9249 | 8.54k | std::stringbuf mtl_buf(mtl_text); |
9250 | | |
9251 | 8.54k | std::istream obj_ifs(&obj_buf); |
9252 | 8.54k | std::istream mtl_ifs(&mtl_buf); |
9253 | | |
9254 | 8.54k | MaterialStreamReader mtl_ss(mtl_ifs); |
9255 | | |
9256 | 8.54k | valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_, |
9257 | 8.54k | &obj_ifs, &mtl_ss, config.triangulate, config.vertex_color); |
9258 | | |
9259 | 8.54k | return valid_; |
9260 | 8.54k | } |
9261 | | |
9262 | | #ifdef __clang__ |
9263 | | #pragma clang diagnostic pop |
9264 | | #endif |
9265 | | } // namespace tinyobj |
9266 | | |
9267 | | #endif |