Mesh_analyzer
Loading...
Searching...
No Matches
tiny_obj_loader.h
1/*
2The MIT License (MIT)
3
4Copyright (c) 2012-Present, Syoyo Fujita and many contributors.
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in
14all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22THE 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
71namespace 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"
146typedef double real_t;
147#else
148//#pragma message "using float"
149typedef float real_t;
150#endif
151
152typedef 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
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
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
328struct 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
337 int joint_id;
338 real_t weight;
339};
340
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.
350struct index_t {
351 int vertex_index;
352 int normal_index;
353 int texcoord_index;
354};
355
356struct 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
372struct 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
378struct points_t {
379 std::vector<index_t> indices; // indices for points
380};
381
382struct shape_t {
383 std::string name;
384 mesh_t mesh;
385 lines_t lines;
386 points_t points;
387};
388
389// Vertex attributes
390struct 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 attrib_t() {}
414
415 //
416 // For pybind11
417 //
418 const std::vector<real_t> &GetVertices() const { return vertices; }
419
420 const std::vector<real_t> &GetVertexWeights() const { return vertex_weights; }
421};
422
423struct 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 object_cb(NULL) {}
459};
460
461class MaterialReader {
462 public:
463 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
475class MaterialFileReader : public MaterialReader {
476 public:
477 // Path could contain separator(';' in Windows, ':' in Posix)
478 explicit MaterialFileReader(const std::string &mtl_basedir)
479 : m_mtlBaseDir(mtl_basedir) {}
480 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
493class MaterialStreamReader : public MaterialReader {
494 public:
495 explicit MaterialStreamReader(std::istream &inStream)
496 : m_inStream(inStream) {}
497 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
508struct 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
521
527 std::string mtl_search_path;
528
529 ObjReaderConfig()
530 : triangulate(true), triangulation_method("simple"), vertex_color(true) {}
531};
532
536class ObjReader {
537 public:
538 ObjReader() : valid_(false) {}
539
546 bool ParseFromFile(const std::string &filename,
547 const ObjReaderConfig &config = ObjReaderConfig());
548
558 bool ParseFromString(const std::string &obj_text, const std::string &mtl_text,
559 const ObjReaderConfig &config = ObjReaderConfig());
560
564 bool Valid() const { return valid_; }
565
566 const attrib_t &GetAttrib() const { return attrib_; }
567
568 const std::vector<shape_t> &GetShapes() const { return shapes_; }
569
570 const std::vector<material_t> &GetMaterials() const { return materials_; }
571
575 const std::string &Warning() const { return warning_; }
576
580 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
594
607bool 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
619bool 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
628bool 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
635void 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
647bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt,
648 const char *linebuf);
649
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).
719static 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.
737static 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
786namespace tinyobj_ff {
787
788// --- integral_constant, true_type, false_type ---
789template <typename T, T V>
790struct 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};
796typedef integral_constant<bool, true> true_type;
797typedef integral_constant<bool, false> false_type;
798
799// --- is_same ---
800template <typename T, typename U> struct is_same : false_type {};
801template <typename T> struct is_same<T, T> : true_type {};
802
803// --- enable_if ---
804template <bool B, typename T = void> struct enable_if {};
805template <typename T> struct enable_if<true, T> { typedef T type; };
806
807// --- conditional ---
808template <bool B, typename T, typename F> struct conditional { typedef T type; };
809template <typename T, typename F> struct conditional<false, T, F> { typedef F type; };
810
811// --- is_integral ---
812template <typename T> struct is_integral : false_type {};
813template <> struct is_integral<bool> : true_type {};
814template <> struct is_integral<char> : true_type {};
815template <> struct is_integral<signed char> : true_type {};
816template <> struct is_integral<unsigned char> : true_type {};
817template <> struct is_integral<short> : true_type {};
818template <> struct is_integral<unsigned short> : true_type {};
819template <> struct is_integral<int> : true_type {};
820template <> struct is_integral<unsigned int> : true_type {};
821template <> struct is_integral<long> : true_type {};
822template <> struct is_integral<unsigned long> : true_type {};
823template <> struct is_integral<long long> : true_type {};
824template <> struct is_integral<unsigned long long> : true_type {};
825template <> struct is_integral<wchar_t> : true_type {};
826template <> struct is_integral<char16_t> : true_type {};
827template <> struct is_integral<char32_t> : true_type {};
828
829// --- is_signed ---
830template <typename T> struct is_signed : integral_constant<bool, T(-1) < T(0)> {};
831
832// --- underlying_type (uses compiler builtin) ---
833template <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) ---
838enum 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) ---
841template <typename T>
842inline T min_val(T a, T b) { return (b < a) ? b : a; }
843
844// --- copy_n ---
845template <typename InputIt, typename Size, typename OutputIt>
846inline OutputIt copy_n(InputIt first, Size count, OutputIt result) {
847 for (Size i = 0; i < count; ++i) *result++ = *first++;
848 return result;
849}
850
851// --- copy_backward ---
852template <typename BidirIt1, typename BidirIt2>
853inline BidirIt2 copy_backward(BidirIt1 first, BidirIt1 last, BidirIt2 d_last) {
854 while (first != last) *(--d_last) = *(--last);
855 return d_last;
856}
857
858// --- fill ---
859template <typename ForwardIt, typename T>
860inline void fill(ForwardIt first, ForwardIt last, const T &value) {
861 for (; first != last; ++first) *first = value;
862}
863
864// --- distance ---
865template <typename It>
866inline typename conditional<true, long long, It>::type
867distance(It first, It last) {
868 return last - first;
869}
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#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
1049namespace fast_float {
1050
1051enum class chars_format : uint64_t;
1052
1053namespace detail {
1054constexpr chars_format basic_json_fmt = chars_format(1 << 5);
1055constexpr chars_format basic_fortran_fmt = chars_format(1 << 6);
1056} // namespace detail
1057
1058enum 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
1073template <typename UC> struct from_chars_result_t {
1074 UC const *ptr;
1075 tinyobj_ff::ff_errc ec;
1076};
1077
1078using from_chars_result = from_chars_result_t<char>;
1079
1080template <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 : format(fmt), decimal_point(dot), base(b) {}
1084
1086 chars_format format;
1088 UC decimal_point;
1090 int base;
1091};
1092
1093using 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 { ((void)(x)); }
1211#endif
1212
1213#ifndef FASTFLOAT_DEBUG_ASSERT
1214#define FASTFLOAT_DEBUG_ASSERT(x) \
1215 { ((void)(x)); }
1216#endif
1217
1218// rust style `try!()` macro, or `?` operator
1219#define FASTFLOAT_TRY(x) \
1220 { \
1221 if (!(x)) \
1222 return false; \
1223 }
1224
1225#define FASTFLOAT_ENABLE_IF(...) \
1226 typename tinyobj_ff::enable_if<(__VA_ARGS__), int>::type
1227
1228namespace fast_float {
1229
1230fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() {
1231#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED
1232 return std::is_constant_evaluated();
1233#else
1234 return false;
1235#endif
1236}
1237
1238template <typename T>
1239struct 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
1257template <typename T>
1258using 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
1265template <typename T> struct is_supported_integer_type : tinyobj_ff::is_integral<T> {};
1266
1267template <typename UC>
1268struct 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.
1280template <typename UC>
1281inline FASTFLOAT_CONSTEXPR14 bool
1282fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
1283 size_t length) {
1284 for (size_t i = 0; i < length; ++i) {
1285 UC const actual = actual_mixedcase[i];
1286 if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) {
1287 return false;
1288 }
1289 }
1290 return true;
1291}
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
1298template <typename T> struct span {
1299 T const *ptr;
1300 size_t length;
1301
1302 constexpr span(T const *_ptr, size_t _length) : ptr(_ptr), length(_length) {}
1303
1304 constexpr span() : ptr(nullptr), length(0) {}
1305
1306 constexpr size_t len() const noexcept { return length; }
1307
1308 FASTFLOAT_CONSTEXPR14 const T &operator[](size_t index) const noexcept {
1309 FASTFLOAT_DEBUG_ASSERT(index < length);
1310 return ptr[index];
1311 }
1312};
1313
1314struct value128 {
1315 uint64_t low;
1316 uint64_t high;
1317
1318 constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
1319
1320 constexpr value128() : low(0), high(0) {}
1321};
1322
1323/* Helper C++14 constexpr generic implementation of leading_zeroes */
1324fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int
1325leading_zeroes_generic(uint64_t input_num, int last_bit = 0) {
1326 if (input_num & uint64_t(0xffffffff00000000)) {
1327 input_num >>= 32;
1328 last_bit |= 32;
1329 }
1330 if (input_num & uint64_t(0xffff0000)) {
1331 input_num >>= 16;
1332 last_bit |= 16;
1333 }
1334 if (input_num & uint64_t(0xff00)) {
1335 input_num >>= 8;
1336 last_bit |= 8;
1337 }
1338 if (input_num & uint64_t(0xf0)) {
1339 input_num >>= 4;
1340 last_bit |= 4;
1341 }
1342 if (input_num & uint64_t(0xc)) {
1343 input_num >>= 2;
1344 last_bit |= 2;
1345 }
1346 if (input_num & uint64_t(0x2)) { /* input_num >>= 1; */
1347 last_bit |= 1;
1348 }
1349 return 63 - last_bit;
1350}
1351
1352/* result might be undefined when input_num is zero */
1353fastfloat_really_inline FASTFLOAT_CONSTEXPR20 int
1354leading_zeroes(uint64_t input_num) {
1355 assert(input_num > 0);
1356 if (cpp20_and_in_constexpr()) {
1357 return leading_zeroes_generic(input_num);
1358 }
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 return __builtin_clzll(input_num);
1371#endif
1372}
1373
1374// slow emulation routine for 32-bit
1375fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) {
1376 return x * (uint64_t)y;
1377}
1378
1379fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
1380umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
1381 uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
1382 uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
1383 uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
1384 uint64_t adbc_carry = (uint64_t)(adbc < ad);
1385 uint64_t lo = bd + (adbc << 32);
1386 *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
1387 (adbc_carry << 32) + (uint64_t)(lo < bd);
1388 return lo;
1389}
1390
1391#ifdef FASTFLOAT_32BIT
1392
1393// slow emulation routine for 32-bit
1394#if !defined(__MINGW64__)
1395fastfloat_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
1405fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
1406full_multiplication(uint64_t a, uint64_t b) {
1407 if (cpp20_and_in_constexpr()) {
1408 value128 answer;
1409 answer.low = umul128_generic(a, b, &answer.high);
1410 return answer;
1411 }
1412 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 answer.low = uint64_t(r);
1424 answer.high = uint64_t(r >> 64);
1425#else
1426 answer.low = umul128_generic(a, b, &answer.high);
1427#endif
1428 return answer;
1429}
1430
1431struct adjusted_mantissa {
1432 uint64_t mantissa{0};
1433 int32_t power2{0}; // a negative value indicates an invalid result
1434 adjusted_mantissa() = default;
1435
1436 constexpr bool operator==(adjusted_mantissa const &o) const {
1437 return mantissa == o.mantissa && power2 == o.power2;
1438 }
1439
1440 constexpr bool operator!=(adjusted_mantissa const &o) const {
1441 return mantissa != o.mantissa || power2 != o.power2;
1442 }
1443};
1444
1445// Bias so we can get the real exponent with an invalid adjusted_mantissa.
1446constexpr static int32_t invalid_am_bias = -0x8000;
1447
1448// used for binary_format_lookup_tables<T>::max_mantissa
1449constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5;
1450
1451template <typename T, typename U = void> struct binary_format_lookup_tables;
1452
1453template <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
1477template <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
1521template <typename U>
1522constexpr double binary_format_lookup_tables<double, U>::powers_of_ten[];
1523
1524template <typename U>
1525constexpr uint64_t binary_format_lookup_tables<double, U>::max_mantissa[];
1526
1527#endif
1528
1529template <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
1552template <typename U>
1553constexpr float binary_format_lookup_tables<float, U>::powers_of_ten[];
1554
1555template <typename U>
1556constexpr uint64_t binary_format_lookup_tables<float, U>::max_mantissa[];
1557
1558#endif
1559
1560template <>
1561inline 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 return -22;
1566#endif
1567}
1568
1569template <>
1570inline constexpr int binary_format<float>::min_exponent_fast_path() {
1571#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
1572 return 0;
1573#else
1574 return -10;
1575#endif
1576}
1577
1578template <>
1579inline constexpr int binary_format<double>::mantissa_explicit_bits() {
1580 return 52;
1581}
1582
1583template <>
1584inline constexpr int binary_format<float>::mantissa_explicit_bits() {
1585 return 23;
1586}
1587
1588template <>
1589inline constexpr int binary_format<double>::max_exponent_round_to_even() {
1590 return 23;
1591}
1592
1593template <>
1594inline constexpr int binary_format<float>::max_exponent_round_to_even() {
1595 return 10;
1596}
1597
1598template <>
1599inline constexpr int binary_format<double>::min_exponent_round_to_even() {
1600 return -4;
1601}
1602
1603template <>
1604inline constexpr int binary_format<float>::min_exponent_round_to_even() {
1605 return -17;
1606}
1607
1608template <> inline constexpr int binary_format<double>::minimum_exponent() {
1609 return -1023;
1610}
1611
1612template <> inline constexpr int binary_format<float>::minimum_exponent() {
1613 return -127;
1614}
1615
1616template <> inline constexpr int binary_format<double>::infinite_power() {
1617 return 0x7FF;
1618}
1619
1620template <> inline constexpr int binary_format<float>::infinite_power() {
1621 return 0xFF;
1622}
1623
1624template <> inline constexpr int binary_format<double>::sign_index() {
1625 return 63;
1626}
1627
1628template <> inline constexpr int binary_format<float>::sign_index() {
1629 return 31;
1630}
1631
1632template <>
1633inline constexpr int binary_format<double>::max_exponent_fast_path() {
1634 return 22;
1635}
1636
1637template <>
1638inline constexpr int binary_format<float>::max_exponent_fast_path() {
1639 return 10;
1640}
1641
1642template <>
1643inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
1644 return uint64_t(2) << mantissa_explicit_bits();
1645}
1646
1647template <>
1648inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
1649 return uint64_t(2) << mantissa_explicit_bits();
1650}
1651
1652// credit: Jakub Jelínek
1653#ifdef __STDCPP_FLOAT16_T__
1654template <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
1670template <typename U>
1671constexpr std::float16_t
1672 binary_format_lookup_tables<std::float16_t, U>::powers_of_ten[];
1673
1674template <typename U>
1675constexpr uint64_t
1676 binary_format_lookup_tables<std::float16_t, U>::max_mantissa[];
1677
1678#endif
1679
1680template <>
1681inline constexpr std::float16_t
1682binary_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
1687template <>
1688inline constexpr binary_format<std::float16_t>::equiv_uint
1689binary_format<std::float16_t>::exponent_mask() {
1690 return 0x7C00;
1691}
1692
1693template <>
1694inline constexpr binary_format<std::float16_t>::equiv_uint
1695binary_format<std::float16_t>::mantissa_mask() {
1696 return 0x03FF;
1697}
1698
1699template <>
1700inline constexpr binary_format<std::float16_t>::equiv_uint
1701binary_format<std::float16_t>::hidden_bit_mask() {
1702 return 0x0400;
1703}
1704
1705template <>
1706inline constexpr int binary_format<std::float16_t>::max_exponent_fast_path() {
1707 return 4;
1708}
1709
1710template <>
1711inline constexpr int binary_format<std::float16_t>::mantissa_explicit_bits() {
1712 return 10;
1713}
1714
1715template <>
1716inline constexpr uint64_t
1717binary_format<std::float16_t>::max_mantissa_fast_path() {
1718 return uint64_t(2) << mantissa_explicit_bits();
1719}
1720
1721template <>
1722inline constexpr uint64_t
1723binary_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
1731template <>
1732inline constexpr int binary_format<std::float16_t>::min_exponent_fast_path() {
1733 return 0;
1734}
1735
1736template <>
1737inline constexpr int
1738binary_format<std::float16_t>::max_exponent_round_to_even() {
1739 return 5;
1740}
1741
1742template <>
1743inline constexpr int
1744binary_format<std::float16_t>::min_exponent_round_to_even() {
1745 return -22;
1746}
1747
1748template <>
1749inline constexpr int binary_format<std::float16_t>::minimum_exponent() {
1750 return -15;
1751}
1752
1753template <>
1754inline constexpr int binary_format<std::float16_t>::infinite_power() {
1755 return 0x1F;
1756}
1757
1758template <> inline constexpr int binary_format<std::float16_t>::sign_index() {
1759 return 15;
1760}
1761
1762template <>
1763inline constexpr int binary_format<std::float16_t>::largest_power_of_ten() {
1764 return 4;
1765}
1766
1767template <>
1768inline constexpr int binary_format<std::float16_t>::smallest_power_of_ten() {
1769 return -27;
1770}
1771
1772template <>
1773inline 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__
1780template <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
1793template <typename U>
1794constexpr std::bfloat16_t
1795 binary_format_lookup_tables<std::bfloat16_t, U>::powers_of_ten[];
1796
1797template <typename U>
1798constexpr uint64_t
1799 binary_format_lookup_tables<std::bfloat16_t, U>::max_mantissa[];
1800
1801#endif
1802
1803template <>
1804inline constexpr std::bfloat16_t
1805binary_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
1810template <>
1811inline constexpr int binary_format<std::bfloat16_t>::max_exponent_fast_path() {
1812 return 3;
1813}
1814
1815template <>
1816inline constexpr binary_format<std::bfloat16_t>::equiv_uint
1817binary_format<std::bfloat16_t>::exponent_mask() {
1818 return 0x7F80;
1819}
1820
1821template <>
1822inline constexpr binary_format<std::bfloat16_t>::equiv_uint
1823binary_format<std::bfloat16_t>::mantissa_mask() {
1824 return 0x007F;
1825}
1826
1827template <>
1828inline constexpr binary_format<std::bfloat16_t>::equiv_uint
1829binary_format<std::bfloat16_t>::hidden_bit_mask() {
1830 return 0x0080;
1831}
1832
1833template <>
1834inline constexpr int binary_format<std::bfloat16_t>::mantissa_explicit_bits() {
1835 return 7;
1836}
1837
1838template <>
1839inline constexpr uint64_t
1840binary_format<std::bfloat16_t>::max_mantissa_fast_path() {
1841 return uint64_t(2) << mantissa_explicit_bits();
1842}
1843
1844template <>
1845inline constexpr uint64_t
1846binary_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
1854template <>
1855inline constexpr int binary_format<std::bfloat16_t>::min_exponent_fast_path() {
1856 return 0;
1857}
1858
1859template <>
1860inline constexpr int
1861binary_format<std::bfloat16_t>::max_exponent_round_to_even() {
1862 return 3;
1863}
1864
1865template <>
1866inline constexpr int
1867binary_format<std::bfloat16_t>::min_exponent_round_to_even() {
1868 return -24;
1869}
1870
1871template <>
1872inline constexpr int binary_format<std::bfloat16_t>::minimum_exponent() {
1873 return -127;
1874}
1875
1876template <>
1877inline constexpr int binary_format<std::bfloat16_t>::infinite_power() {
1878 return 0xFF;
1879}
1880
1881template <> inline constexpr int binary_format<std::bfloat16_t>::sign_index() {
1882 return 15;
1883}
1884
1885template <>
1886inline constexpr int binary_format<std::bfloat16_t>::largest_power_of_ten() {
1887 return 38;
1888}
1889
1890template <>
1891inline constexpr int binary_format<std::bfloat16_t>::smallest_power_of_ten() {
1892 return -60;
1893}
1894
1895template <>
1896inline constexpr size_t binary_format<std::bfloat16_t>::max_digits() {
1897 return 98;
1898}
1899#endif // __STDCPP_BFLOAT16_T__
1900
1901template <>
1902inline constexpr uint64_t
1903binary_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 return (void)max_mantissa[0], max_mantissa[power];
1909}
1910
1911template <>
1912inline constexpr uint64_t
1913binary_format<float>::max_mantissa_fast_path(int64_t power) {
1914 // caller is responsible to ensure that
1915 // power >= 0 && power <= 10
1916 //
1917 // Work around clang bug https://godbolt.org/z/zedh7rrhc
1918 return (void)max_mantissa[0], max_mantissa[power];
1919}
1920
1921template <>
1922inline constexpr double
1923binary_format<double>::exact_power_of_ten(int64_t power) {
1924 // Work around clang bug https://godbolt.org/z/zedh7rrhc
1925 return (void)powers_of_ten[0], powers_of_ten[power];
1926}
1927
1928template <>
1929inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
1930 // Work around clang bug https://godbolt.org/z/zedh7rrhc
1931 return (void)powers_of_ten[0], powers_of_ten[power];
1932}
1933
1934template <> inline constexpr int binary_format<double>::largest_power_of_ten() {
1935 return 308;
1936}
1937
1938template <> inline constexpr int binary_format<float>::largest_power_of_ten() {
1939 return 38;
1940}
1941
1942template <>
1943inline constexpr int binary_format<double>::smallest_power_of_ten() {
1944 return -342;
1945}
1946
1947template <> inline constexpr int binary_format<float>::smallest_power_of_ten() {
1948 return -64;
1949}
1950
1951template <> inline constexpr size_t binary_format<double>::max_digits() {
1952 return 769;
1953}
1954
1955template <> inline constexpr size_t binary_format<float>::max_digits() {
1956 return 114;
1957}
1958
1959template <>
1960inline constexpr binary_format<float>::equiv_uint
1961binary_format<float>::exponent_mask() {
1962 return 0x7F800000;
1963}
1964
1965template <>
1966inline constexpr binary_format<double>::equiv_uint
1967binary_format<double>::exponent_mask() {
1968 return 0x7FF0000000000000;
1969}
1970
1971template <>
1972inline constexpr binary_format<float>::equiv_uint
1973binary_format<float>::mantissa_mask() {
1974 return 0x007FFFFF;
1975}
1976
1977template <>
1978inline constexpr binary_format<double>::equiv_uint
1979binary_format<double>::mantissa_mask() {
1980 return 0x000FFFFFFFFFFFFF;
1981}
1982
1983template <>
1984inline constexpr binary_format<float>::equiv_uint
1985binary_format<float>::hidden_bit_mask() {
1986 return 0x00800000;
1987}
1988
1989template <>
1990inline constexpr binary_format<double>::equiv_uint
1991binary_format<double>::hidden_bit_mask() {
1992 return 0x0010000000000000;
1993}
1994
1995template <typename T>
1996fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
1997to_float(bool negative, adjusted_mantissa am, T &value) {
1998 using equiv_uint = equiv_uint_t<T>;
1999 equiv_uint word = equiv_uint(am.mantissa);
2000 word = equiv_uint(word | equiv_uint(am.power2)
2001 << binary_format<T>::mantissa_explicit_bits());
2002 word =
2003 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 ::memcpy(&value, &word, sizeof(T));
2008#endif
2009}
2010
2011template <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
2028template <typename T> constexpr bool space_lut<T>::value[];
2029
2030#endif
2031
2032template <typename UC> constexpr bool is_space(UC c) {
2033 return c < 256 && space_lut<>::value[uint8_t(c)];
2034}
2035
2036template <typename UC> static constexpr uint64_t int_cmp_zeros() {
2037 static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4),
2038 "Unsupported character size");
2039 return (sizeof(UC) == 1) ? 0x3030303030303030
2040 : (sizeof(UC) == 2)
2041 ? (uint64_t(UC('0')) << 48 | uint64_t(UC('0')) << 32 |
2042 uint64_t(UC('0')) << 16 | UC('0'))
2043 : (uint64_t(UC('0')) << 32 | UC('0'));
2044}
2045
2046template <typename UC> static constexpr int int_cmp_len() {
2047 return sizeof(uint64_t) / sizeof(UC);
2048}
2049
2050template <typename UC> constexpr UC const *str_const_nan();
2051
2052template <> constexpr char const *str_const_nan<char>() { return "nan"; }
2053
2054template <> constexpr wchar_t const *str_const_nan<wchar_t>() { return L"nan"; }
2055
2056template <> constexpr char16_t const *str_const_nan<char16_t>() {
2057 return u"nan";
2058}
2059
2060template <> constexpr char32_t const *str_const_nan<char32_t>() {
2061 return U"nan";
2062}
2063
2064#ifdef __cpp_char8_t
2065template <> constexpr char8_t const *str_const_nan<char8_t>() {
2066 return u8"nan";
2067}
2068#endif
2069
2070template <typename UC> constexpr UC const *str_const_inf();
2071
2072template <> constexpr char const *str_const_inf<char>() { return "infinity"; }
2073
2074template <> constexpr wchar_t const *str_const_inf<wchar_t>() {
2075 return L"infinity";
2076}
2077
2078template <> constexpr char16_t const *str_const_inf<char16_t>() {
2079 return u"infinity";
2080}
2081
2082template <> constexpr char32_t const *str_const_inf<char32_t>() {
2083 return U"infinity";
2084}
2085
2086#ifdef __cpp_char8_t
2087template <> constexpr char8_t const *str_const_inf<char8_t>() {
2088 return u8"infinity";
2089}
2090#endif
2091
2092template <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
2134template <typename T> constexpr uint8_t int_luts<T>::chdigit[];
2135
2136template <typename T> constexpr size_t int_luts<T>::maxdigits_u64[];
2137
2138template <typename T> constexpr uint64_t int_luts<T>::min_safe_u64[];
2139
2140#endif
2141
2142template <typename UC>
2143fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) {
2144 return int_luts<>::chdigit[static_cast<unsigned char>(c)];
2145}
2146
2147fastfloat_really_inline constexpr size_t max_digits_u64(int base) {
2148 return int_luts<>::maxdigits_u64[base - 2];
2149}
2150
2151// If a u64 is exactly max_digits_u64() in length, this is
2152// the value below which it has definitely overflowed.
2153fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) {
2154 return int_luts<>::min_safe_u64[base - 2];
2155}
2156
2157static_assert(tinyobj_ff::is_same<equiv_uint_t<double>, uint64_t>::value,
2158 "equiv_uint should be uint64_t for double");
2159static_assert(std::numeric_limits<double>::is_iec559,
2160 "double must fulfill the requirements of IEC 559 (IEEE 754)");
2161
2162static_assert(tinyobj_ff::is_same<equiv_uint_t<float>, uint32_t>::value,
2163 "equiv_uint should be uint32_t for float");
2164static_assert(std::numeric_limits<float>::is_iec559,
2165 "float must fulfill the requirements of IEC 559 (IEEE 754)");
2166
2167#ifdef __STDCPP_FLOAT64_T__
2168static_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");
2170static_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__
2176static_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");
2178static_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__
2184static_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");
2187static_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__
2193static_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");
2196static_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
2201constexpr chars_format operator~(chars_format rhs) noexcept {
2202 using int_type = tinyobj_ff::underlying_type<chars_format>::type;
2203 return static_cast<chars_format>(~static_cast<int_type>(rhs));
2204}
2205
2206constexpr chars_format operator&(chars_format lhs, chars_format rhs) noexcept {
2207 using int_type = tinyobj_ff::underlying_type<chars_format>::type;
2208 return static_cast<chars_format>(static_cast<int_type>(lhs) &
2209 static_cast<int_type>(rhs));
2210}
2211
2212constexpr chars_format operator|(chars_format lhs, chars_format rhs) noexcept {
2213 using int_type = tinyobj_ff::underlying_type<chars_format>::type;
2214 return static_cast<chars_format>(static_cast<int_type>(lhs) |
2215 static_cast<int_type>(rhs));
2216}
2217
2218constexpr chars_format operator^(chars_format lhs, chars_format rhs) noexcept {
2219 using int_type = tinyobj_ff::underlying_type<chars_format>::type;
2220 return static_cast<chars_format>(static_cast<int_type>(lhs) ^
2221 static_cast<int_type>(rhs));
2222}
2223
2224fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
2225operator&=(chars_format &lhs, chars_format rhs) noexcept {
2226 return lhs = (lhs & rhs);
2227}
2228
2229fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
2230operator|=(chars_format &lhs, chars_format rhs) noexcept {
2231 return lhs = (lhs | rhs);
2232}
2233
2234fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
2235operator^=(chars_format &lhs, chars_format rhs) noexcept {
2236 return lhs = (lhs ^ rhs);
2237}
2238
2239namespace detail {
2240// adjust for deprecated feature macros
2241constexpr chars_format adjust_for_feature_macros(chars_format fmt) {
2242 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 ;
2250}
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
2262namespace fast_float {
2288template <typename T, typename UC = char,
2289 typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>::value)>
2290FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
2291from_chars(UC const *first, UC const *last, T &value,
2292 chars_format fmt = chars_format::general) noexcept;
2293
2298template <typename T, typename UC = char>
2299FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
2300from_chars_advanced(UC const *first, UC const *last, T &value,
2301 parse_options_t<UC> options) noexcept;
2302
2306template <typename T, typename UC = char,
2307 typename = FASTFLOAT_ENABLE_IF(is_supported_integer_type<T>::value)>
2308FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
2309from_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
2331namespace fast_float {
2332
2333template <typename UC> fastfloat_really_inline constexpr bool has_simd_opt() {
2334#ifdef FASTFLOAT_HAS_SIMD
2335 return tinyobj_ff::is_same<UC, char16_t>::value;
2336#else
2337 return false;
2338#endif
2339}
2340
2341// Next function can be micro-optimized, but compilers are entirely
2342// able to optimize it well.
2343template <typename UC>
2344fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
2345 return !(c > UC('9') || c < UC('0'));
2346}
2347
2348fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
2349 return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 |
2350 (val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 |
2351 (val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 |
2352 (val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
2353}
2354
2355// Read 8 UC into a u64. Truncates UC if not char.
2356template <typename UC>
2357fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
2358read8_to_u64(UC const *chars) {
2359 if (cpp20_and_in_constexpr() || !tinyobj_ff::is_same<UC, char>::value) {
2360 uint64_t val = 0;
2361 for (int i = 0; i < 8; ++i) {
2362 val |= uint64_t(uint8_t(*chars)) << (i * 8);
2363 ++chars;
2364 }
2365 return val;
2366 }
2367 uint64_t val;
2368 ::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 return val;
2374}
2375
2376#ifdef FASTFLOAT_SSE2
2377
2378fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) {
2379 FASTFLOAT_SIMD_DISABLE_WARNINGS
2380 __m128i const packed = _mm_packus_epi16(data, data);
2381#ifdef FASTFLOAT_64BIT
2382 return uint64_t(_mm_cvtsi128_si64(packed));
2383#else
2384 uint64_t value;
2385 // Visual Studio + older versions of GCC don't support _mm_storeu_si64
2386 _mm_storel_epi64(reinterpret_cast<__m128i *>(&value), packed);
2387 return value;
2388#endif
2389 FASTFLOAT_SIMD_RESTORE_WARNINGS
2390}
2391
2392fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) {
2393 FASTFLOAT_SIMD_DISABLE_WARNINGS
2394 return simd_read8_to_u64(
2395 _mm_loadu_si128(reinterpret_cast<__m128i const *>(chars)));
2396 FASTFLOAT_SIMD_RESTORE_WARNINGS
2397}
2398
2399#elif defined(FASTFLOAT_NEON)
2400
2401fastfloat_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
2408fastfloat_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
2419template <typename UC>
2420#else
2421template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
2422#endif
2423// dummy for compile
2424uint64_t simd_read8_to_u64(UC const *) {
2425 return 0;
2426}
2427
2428// credit @aqrit
2429fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
2430parse_eight_digits_unrolled(uint64_t val) {
2431 uint64_t const mask = 0x000000FF000000FF;
2432 uint64_t const mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
2433 uint64_t const mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
2434 val -= 0x3030303030303030;
2435 val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
2436 val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
2437 return uint32_t(val);
2438}
2439
2440// Call this if chars are definitely 8 digits.
2441template <typename UC>
2442fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t
2443parse_eight_digits_unrolled(UC const *chars) noexcept {
2444 if (cpp20_and_in_constexpr() || !has_simd_opt<UC>()) {
2445 return parse_eight_digits_unrolled(read8_to_u64(chars)); // truncation okay
2446 }
2447 return parse_eight_digits_unrolled(simd_read8_to_u64(chars));
2448}
2449
2450// credit @aqrit
2451fastfloat_really_inline constexpr bool
2452is_made_of_eight_digits_fast(uint64_t val) noexcept {
2453 return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
2454 0x8080808080808080));
2455}
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.
2462fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
2463simd_parse_if_eight_digits_unrolled(char16_t const *chars,
2464 uint64_t &i) noexcept {
2465 if (cpp20_and_in_constexpr()) {
2466 return false;
2467 }
2468#ifdef FASTFLOAT_SSE2
2469 FASTFLOAT_SIMD_DISABLE_WARNINGS
2470 __m128i const data =
2471 _mm_loadu_si128(reinterpret_cast<__m128i const *>(chars));
2472
2473 // (x - '0') <= 9
2474 // http://0x80.pl/articles/simd-parsing-int-sequences.html
2475 __m128i const t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
2476 __m128i const t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
2477
2478 if (_mm_movemask_epi8(t1) == 0) {
2479 i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
2480 return true;
2481 } else
2482 return false;
2483 FASTFLOAT_SIMD_RESTORE_WARNINGS
2484#elif defined(FASTFLOAT_NEON)
2485 FASTFLOAT_SIMD_DISABLE_WARNINGS
2486 uint16x8_t const data = vld1q_u16(reinterpret_cast<uint16_t const *>(chars));
2487
2488 // (x - '0') <= 9
2489 // http://0x80.pl/articles/simd-parsing-int-sequences.html
2490 uint16x8_t const t0 = vsubq_u16(data, vmovq_n_u16('0'));
2491 uint16x8_t const mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1));
2492
2493 if (vminvq_u16(mask) == 0xFFFF) {
2494 i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
2495 return true;
2496 } else
2497 return false;
2498 FASTFLOAT_SIMD_RESTORE_WARNINGS
2499#else
2500 (void)chars;
2501 (void)i;
2502 return false;
2503#endif // FASTFLOAT_SSE2
2504}
2505
2506#endif // FASTFLOAT_HAS_SIMD
2507
2508// MSVC SFINAE is broken pre-VS2017
2509#if defined(_MSC_VER) && _MSC_VER <= 1900
2510template <typename UC>
2511#else
2512template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
2513#endif
2514// dummy for compile
2515bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) {
2516 return 0;
2517}
2518
2519template <typename UC, FASTFLOAT_ENABLE_IF(!tinyobj_ff::is_same<UC, char>::value) = 0>
2520fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
2521loop_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
2532fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
2533loop_parse_if_eight_digits(char const *&p, char const *const pend,
2534 uint64_t &i) {
2535 // optimizes better than parse_if_eight_digits_unrolled() for UC = char.
2536 while ((tinyobj_ff::distance(p, pend) >= 8) &&
2537 is_made_of_eight_digits_fast(read8_to_u64(p))) {
2538 i = i * 100000000 +
2539 parse_eight_digits_unrolled(read8_to_u64(
2540 p)); // in rare cases, this will overflow, but that's ok
2541 p += 8;
2542 }
2543}
2544
2545enum 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
2564template <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
2577using byte_span = span<char const>;
2578using parsed_number_string = parsed_number_string_t<char>;
2579
2580template <typename UC>
2581fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
2582report_parse_error(UC const *p, parse_error error) {
2583 parsed_number_string_t<UC> answer;
2584 answer.valid = false;
2585 answer.lastmatch = p;
2586 answer.error = error;
2587 return answer;
2588}
2589
2590// Assuming that you use no more than 19 digits, this will
2591// parse an ASCII string.
2592template <bool basic_json_fmt, typename UC>
2593fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
2594parse_number_string(UC const *p, UC const *pend,
2595 parse_options_t<UC> options) noexcept {
2596 chars_format const fmt = detail::adjust_for_feature_macros(options.format);
2597 UC const decimal_point = options.decimal_point;
2598
2599 parsed_number_string_t<UC> answer;
2600 answer.valid = false;
2601 answer.too_many_digits = false;
2602 // assume p < pend, so dereference without checks;
2603 answer.negative = (*p == UC('-'));
2604 // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
2605 if ((*p == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) &&
2606 !basic_json_fmt && *p == UC('+'))) {
2607 ++p;
2608 if (p == pend) {
2609 return report_parse_error<UC>(
2610 p, parse_error::missing_integer_or_dot_after_sign);
2611 }
2612 FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
2613 if (!is_integer(*p)) { // a sign must be followed by an integer
2614 return report_parse_error<UC>(p,
2615 parse_error::missing_integer_after_sign);
2616 }
2617 }
2618 else {
2619 if (!is_integer(*p) &&
2620 (*p !=
2621 decimal_point)) { // a sign must be followed by an integer or the dot
2622 return report_parse_error<UC>(
2623 p, parse_error::missing_integer_or_dot_after_sign);
2624 }
2625 }
2626 }
2627 UC const *const start_digits = p;
2628
2629 uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
2630
2631 while ((p != pend) && is_integer(*p)) {
2632 // a multiplication by 10 is cheaper than an arbitrary integer
2633 // multiplication
2634 i = 10 * i +
2635 uint64_t(*p -
2636 UC('0')); // might overflow, we will handle the overflow later
2637 ++p;
2638 }
2639 UC const *const end_of_integer_part = p;
2640 int64_t digit_count = int64_t(end_of_integer_part - start_digits);
2641 answer.integer = span<UC const>(start_digits, size_t(digit_count));
2642 FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
2643 // at least 1 digit in integer part, without leading zeros
2644 if (digit_count == 0) {
2645 return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part);
2646 }
2647 if ((start_digits[0] == UC('0') && digit_count > 1)) {
2648 return report_parse_error<UC>(start_digits,
2649 parse_error::leading_zeros_in_integer_part);
2650 }
2651 }
2652
2653 int64_t exponent = 0;
2654 bool const has_decimal_point = (p != pend) && (*p == decimal_point);
2655 if (has_decimal_point) {
2656 ++p;
2657 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 loop_parse_if_eight_digits(p, pend, i);
2661
2662 while ((p != pend) && is_integer(*p)) {
2663 uint8_t digit = uint8_t(*p - UC('0'));
2664 ++p;
2665 i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
2666 }
2667 exponent = before - p;
2668 answer.fraction = span<UC const>(before, size_t(p - before));
2669 digit_count -= exponent;
2670 }
2671 FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
2672 // at least 1 digit in fractional part
2673 if (has_decimal_point && exponent == 0) {
2674 return report_parse_error<UC>(p,
2675 parse_error::no_digits_in_fractional_part);
2676 }
2677 }
2678 else if (digit_count == 0) { // we must have encountered at least one integer!
2679 return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
2680 }
2681 int64_t exp_number = 0; // explicit exponential part
2682 if ((uint64_t(fmt & chars_format::scientific) && (p != pend) &&
2683 ((UC('e') == *p) || (UC('E') == *p))) ||
2684 (uint64_t(fmt & detail::basic_fortran_fmt) && (p != pend) &&
2685 ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) ||
2686 (UC('D') == *p)))) {
2687 UC const *location_of_e = p;
2688 if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) ||
2689 (UC('D') == *p)) {
2690 ++p;
2691 }
2692 bool neg_exp = false;
2693 if ((p != pend) && (UC('-') == *p)) {
2694 neg_exp = true;
2695 ++p;
2696 } else if ((p != pend) &&
2697 (UC('+') ==
2698 *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
2699 ++p;
2700 }
2701 if ((p == pend) || !is_integer(*p)) {
2702 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 return report_parse_error<UC>(p, parse_error::missing_exponential_part);
2707 }
2708 // Otherwise, we will be ignoring the 'e'.
2709 p = location_of_e;
2710 } else {
2711 while ((p != pend) && is_integer(*p)) {
2712 uint8_t digit = uint8_t(*p - UC('0'));
2713 if (exp_number < 0x10000000) {
2714 exp_number = 10 * exp_number + digit;
2715 }
2716 ++p;
2717 }
2718 if (neg_exp) {
2719 exp_number = -exp_number;
2720 }
2721 exponent += exp_number;
2722 }
2723 } else {
2724 // If it scientific and not fixed, we have to bail out.
2725 if (uint64_t(fmt & chars_format::scientific) &&
2726 !uint64_t(fmt & chars_format::fixed)) {
2727 return report_parse_error<UC>(p, parse_error::missing_exponential_part);
2728 }
2729 }
2730 answer.lastmatch = p;
2731 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 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 UC const *start = start_digits;
2744 while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
2745 if (*start == UC('0')) {
2746 digit_count--;
2747 }
2748 start++;
2749 }
2750
2751 if (digit_count > 19) {
2752 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 i = 0;
2757 p = answer.integer.ptr;
2758 UC const *int_end = p + answer.integer.len();
2759 uint64_t const minimal_nineteen_digit_integer{1000000000000000000};
2760 while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
2761 i = i * 10 + uint64_t(*p - UC('0'));
2762 ++p;
2763 }
2764 if (i >= minimal_nineteen_digit_integer) { // We have a big integers
2765 exponent = end_of_integer_part - p + exp_number;
2766 } else { // We have a value with a fractional component.
2767 p = answer.fraction.ptr;
2768 UC const *frac_end = p + answer.fraction.len();
2769 while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
2770 i = i * 10 + uint64_t(*p - UC('0'));
2771 ++p;
2772 }
2773 exponent = answer.fraction.ptr - p + exp_number;
2774 }
2775 // We have now corrected both exponent and i, to a truncated value
2776 }
2777 }
2778 answer.exponent = exponent;
2779 answer.mantissa = i;
2780 return answer;
2781}
2782
2783template <typename T, typename UC>
2784fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
2785parse_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
2903namespace fast_float {
2904
2915
2929template <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
3595template <class unused>
3596constexpr uint64_t
3597 powers_template<unused>::power_of_five_128[number_of_entries];
3598
3599#endif
3600
3601using 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
3614namespace 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//
3621template <int bit_precision>
3622fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
3623compute_product_approximation(int64_t q, uint64_t w) {
3624 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 value128 firstproduct =
3629 full_multiplication(w, powers::power_of_five_128[index]);
3630 static_assert((bit_precision >= 0) && (bit_precision <= 64),
3631 " precision should be in (0,64]");
3632 constexpr uint64_t precision_mask =
3633 (bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
3634 : uint64_t(0xFFFFFFFFFFFFFFFF);
3635 if ((firstproduct.high & precision_mask) ==
3636 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 value128 secondproduct =
3641 full_multiplication(w, powers::power_of_five_128[index + 1]);
3642 firstproduct.low += secondproduct.high;
3643 if (secondproduct.high > firstproduct.low) {
3644 firstproduct.high++;
3645 }
3646 }
3647 return firstproduct;
3648}
3649
3650namespace detail {
3666constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
3667 return (((152170 + 65536) * q) >> 16) + 63;
3668}
3669} // namespace detail
3670
3671// create an adjusted mantissa, biased by the invalid power2
3672// for significant digits already multiplied by 10 ** q.
3673template <typename binary>
3674fastfloat_really_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa
3675compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
3676 int hilz = int(w >> 63) ^ 1;
3677 adjusted_mantissa answer;
3678 answer.mantissa = w << hilz;
3679 int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent();
3680 answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 +
3681 invalid_am_bias);
3682 return answer;
3683}
3684
3685// w * 10 ** q, without rounding the representation up.
3686// the power2 in the exponent will be adjusted by invalid_am_bias.
3687template <typename binary>
3688fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
3689compute_error(int64_t q, uint64_t w) noexcept {
3690 int lz = leading_zeroes(w);
3691 w <<= lz;
3692 value128 product =
3693 compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
3694 return compute_error_scaled<binary>(q, product.high, lz);
3695}
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.
3702template <typename binary>
3703fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
3704compute_float(int64_t q, uint64_t w) noexcept {
3705 adjusted_mantissa answer;
3706 if ((w == 0) || (q < binary::smallest_power_of_ten())) {
3707 answer.power2 = 0;
3708 answer.mantissa = 0;
3709 // result should be zero
3710 return answer;
3711 }
3712 if (q > binary::largest_power_of_ten()) {
3713 // we want to get infinity:
3714 answer.power2 = binary::infinite_power();
3715 answer.mantissa = 0;
3716 return answer;
3717 }
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 int lz = leading_zeroes(w);
3723 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 value128 product =
3732 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 int upperbit = int(product.high >> 63);
3743 int shift = upperbit + 64 - binary::mantissa_explicit_bits() - 3;
3744
3745 answer.mantissa = product.high >> shift;
3746
3747 answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz -
3748 binary::minimum_exponent());
3749 if (answer.power2 <= 0) { // we have a subnormal?
3750 // Here have that answer.power2 <= 0 so -answer.power2 >= 0
3751 if (-answer.power2 + 1 >=
3752 64) { // if we have more than 64 bits below the minimum exponent, you
3753 // have a zero for sure.
3754 answer.power2 = 0;
3755 answer.mantissa = 0;
3756 // result should be zero
3757 return answer;
3758 }
3759 // next line is safe because -answer.power2 + 1 < 64
3760 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 answer.mantissa += (answer.mantissa & 1); // round up
3765 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 answer.power2 =
3774 (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits()))
3775 ? 0
3776 : 1;
3777 return answer;
3778 }
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 if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) &&
3784 (q <= binary::max_exponent_round_to_even()) &&
3785 ((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 if ((answer.mantissa << shift) == product.high) {
3792 answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up
3793 }
3794 }
3795
3796 answer.mantissa += (answer.mantissa & 1); // round up
3797 answer.mantissa >>= 1;
3798 if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) {
3799 answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits());
3800 answer.power2++; // undo previous addition
3801 }
3802
3803 answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits());
3804 if (answer.power2 >= binary::infinite_power()) { // infinity
3805 answer.power2 = binary::infinite_power();
3806 answer.mantissa = 0;
3807 }
3808 return answer;
3809}
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
3821namespace 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
3831typedef uint64_t limb;
3832constexpr size_t limb_bits = 64;
3833#else
3834#define FASTFLOAT_32BIT_LIMB
3835typedef uint32_t limb;
3836constexpr size_t limb_bits = 32;
3837#endif
3838
3839typedef 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.
3845constexpr size_t bigint_bits = 4000;
3846constexpr 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.
3850template <uint16_t size> struct stackvec {
3851 limb data[size];
3852 // we never need more than 150 limbs
3853 uint16_t length{0};
3854
3855 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 FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) {
3863 FASTFLOAT_ASSERT(try_extend(s));
3864 }
3865
3866 FASTFLOAT_CONSTEXPR14 limb &operator[](size_t index) noexcept {
3867 FASTFLOAT_DEBUG_ASSERT(index < length);
3868 return data[index];
3869 }
3870
3871 FASTFLOAT_CONSTEXPR14 const limb &operator[](size_t index) const noexcept {
3872 FASTFLOAT_DEBUG_ASSERT(index < length);
3873 return data[index];
3874 }
3875
3876 // index from the end of the container
3877 FASTFLOAT_CONSTEXPR14 const limb &rindex(size_t index) const noexcept {
3878 FASTFLOAT_DEBUG_ASSERT(index < length);
3879 size_t rindex = length - index - 1;
3880 return data[rindex];
3881 }
3882
3883 // set the length, without bounds checking.
3884 FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
3885 length = uint16_t(len);
3886 }
3887
3888 constexpr size_t len() const noexcept { return length; }
3889
3890 constexpr bool is_empty() const noexcept { return length == 0; }
3891
3892 constexpr size_t capacity() const noexcept { return size; }
3893
3894 // append item to vector, without bounds checking
3895 FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
3896 data[length] = value;
3897 length++;
3898 }
3899
3900 // append item to vector, returning if item was added
3901 FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept {
3902 if (len() < capacity()) {
3903 push_unchecked(value);
3904 return true;
3905 } else {
3906 return false;
3907 }
3908 }
3909
3910 // add items to the vector, from a span, without bounds checking
3911 FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
3912 limb *ptr = data + length;
3913 tinyobj_ff::copy_n(s.ptr, s.len(), ptr);
3914 set_len(len() + s.len());
3915 }
3916
3917 // try to add items to the vector, returning if items were added
3918 FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept {
3919 if (len() + s.len() <= capacity()) {
3920 extend_unchecked(s);
3921 return true;
3922 } else {
3923 return false;
3924 }
3925 }
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 void resize_unchecked(size_t new_len, limb value) noexcept {
3932 if (new_len > len()) {
3933 size_t count = new_len - len();
3934 limb *first = data + len();
3935 limb *last = first + count;
3936 tinyobj_ff::fill(first, last, value);
3937 set_len(new_len);
3938 } else {
3939 set_len(new_len);
3940 }
3941 }
3942
3943 // try to resize the vector, returning if the vector was resized.
3944 FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept {
3945 if (new_len > capacity()) {
3946 return false;
3947 } else {
3948 resize_unchecked(new_len, value);
3949 return true;
3950 }
3951 }
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 FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept {
3957 while (index < len()) {
3958 if (rindex(index) != 0) {
3959 return true;
3960 }
3961 index++;
3962 }
3963 return false;
3964 }
3965
3966 // normalize the big integer, so most-significant zero limbs are removed.
3967 FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
3968 while (len() > 0 && rindex(0) == 0) {
3969 length--;
3970 }
3971 }
3972};
3973
3974fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
3975empty_hi64(bool &truncated) noexcept {
3976 truncated = false;
3977 return 0;
3978}
3979
3980fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
3981uint64_hi64(uint64_t r0, bool &truncated) noexcept {
3982 truncated = false;
3983 int shl = leading_zeroes(r0);
3984 return r0 << shl;
3985}
3986
3987fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
3988uint64_hi64(uint64_t r0, uint64_t r1, bool &truncated) noexcept {
3989 int shl = leading_zeroes(r0);
3990 if (shl == 0) {
3991 truncated = r1 != 0;
3992 return r0;
3993 } else {
3994 int shr = 64 - shl;
3995 truncated = (r1 << shl) != 0;
3996 return (r0 << shl) | (r1 >> shr);
3997 }
3998}
3999
4000fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
4001uint32_hi64(uint32_t r0, bool &truncated) noexcept {
4002 return uint64_hi64(r0, truncated);
4003}
4004
4005fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
4006uint32_hi64(uint32_t r0, uint32_t r1, bool &truncated) noexcept {
4007 uint64_t x0 = r0;
4008 uint64_t x1 = r1;
4009 return uint64_hi64((x0 << 32) | x1, truncated);
4010}
4011
4012fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
4013uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool &truncated) noexcept {
4014 uint64_t x0 = r0;
4015 uint64_t x1 = r1;
4016 uint64_t x2 = r2;
4017 return uint64_hi64(x0, (x1 << 32) | x2, truncated);
4018}
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.
4024fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb
4025scalar_add(limb x, limb y, bool &overflow) noexcept {
4026 limb z;
4027// gcc and clang
4028#if defined(__has_builtin)
4029#if __has_builtin(__builtin_add_overflow)
4030 if (!cpp20_and_in_constexpr()) {
4031 overflow = __builtin_add_overflow(x, y, &z);
4032 return z;
4033 }
4034#endif
4035#endif
4036
4037 // generic, this still optimizes correctly on MSVC.
4038 z = x + y;
4039 overflow = z < x;
4040 return z;
4041}
4042
4043// multiply two small integers, getting both the high and low bits.
4044fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb
4045scalar_mul(limb x, limb y, limb &carry) noexcept {
4046#ifdef FASTFLOAT_64BIT_LIMB
4047#if defined(__SIZEOF_INT128__)
4048 // GCC and clang both define it as an extension.
4049 __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry);
4050 carry = limb(z >> limb_bits);
4051 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}
4068
4069// add scalar value to bigint starting from offset.
4070// used in grade school multiplication
4071template <uint16_t size>
4072inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
4073 size_t start) noexcept {
4074 size_t index = start;
4075 limb carry = y;
4076 bool overflow;
4077 while (carry != 0 && index < vec.len()) {
4078 vec[index] = scalar_add(vec[index], carry, overflow);
4079 carry = limb(overflow);
4080 index += 1;
4081 }
4082 if (carry != 0) {
4083 FASTFLOAT_TRY(vec.try_push(carry));
4084 }
4085 return true;
4086}
4087
4088// add scalar value to bigint.
4089template <uint16_t size>
4090fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
4091small_add(stackvec<size> &vec, limb y) noexcept {
4092 return small_add_from(vec, y, 0);
4093}
4094
4095// multiply bigint by scalar value.
4096template <uint16_t size>
4097inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
4098 limb y) noexcept {
4099 limb carry = 0;
4100 for (size_t index = 0; index < vec.len(); index++) {
4101 vec[index] = scalar_mul(vec[index], y, carry);
4102 }
4103 if (carry != 0) {
4104 FASTFLOAT_TRY(vec.try_push(carry));
4105 }
4106 return true;
4107}
4108
4109// add bigint to bigint starting from index.
4110// used in grade school multiplication
4111template <uint16_t size>
4112FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
4113 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 if (x.len() < start || y.len() > x.len() - start) {
4117 FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
4118 }
4119
4120 bool carry = false;
4121 for (size_t index = 0; index < y.len(); index++) {
4122 limb xi = x[index + start];
4123 limb yi = y[index];
4124 bool c1 = false;
4125 bool c2 = false;
4126 xi = scalar_add(xi, yi, c1);
4127 if (carry) {
4128 xi = scalar_add(xi, 1, c2);
4129 }
4130 x[index + start] = xi;
4131 carry = c1 | c2;
4132 }
4133
4134 // handle overflow
4135 if (carry) {
4136 FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start));
4137 }
4138 return true;
4139}
4140
4141// add bigint to bigint.
4142template <uint16_t size>
4143fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
4144large_add_from(stackvec<size> &x, limb_span y) noexcept {
4145 return large_add_from(x, y, 0);
4146}
4147
4148// grade-school multiplication algorithm
4149template <uint16_t size>
4150FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
4151 limb_span xs = limb_span(x.data, x.len());
4152 stackvec<size> z(xs);
4153 limb_span zs = limb_span(z.data, z.len());
4154
4155 if (y.len() != 0) {
4156 limb y0 = y[0];
4157 FASTFLOAT_TRY(small_mul(x, y0));
4158 for (size_t index = 1; index < y.len(); index++) {
4159 limb yi = y[index];
4160 stackvec<size> zi;
4161 if (yi != 0) {
4162 // re-use the same buffer throughout
4163 zi.set_len(0);
4164 FASTFLOAT_TRY(zi.try_extend(zs));
4165 FASTFLOAT_TRY(small_mul(zi, yi));
4166 limb_span zis = limb_span(zi.data, zi.len());
4167 FASTFLOAT_TRY(large_add_from(x, zis, index));
4168 }
4169 }
4170 }
4171
4172 x.normalize();
4173 return true;
4174}
4175
4176// grade-school multiplication algorithm
4177template <uint16_t size>
4178FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept {
4179 if (y.len() == 1) {
4180 FASTFLOAT_TRY(small_mul(x, y[0]));
4181 } else {
4182 FASTFLOAT_TRY(long_mul(x, y));
4183 }
4184 return true;
4185}
4186
4187template <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
4232template <typename T> constexpr uint32_t pow5_tables<T>::large_step;
4233
4234template <typename T> constexpr uint64_t pow5_tables<T>::small_power_of_5[];
4235
4236template <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.
4244struct bigint : pow5_tables<> {
4245 // storage of the limbs, in little-endian order.
4246 stackvec<bigint_limbs> vec;
4247
4248 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 FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) : vec() {
4256#ifdef FASTFLOAT_64BIT_LIMB
4257 vec.push_unchecked(value);
4258#else
4259 vec.push_unchecked(uint32_t(value));
4260 vec.push_unchecked(uint32_t(value >> 32));
4261#endif
4262 vec.normalize();
4263 }
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 FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool &truncated) const noexcept {
4268#ifdef FASTFLOAT_64BIT_LIMB
4269 if (vec.len() == 0) {
4270 return empty_hi64(truncated);
4271 } else if (vec.len() == 1) {
4272 return uint64_hi64(vec.rindex(0), truncated);
4273 } else {
4274 uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated);
4275 truncated |= vec.nonzero(2);
4276 return result;
4277 }
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 }
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 FASTFLOAT_CONSTEXPR20 int compare(bigint const &other) const noexcept {
4301 if (vec.len() > other.vec.len()) {
4302 return 1;
4303 } else if (vec.len() < other.vec.len()) {
4304 return -1;
4305 } else {
4306 for (size_t index = vec.len(); index > 0; index--) {
4307 limb xi = vec[index - 1];
4308 limb yi = other.vec[index - 1];
4309 if (xi > yi) {
4310 return 1;
4311 } else if (xi < yi) {
4312 return -1;
4313 }
4314 }
4315 return 0;
4316 }
4317 }
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 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 FASTFLOAT_DEBUG_ASSERT(n != 0);
4328 FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
4329
4330 size_t shl = n;
4331 size_t shr = limb_bits - shl;
4332 limb prev = 0;
4333 for (size_t index = 0; index < vec.len(); index++) {
4334 limb xi = vec[index];
4335 vec[index] = (xi << shl) | (prev >> shr);
4336 prev = xi;
4337 }
4338
4339 limb carry = prev >> shr;
4340 if (carry != 0) {
4341 return vec.try_push(carry);
4342 }
4343 return true;
4344 }
4345
4346 // move the limbs left by `n` limbs.
4347 FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept {
4348 FASTFLOAT_DEBUG_ASSERT(n != 0);
4349 if (n + vec.len() > vec.capacity()) {
4350 return false;
4351 } else if (!vec.is_empty()) {
4352 // move limbs
4353 limb *dst = vec.data + n;
4354 limb const *src = vec.data;
4355 tinyobj_ff::copy_backward(src, src + vec.len(), dst + vec.len());
4356 // fill in empty limbs
4357 limb *first = vec.data;
4358 limb *last = first + n;
4359 tinyobj_ff::fill(first, last, 0);
4360 vec.set_len(n + vec.len());
4361 return true;
4362 } else {
4363 return true;
4364 }
4365 }
4366
4367 // move the limbs left by `n` bits.
4368 FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept {
4369 size_t rem = n % limb_bits;
4370 size_t div = n / limb_bits;
4371 if (rem != 0) {
4372 FASTFLOAT_TRY(shl_bits(rem));
4373 }
4374 if (div != 0) {
4375 FASTFLOAT_TRY(shl_limbs(div));
4376 }
4377 return true;
4378 }
4379
4380 // get the number of leading zeros in the bigint.
4381 FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept {
4382 if (vec.is_empty()) {
4383 return 0;
4384 } else {
4385#ifdef FASTFLOAT_64BIT_LIMB
4386 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 }
4393 }
4394
4395 // get the number of bits in the bigint.
4396 FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept {
4397 int lz = ctlz();
4398 return int(limb_bits * vec.len()) - lz;
4399 }
4400
4401 FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { return small_mul(vec, y); }
4402
4403 FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); }
4404
4405 // multiply as if by 2 raised to a power.
4406 FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept { return shl(exp); }
4407
4408 // multiply as if by 5 raised to a power.
4409 FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept {
4410 // multiply by a power of 5
4411 size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
4412 limb_span large = limb_span(large_power_of_5, large_length);
4413 while (exp >= large_step) {
4414 FASTFLOAT_TRY(large_mul(vec, large));
4415 exp -= large_step;
4416 }
4417#ifdef FASTFLOAT_64BIT_LIMB
4418 uint32_t small_step = 27;
4419 limb max_native = 7450580596923828125UL;
4420#else
4421 uint32_t small_step = 13;
4422 limb max_native = 1220703125U;
4423#endif
4424 while (exp >= small_step) {
4425 FASTFLOAT_TRY(small_mul(vec, max_native));
4426 exp -= small_step;
4427 }
4428 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 FASTFLOAT_TRY(small_mul(
4433 vec, limb(((void)small_power_of_5[0], small_power_of_5[exp]))));
4434 }
4435
4436 return true;
4437 }
4438
4439 // multiply as if by 10 raised to a power.
4440 FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept {
4441 FASTFLOAT_TRY(pow5(exp));
4442 return pow2(exp);
4443 }
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
4456namespace fast_float {
4457
4458// 1e0 to 1e19
4459constexpr 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.
4484template <typename UC>
4485fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t
4486scientific_exponent(parsed_number_string_t<UC> &num) noexcept {
4487 uint64_t mantissa = num.mantissa;
4488 int32_t exponent = int32_t(num.exponent);
4489 while (mantissa >= 10000) {
4490 mantissa /= 10000;
4491 exponent += 4;
4492 }
4493 while (mantissa >= 100) {
4494 mantissa /= 100;
4495 exponent += 2;
4496 }
4497 while (mantissa >= 10) {
4498 mantissa /= 10;
4499 exponent += 1;
4500 }
4501 return exponent;
4502}
4503
4504// this converts a native floating-point number to an extended-precision float.
4505template <typename T>
4506fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
4507to_extended(T value) noexcept {
4508 using equiv_uint = equiv_uint_t<T>;
4509 constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
4510 constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
4511 constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
4512
4513 adjusted_mantissa am;
4514 int32_t bias = binary_format<T>::mantissa_explicit_bits() -
4515 binary_format<T>::minimum_exponent();
4516 equiv_uint bits;
4517#if FASTFLOAT_HAS_BIT_CAST
4518 bits = std::bit_cast<equiv_uint>(value);
4519#else
4520 ::memcpy(&bits, &value, sizeof(T));
4521#endif
4522 if ((bits & exponent_mask) == 0) {
4523 // denormal
4524 am.power2 = 1 - bias;
4525 am.mantissa = bits & mantissa_mask;
4526 } else {
4527 // normal
4528 am.power2 = int32_t((bits & exponent_mask) >>
4529 binary_format<T>::mantissa_explicit_bits());
4530 am.power2 -= bias;
4531 am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
4532 }
4533
4534 return am;
4535}
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.
4540template <typename T>
4541fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
4542to_extended_halfway(T value) noexcept {
4543 adjusted_mantissa am = to_extended(value);
4544 am.mantissa <<= 1;
4545 am.mantissa += 1;
4546 am.power2 -= 1;
4547 return am;
4548}
4549
4550// round an extended-precision float to the nearest machine float.
4551template <typename T, typename callback>
4552fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
4553 callback cb) noexcept {
4554 int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
4555 if (-am.power2 >= mantissa_shift) {
4556 // have a denormal float
4557 int32_t shift = -am.power2 + 1;
4558 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 am.power2 = (am.mantissa <
4561 (uint64_t(1) << binary_format<T>::mantissa_explicit_bits()))
4562 ? 0
4563 : 1;
4564 return;
4565 }
4566
4567 // have a normal float, use the default shift.
4568 cb(am, mantissa_shift);
4569
4570 // check for carry
4571 if (am.mantissa >=
4572 (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
4573 am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
4574 am.power2++;
4575 }
4576
4577 // check for infinite: we could have carried to an infinite power
4578 am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
4579 if (am.power2 >= binary_format<T>::infinite_power()) {
4580 am.power2 = binary_format<T>::infinite_power();
4581 am.mantissa = 0;
4582 }
4583}
4584
4585template <typename callback>
4586fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
4587round_nearest_tie_even(adjusted_mantissa &am, int32_t shift,
4588 callback cb) noexcept {
4589 uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1;
4590 uint64_t const halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1);
4591 uint64_t truncated_bits = am.mantissa & mask;
4592 bool is_above = truncated_bits > halfway;
4593 bool is_halfway = truncated_bits == halfway;
4594
4595 // shift digits into position
4596 if (shift == 64) {
4597 am.mantissa = 0;
4598 } else {
4599 am.mantissa >>= shift;
4600 }
4601 am.power2 += shift;
4602
4603 bool is_odd = (am.mantissa & 1) == 1;
4604 am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
4605}
4606
4607fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
4608round_down(adjusted_mantissa &am, int32_t shift) noexcept {
4609 if (shift == 64) {
4610 am.mantissa = 0;
4611 } else {
4612 am.mantissa >>= shift;
4613 }
4614 am.power2 += shift;
4615}
4616
4617template <typename UC>
4618fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
4619skip_zeros(UC const *&first, UC const *last) noexcept {
4620 uint64_t val;
4621 while (!cpp20_and_in_constexpr() &&
4622 tinyobj_ff::distance(first, last) >= int_cmp_len<UC>()) {
4623 ::memcpy(&val, first, sizeof(uint64_t));
4624 if (val != int_cmp_zeros<UC>()) {
4625 break;
4626 }
4627 first += int_cmp_len<UC>();
4628 }
4629 while (first != last) {
4630 if (*first != UC('0')) {
4631 break;
4632 }
4633 first++;
4634 }
4635}
4636
4637// determine if any non-zero digits were truncated.
4638// all characters must be valid digits.
4639template <typename UC>
4640fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
4641is_truncated(UC const *first, UC const *last) noexcept {
4642 // do 8-bit optimizations, can just compare to 8 literal 0s.
4643 uint64_t val;
4644 while (!cpp20_and_in_constexpr() &&
4645 tinyobj_ff::distance(first, last) >= int_cmp_len<UC>()) {
4646 ::memcpy(&val, first, sizeof(uint64_t));
4647 if (val != int_cmp_zeros<UC>()) {
4648 return true;
4649 }
4650 first += int_cmp_len<UC>();
4651 }
4652 while (first != last) {
4653 if (*first != UC('0')) {
4654 return true;
4655 }
4656 ++first;
4657 }
4658 return false;
4659}
4660
4661template <typename UC>
4662fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
4663is_truncated(span<UC const> s) noexcept {
4664 return is_truncated(s.ptr, s.ptr + s.len());
4665}
4666
4667template <typename UC>
4668fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
4669parse_eight_digits(UC const *&p, limb &value, size_t &counter,
4670 size_t &count) noexcept {
4671 value = value * 100000000 + parse_eight_digits_unrolled(p);
4672 p += 8;
4673 counter += 8;
4674 count += 8;
4675}
4676
4677template <typename UC>
4678fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
4679parse_one_digit(UC const *&p, limb &value, size_t &counter,
4680 size_t &count) noexcept {
4681 value = value * 10 + limb(*p - UC('0'));
4682 p++;
4683 counter++;
4684 count++;
4685}
4686
4687fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
4688add_native(bigint &big, limb power, limb value) noexcept {
4689 big.mul(power);
4690 big.add(value);
4691}
4692
4693fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
4694round_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 add_native(big, 10, 1);
4698 count++;
4699}
4700
4701// parse the significant digits into a big integer
4702template <typename UC>
4703inline FASTFLOAT_CONSTEXPR20 void
4704parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
4705 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 size_t counter = 0;
4710 digits = 0;
4711 limb value = 0;
4712#ifdef FASTFLOAT_64BIT_LIMB
4713 size_t step = 19;
4714#else
4715 size_t step = 9;
4716#endif
4717
4718 // process all integer digits.
4719 UC const *p = num.integer.ptr;
4720 UC const *pend = p + num.integer.len();
4721 skip_zeros(p, pend);
4722 // process all digits, in increments of step per loop
4723 while (p != pend) {
4724 while ((tinyobj_ff::distance(p, pend) >= 8) && (step - counter >= 8) &&
4725 (max_digits - digits >= 8)) {
4726 parse_eight_digits(p, value, counter, digits);
4727 }
4728 while (counter < step && p != pend && digits < max_digits) {
4729 parse_one_digit(p, value, counter, digits);
4730 }
4731 if (digits == max_digits) {
4732 // add the temporary value, then check if we've truncated any digits
4733 add_native(result, limb(powers_of_ten_uint64[counter]), value);
4734 bool truncated = is_truncated(p, pend);
4735 if (num.fraction.ptr != nullptr) {
4736 truncated |= is_truncated(num.fraction);
4737 }
4738 if (truncated) {
4739 round_up_bigint(result, digits);
4740 }
4741 return;
4742 } else {
4743 add_native(result, limb(powers_of_ten_uint64[counter]), value);
4744 counter = 0;
4745 value = 0;
4746 }
4747 }
4748
4749 // add our fraction digits, if they're available.
4750 if (num.fraction.ptr != nullptr) {
4751 p = num.fraction.ptr;
4752 pend = p + num.fraction.len();
4753 if (digits == 0) {
4754 skip_zeros(p, pend);
4755 }
4756 // process all digits, in increments of step per loop
4757 while (p != pend) {
4758 while ((tinyobj_ff::distance(p, pend) >= 8) && (step - counter >= 8) &&
4759 (max_digits - digits >= 8)) {
4760 parse_eight_digits(p, value, counter, digits);
4761 }
4762 while (counter < step && p != pend && digits < max_digits) {
4763 parse_one_digit(p, value, counter, digits);
4764 }
4765 if (digits == max_digits) {
4766 // add the temporary value, then check if we've truncated any digits
4767 add_native(result, limb(powers_of_ten_uint64[counter]), value);
4768 bool truncated = is_truncated(p, pend);
4769 if (truncated) {
4770 round_up_bigint(result, digits);
4771 }
4772 return;
4773 } else {
4774 add_native(result, limb(powers_of_ten_uint64[counter]), value);
4775 counter = 0;
4776 value = 0;
4777 }
4778 }
4779 }
4780
4781 if (counter != 0) {
4782 add_native(result, limb(powers_of_ten_uint64[counter]), value);
4783 }
4784}
4785
4786template <typename T>
4787inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
4788positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
4789 FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
4790 adjusted_mantissa answer;
4791 bool truncated;
4792 answer.mantissa = bigmant.hi64(truncated);
4793 int bias = binary_format<T>::mantissa_explicit_bits() -
4794 binary_format<T>::minimum_exponent();
4795 answer.power2 = bigmant.bit_length() - 64 + bias;
4796
4797 round<T>(answer, [truncated](adjusted_mantissa &a, int32_t shift) {
4798 round_nearest_tie_even(
4799 a, shift,
4800 [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
4801 return is_above || (is_halfway && truncated) ||
4802 (is_odd && is_halfway);
4803 });
4804 });
4805
4806 return answer;
4807}
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.
4814template <typename T>
4815inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
4816 bigint &bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
4817 bigint &real_digits = bigmant;
4818 int32_t real_exp = exponent;
4819
4820 // get the value of `b`, rounded down, and get a bigint representation of b+h
4821 adjusted_mantissa am_b = am;
4822 // gcc7 buf: use a lambda to remove the noexcept qualifier bug with
4823 // -Wnoexcept-type.
4824 round<T>(am_b,
4825 [](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); });
4826 T b;
4827 to_float(false, am_b, b);
4828 adjusted_mantissa theor = to_extended_halfway(b);
4829 bigint theor_digits(theor.mantissa);
4830 int32_t theor_exp = theor.power2;
4831
4832 // scale real digits and theor digits to be same power.
4833 int32_t pow2_exp = theor_exp - real_exp;
4834 uint32_t pow5_exp = uint32_t(-real_exp);
4835 if (pow5_exp != 0) {
4836 FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
4837 }
4838 if (pow2_exp > 0) {
4839 FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp)));
4840 } else if (pow2_exp < 0) {
4841 FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp)));
4842 }
4843
4844 // compare digits, and use it to director rounding
4845 int ord = real_digits.compare(theor_digits);
4846 adjusted_mantissa answer = am;
4847 round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
4848 round_nearest_tie_even(
4849 a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
4850 (void)_; // not needed, since we've done our comparison
4851 (void)__; // not needed, since we've done our comparison
4852 if (ord > 0) {
4853 return true;
4854 } else if (ord < 0) {
4855 return false;
4856 } else {
4857 return is_odd;
4858 }
4859 });
4860 });
4861
4862 return answer;
4863}
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.
4878template <typename T, typename UC>
4879inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
4880digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept {
4881 // remove the invalid exponent bias
4882 am.power2 -= invalid_am_bias;
4883
4884 int32_t sci_exp = scientific_exponent(num);
4885 size_t max_digits = binary_format<T>::max_digits();
4886 size_t digits = 0;
4887 bigint bigmant;
4888 parse_mantissa(bigmant, num, max_digits, digits);
4889 // can't underflow, since digits is at most max_digits.
4890 int32_t exponent = sci_exp + 1 - int32_t(digits);
4891 if (exponent >= 0) {
4892 return positive_digit_comp<T>(bigmant, exponent);
4893 } else {
4894 return negative_digit_comp<T>(bigmant, am, exponent);
4895 }
4896}
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
4910namespace fast_float {
4911
4912namespace detail {
4918template <typename T, typename UC>
4919from_chars_result_t<UC>
4920 FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last,
4921 T &value, chars_format fmt) noexcept {
4922 from_chars_result_t<UC> answer{};
4923 answer.ptr = first;
4924 answer.ec = tinyobj_ff::ff_errc(); // be optimistic
4925 // assume first < last, so dereference without checks;
4926 bool const minusSign = (*first == UC('-'));
4927 // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
4928 if ((*first == UC('-')) ||
4929 (uint64_t(fmt & chars_format::allow_leading_plus) &&
4930 (*first == UC('+')))) {
4931 ++first;
4932 }
4933 if (last - first >= 3) {
4934 if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
4935 answer.ptr = (first += 3);
4936 value = minusSign ? -std::numeric_limits<T>::quiet_NaN()
4937 : 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 if (first != last && *first == UC('(')) {
4941 for (UC const *ptr = first + 1; ptr != last; ++ptr) {
4942 if (*ptr == UC(')')) {
4943 answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
4944 break;
4945 } else if (!((UC('a') <= *ptr && *ptr <= UC('z')) ||
4946 (UC('A') <= *ptr && *ptr <= UC('Z')) ||
4947 (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
4948 break; // forbidden char, not nan(n-char-seq-opt)
4949 }
4950 }
4951 return answer;
4952 }
4953 if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
4954 if ((last - first >= 8) &&
4955 fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
4956 answer.ptr = first + 8;
4957 } else {
4958 answer.ptr = first + 3;
4959 }
4960 value = minusSign ? -std::numeric_limits<T>::infinity()
4961 : std::numeric_limits<T>::infinity();
4962 return answer;
4963 }
4964 }
4965 answer.ec = tinyobj_ff::ff_errc::invalid_argument;
4966 return answer;
4967}
4968
4974fastfloat_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 static float volatile fmin = std::numeric_limits<float>::min();
4996 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#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 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}
5033
5034} // namespace detail
5035
5036template <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 parse_options_t<UC> options) noexcept {
5041 return from_chars_advanced(first, last, value, options);
5042 }
5043};
5044
5045#ifdef __STDCPP_FLOAT32_T__
5046template <> 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__
5063template <> 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
5079template <typename T, typename UC, typename>
5080FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
5081from_chars(UC const *first, UC const *last, T &value,
5082 chars_format fmt /*= chars_format::general*/) noexcept {
5083 return from_chars_caller<T>::call(first, last, value,
5084 parse_options_t<UC>(fmt));
5085}
5086
5092template <typename T, typename UC>
5093FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
5094from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
5095
5096 static_assert(is_supported_float_type<T>::value,
5097 "only some floating-point types are supported");
5098 static_assert(is_supported_char_type<UC>::value,
5099 "only char, wchar_t, char16_t and char32_t are supported");
5100
5101 from_chars_result_t<UC> answer;
5102
5103 answer.ec = tinyobj_ff::ff_errc(); // be optimistic
5104 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 if (binary_format<T>::min_exponent_fast_path() <= pns.exponent &&
5111 pns.exponent <= binary_format<T>::max_exponent_fast_path() &&
5112 !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 if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
5121 // We have that fegetround() == FE_TONEAREST.
5122 // Next is Clinger's fast path.
5123 if (pns.mantissa <= binary_format<T>::max_mantissa_fast_path()) {
5124 value = T(pns.mantissa);
5125 if (pns.exponent < 0) {
5126 value = value / binary_format<T>::exact_power_of_ten(-pns.exponent);
5127 } else {
5128 value = value * binary_format<T>::exact_power_of_ten(pns.exponent);
5129 }
5130 if (pns.negative) {
5131 value = -value;
5132 }
5133 return answer;
5134 }
5135 } 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 if (pns.exponent >= 0 &&
5140 pns.mantissa <=
5141 binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
5142#if defined(__clang__) || defined(FASTFLOAT_32BIT)
5143 // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
5144 if (pns.mantissa == 0) {
5145 value = pns.negative ? T(-0.) : T(0.);
5146 return answer;
5147 }
5148#endif
5149 value = T(pns.mantissa) *
5150 binary_format<T>::exact_power_of_ten(pns.exponent);
5151 if (pns.negative) {
5152 value = -value;
5153 }
5154 return answer;
5155 }
5156 }
5157 }
5158 adjusted_mantissa am =
5159 compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
5160 if (pns.too_many_digits && am.power2 >= 0) {
5161 if (am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
5162 am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
5163 }
5164 }
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 if (am.power2 < 0) {
5169 am = digit_comp<T>(pns, am);
5170 }
5171 to_float(pns.negative, am, value);
5172 // Test for over/underflow.
5173 if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) ||
5174 am.power2 == binary_format<T>::infinite_power()) {
5175 answer.ec = tinyobj_ff::ff_errc::result_out_of_range;
5176 }
5177 return answer;
5178}
5179
5180template <typename T, typename UC>
5181FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
5182from_chars_float_advanced(UC const *first, UC const *last, T &value,
5183 parse_options_t<UC> options) noexcept {
5184
5185 static_assert(is_supported_float_type<T>::value,
5186 "only some floating-point types are supported");
5187 static_assert(is_supported_char_type<UC>::value,
5188 "only char, wchar_t, char16_t and char32_t are supported");
5189
5190 chars_format const fmt = detail::adjust_for_feature_macros(options.format);
5191
5192 from_chars_result_t<UC> answer;
5193 if (uint64_t(fmt & chars_format::skip_white_space)) {
5194 while ((first != last) && fast_float::is_space(*first)) {
5195 first++;
5196 }
5197 }
5198 if (first == last) {
5199 answer.ec = tinyobj_ff::ff_errc::invalid_argument;
5200 answer.ptr = first;
5201 return answer;
5202 }
5203 parsed_number_string_t<UC> pns =
5204 uint64_t(fmt & detail::basic_json_fmt)
5205 ? parse_number_string<true, UC>(first, last, options)
5206 : parse_number_string<false, UC>(first, last, options);
5207 if (!pns.valid) {
5208 if (uint64_t(fmt & chars_format::no_infnan)) {
5209 answer.ec = tinyobj_ff::ff_errc::invalid_argument;
5210 answer.ptr = first;
5211 return answer;
5212 } else {
5213 return detail::parse_infnan(first, last, value, fmt);
5214 }
5215 }
5216
5217 // call overload that takes parsed_number_string_t directly.
5218 return from_chars_advanced(pns, value);
5219}
5220
5221template <typename T, typename UC, typename>
5222FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
5223from_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
5235template <typename T, typename UC>
5236FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
5237from_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
5263template <size_t TypeIx> struct from_chars_advanced_caller {
5264 static_assert(TypeIx > 0, "unsupported type");
5265};
5266
5267template <> 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 parse_options_t<UC> options) noexcept {
5272 return from_chars_float_advanced(first, last, value, options);
5273 }
5274};
5275
5276template <> 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
5285template <typename T, typename UC>
5286FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
5287from_chars_advanced(UC const *first, UC const *last, T &value,
5288 parse_options_t<UC> options) noexcept {
5289 return from_chars_advanced_caller<
5290 size_t(is_supported_float_type<T>::value) +
5291 2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value,
5292 options);
5293}
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
5345namespace tinyobj {
5346
5347MaterialReader::~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.
5352class 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#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 : 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 explicit StreamReader(std::istream &is) : buf_(NULL), length_(0), idx_(0), line_num_(1), col_num_(1) {
5370 const size_t max_stream_bytes = TINYOBJLOADER_STREAM_READER_MAX_BYTES;
5371 std::streampos start_pos = is.tellg();
5372 bool can_seek = (start_pos != std::streampos(-1));
5373 if (can_seek) {
5374 is.seekg(0, std::ios::end);
5375 std::streampos end_pos = is.tellg();
5376 if (end_pos >= start_pos) {
5377 std::streamoff remaining_off = static_cast<std::streamoff>(end_pos - start_pos);
5378 is.seekg(start_pos);
5379 unsigned long long remaining_ull = static_cast<unsigned long long>(remaining_off);
5380 if (remaining_ull > static_cast<unsigned long long>((std::numeric_limits<size_t>::max)())) {
5381 std::stringstream ss;
5382 ss << "input stream too large for this platform (" << remaining_ull
5383 << " bytes exceeds size_t max " << (std::numeric_limits<size_t>::max)() << ")\n";
5384 push_error(ss.str());
5385 buf_ = "";
5386 length_ = 0;
5387 return;
5388 }
5389 size_t remaining_size = static_cast<size_t>(remaining_ull);
5390 if (remaining_size > max_stream_bytes) {
5391 std::stringstream ss;
5392 ss << "input stream too large (" << remaining_size
5393 << " bytes exceeds limit " << max_stream_bytes << " bytes)\n";
5394 push_error(ss.str());
5395 buf_ = "";
5396 length_ = 0;
5397 return;
5398 }
5399 owned_buf_.resize(remaining_size);
5400 if (remaining_size > 0) {
5401 is.read(&owned_buf_[0], static_cast<std::streamsize>(remaining_size));
5402 }
5403 size_t actually_read = static_cast<size_t>(is.gcount());
5404 owned_buf_.resize(actually_read);
5405 }
5406 }
5407 if (!can_seek || owned_buf_.empty()) {
5408 // Stream doesn't support seeking, or seek probing failed.
5409 if (can_seek) is.seekg(start_pos);
5410 is.clear();
5411 std::vector<char> content;
5412 char chunk[4096];
5413 size_t total_read = 0;
5414 while (is.good()) {
5415 is.read(chunk, static_cast<std::streamsize>(sizeof(chunk)));
5416 std::streamsize nread = is.gcount();
5417 if (nread <= 0) break;
5418 size_t n = static_cast<size_t>(nread);
5419 if (n > (max_stream_bytes - total_read)) {
5420 std::stringstream ss;
5421 ss << "input stream too large (exceeds limit " << max_stream_bytes
5422 << " bytes)\n";
5423 push_error(ss.str());
5424 owned_buf_.clear();
5425 buf_ = "";
5426 length_ = 0;
5427 return;
5428 }
5429 content.insert(content.end(), chunk, chunk + n);
5430 total_read += n;
5431 }
5432 owned_buf_.swap(content);
5433 }
5434 buf_ = owned_buf_.empty() ? "" : &owned_buf_[0];
5435 length_ = owned_buf_.size();
5436 }
5437
5438 bool eof() const { return idx_ >= length_; }
5439 size_t tell() const { return idx_; }
5440 size_t size() const { return length_; }
5441 size_t line_num() const { return line_num_; }
5442 size_t col_num() const { return col_num_; }
5443
5444 char peek() const {
5445 if (idx_ >= length_) return '\0';
5446 return buf_[idx_];
5447 }
5448
5449 char get() {
5450 if (idx_ >= length_) return '\0';
5451 char c = buf_[idx_++];
5452 if (c == '\n') { line_num_++; col_num_ = 1; } else { col_num_++; }
5453 return c;
5454 }
5455
5456 void advance(size_t n) {
5457 for (size_t i = 0; i < n && idx_ < length_; i++) {
5458 if (buf_[idx_] == '\n') { line_num_++; col_num_ = 1; } else { col_num_++; }
5459 idx_++;
5460 }
5461 }
5462
5463 void skip_space() {
5464 while (idx_ < length_ && (buf_[idx_] == ' ' || buf_[idx_] == '\t')) {
5465 col_num_++;
5466 idx_++;
5467 }
5468 }
5469
5470 void skip_space_and_cr() {
5471 while (idx_ < length_ && (buf_[idx_] == ' ' || buf_[idx_] == '\t' || buf_[idx_] == '\r')) {
5472 col_num_++;
5473 idx_++;
5474 }
5475 }
5476
5477 void skip_line() {
5478 while (idx_ < length_) {
5479 char c = buf_[idx_];
5480 if (c == '\n') {
5481 idx_++;
5482 line_num_++;
5483 col_num_ = 1;
5484 return;
5485 }
5486 if (c == '\r') {
5487 idx_++;
5488 if (idx_ < length_ && buf_[idx_] == '\n') {
5489 idx_++;
5490 }
5491 line_num_++;
5492 col_num_ = 1;
5493 return;
5494 }
5495 col_num_++;
5496 idx_++;
5497 }
5498 }
5499
5500 bool at_line_end() const {
5501 if (idx_ >= length_) return true;
5502 char c = buf_[idx_];
5503 return (c == '\n' || c == '\r' || c == '\0');
5504 }
5505
5506 std::string read_line() {
5507 std::string result;
5508 while (idx_ < length_) {
5509 char c = buf_[idx_];
5510 if (c == '\n' || c == '\r') break;
5511 result += c;
5512 col_num_++;
5513 idx_++;
5514 }
5515 return result;
5516 }
5517
5518 // Reads a whitespace-delimited token. Used by tests and as a general utility.
5519 std::string read_token() {
5520 skip_space();
5521 std::string result;
5522 while (idx_ < length_) {
5523 char c = buf_[idx_];
5524 if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break;
5525 result += c;
5526 col_num_++;
5527 idx_++;
5528 }
5529 return result;
5530 }
5531
5532 bool match(const char *prefix, size_t len) const {
5533 if (idx_ >= length_ || len > length_ - idx_) return false;
5534 return (memcmp(buf_ + idx_, prefix, len) == 0);
5535 }
5536
5537 bool char_at(size_t offset, char c) const {
5538 if (idx_ >= length_ || offset >= length_ - idx_) return false;
5539 return buf_[idx_ + offset] == c;
5540 }
5541
5542 char peek_at(size_t offset) const {
5543 if (idx_ >= length_ || offset >= length_ - idx_) return '\0';
5544 return buf_[idx_ + offset];
5545 }
5546
5547 const char *current_ptr() const {
5548 if (idx_ >= length_) return "";
5549 return buf_ + idx_;
5550 }
5551
5552 size_t remaining() const {
5553 return (idx_ < length_) ? (length_ - idx_) : 0;
5554 }
5555
5556 // Returns the full text of the current line (for diagnostic display).
5557 std::string current_line_text() const {
5558 // Scan backward to find line start
5559 size_t line_start = idx_;
5560 while (line_start > 0 && buf_[line_start - 1] != '\n' && buf_[line_start - 1] != '\r') {
5561 line_start--;
5562 }
5563 // Scan forward to find line end
5564 size_t line_end = idx_;
5565 while (line_end < length_ && buf_[line_end] != '\n' && buf_[line_end] != '\r') {
5566 line_end++;
5567 }
5568 return std::string(buf_ + line_start, line_end - line_start);
5569 }
5570
5571 // Clang-style formatted error with file:line:col and caret.
5572 std::string format_error(const std::string &filename, const std::string &msg) const {
5573 std::stringstream line_ss, col_ss;
5574 line_ss << line_num_;
5575 col_ss << col_num_;
5576 std::string result;
5577 result += filename + ":" + line_ss.str() + ":" + col_ss.str() + ": error: " + msg + "\n";
5578 std::string line_text = current_line_text();
5579 result += line_text + "\n";
5580 // Build caret line preserving tab alignment
5581 std::string caret;
5582 size_t caret_pos = (col_num_ > 0) ? (col_num_ - 1) : 0;
5583 for (size_t i = 0; i < caret_pos && i < line_text.size(); i++) {
5584 caret += (line_text[i] == '\t') ? '\t' : ' ';
5585 }
5586 caret += "^";
5587 result += caret + "\n";
5588 return result;
5589 }
5590
5591 std::string format_error(const std::string &msg) const {
5592 return format_error("<input>", msg);
5593 }
5594
5595 // Error stack
5596 void push_error(const std::string &msg) {
5597 errors_.push_back(msg);
5598 }
5599
5600 void push_formatted_error(const std::string &filename, const std::string &msg) {
5601 errors_.push_back(format_error(filename, msg));
5602 }
5603
5604 bool has_errors() const { return !errors_.empty(); }
5605
5606 std::string get_errors() const {
5607 std::string result;
5608 for (size_t i = 0; i < errors_.size(); i++) {
5609 result += errors_[i];
5610 }
5611 return result;
5612 }
5613
5614 const std::vector<std::string> &error_stack() const { return errors_; }
5615
5616 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.
5633struct 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
5722struct vertex_index_t {
5723 int v_idx, vt_idx, vn_idx;
5724 vertex_index_t() : v_idx(-1), vt_idx(-1), vn_idx(-1) {}
5725 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 : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}
5728};
5729
5730// Internal data structure for face representation
5731// index + smoothing group.
5732struct 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 face_t() : smoothing_group_id(0), pad_(0) {}
5739};
5740
5741// Internal data structure for line representation
5742struct __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
5750struct __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
5757struct tag_sizes {
5758 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
5764struct 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, ...)
5772struct PrimGroup {
5773 std::vector<face_t> faceGroup;
5774 std::vector<__line_t> lineGroup;
5775 std::vector<__points_t> pointsGroup;
5776
5777 void clear() {
5778 faceGroup.clear();
5779 lineGroup.clear();
5780 pointsGroup.clear();
5781 }
5782
5783 bool IsEmpty() const {
5784 return faceGroup.empty() && lineGroup.empty() && pointsGroup.empty();
5785 }
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#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#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0'))
5796
5797template <typename T>
5798static inline std::string toString(const T &t) {
5799 std::stringstream ss;
5800 ss << t;
5801 return ss.str();
5802}
5803
5804static inline std::string removeUtf8Bom(const std::string& input) {
5805 // UTF-8 BOM = 0xEF,0xBB,0xBF
5806 if (input.size() >= 3 &&
5807 static_cast<unsigned char>(input[0]) == 0xEF &&
5808 static_cast<unsigned char>(input[1]) == 0xBB &&
5809 static_cast<unsigned char>(input[2]) == 0xBF) {
5810 return input.substr(3); // Skip BOM
5811 }
5812 return input;
5813}
5814
5815// Trim trailing spaces and tabs from a string.
5816static inline std::string trimTrailingWhitespace(const std::string &s) {
5817 size_t end = s.find_last_not_of(" \t");
5818 if (end == std::string::npos) return "";
5819 return s.substr(0, end + 1);
5820}
5821
5822struct 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.
5829static inline int size_to_int(size_t sz) {
5830 return sz > static_cast<size_t>(INT_MAX) ? INT_MAX : static_cast<int>(sz);
5831}
5832
5833// Make index zero-base, and also support relative index.
5834static inline bool fixIndex(int idx, int n, int *ret, bool allow_zero,
5835 const warning_context &context) {
5836 if (!ret) {
5837 return false;
5838 }
5839
5840 if (idx > 0) {
5841 (*ret) = idx - 1;
5842 return true;
5843 }
5844
5845 if (idx == 0) {
5846 // zero is not allowed according to the spec.
5847 if (context.warn) {
5848 (*context.warn) +=
5849 context.filename + ":" + toString(context.line_number) +
5850 ": warning: zero value index found (will have a value of -1 for "
5851 "normal and tex indices)\n";
5852 }
5853
5854 (*ret) = idx - 1;
5855 return allow_zero;
5856 }
5857
5858 if (idx < 0) {
5859 (*ret) = n + idx; // negative value = relative
5860 if ((*ret) < 0) {
5861 return false; // invalid relative index
5862 }
5863 return true;
5864 }
5865
5866 return false; // never reach here.
5867}
5868
5869static inline std::string parseString(const char **token) {
5870 std::string s;
5871 (*token) += strspn((*token), " \t");
5872 size_t e = strcspn((*token), " \t\r");
5873 s = std::string((*token), &(*token)[e]);
5874 (*token) += e;
5875 return s;
5876}
5877
5878static inline int parseInt(const char **token) {
5879 (*token) += strspn((*token), " \t");
5880 int i = atoi((*token));
5881 (*token) += strcspn((*token), " \t\r");
5882 return i;
5883}
5884
5885#ifndef TINYOBJLOADER_DISABLE_FAST_FLOAT
5886
5887// ---- fast_float-based float parser (bit-exact with strtod, ~3x faster) ----
5888
5889namespace detail_fp {
5890
5891// Case-insensitive prefix match. Returns pointer past matched prefix, or NULL.
5892static inline const char *match_iprefix(const char *p, const char *end,
5893 const char *prefix) {
5894 while (*prefix) {
5895 if (p == end) return NULL;
5896 char c = *p;
5897 char e = *prefix;
5898 if (c >= 'A' && c <= 'Z') c += 32;
5899 if (e >= 'A' && e <= 'Z') e += 32;
5900 if (c != e) return NULL;
5901 ++p;
5902 ++prefix;
5903 }
5904 return p;
5905}
5906
5907// Try to parse nan/inf. Returns true if matched, sets *result and *end_ptr.
5908static inline bool tryParseNanInf(const char *first, const char *last,
5909 double *result, const char **end_ptr) {
5910 if (first >= last) return false;
5911
5912 const char *p = first;
5913 bool negative = false;
5914
5915 if (*p == '-') {
5916 negative = true;
5917 ++p;
5918 } else if (*p == '+') {
5919 ++p;
5920 }
5921
5922 if (p >= last) return false;
5923
5924 // Try "nan"
5925 const char *after = match_iprefix(p, last, "nan");
5926 if (after) {
5927 *result = 0.0; // nan -> 0.0 for OBJ
5928 *end_ptr = after;
5929 return true;
5930 }
5931
5932 // Try "infinity" first (longer match), then "inf"
5933 after = match_iprefix(p, last, "infinity");
5934 if (after) {
5935 *result = negative ? std::numeric_limits<double>::lowest()
5936 : (std::numeric_limits<double>::max)();
5937 *end_ptr = after;
5938 return true;
5939 }
5940
5941 after = match_iprefix(p, last, "inf");
5942 if (after) {
5943 *result = negative ? std::numeric_limits<double>::lowest()
5944 : (std::numeric_limits<double>::max)();
5945 *end_ptr = after;
5946 return true;
5947 }
5948
5949 return false;
5950}
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//
5964static bool tryParseDouble(const char *s, const char *s_end, double *result) {
5965 if (!s || !s_end || !result || s >= s_end) {
5966 return false;
5967 }
5968
5969 // Check for nan/inf (starts with [nNiI] or [+-] followed by [nNiI])
5970 const char *p = s;
5971 if (p < s_end && (*p == '+' || *p == '-')) ++p;
5972 if (p < s_end) {
5973 char fc = *p;
5974 if (fc >= 'A' && fc <= 'Z') fc += 32;
5975 if (fc == 'n' || fc == 'i') {
5976 const char *end_ptr;
5977 if (detail_fp::tryParseNanInf(s, s_end, result, &end_ptr)) {
5978 return true;
5979 }
5980 }
5981 }
5982
5983 // Use allow_leading_plus so fast_float handles '+' natively.
5984 double tmp;
5985 auto r = fast_float::from_chars(s, s_end, tmp,
5986 fast_float::chars_format::general |
5987 fast_float::chars_format::allow_leading_plus);
5988 if (r.ec == tinyobj_ff::ff_errc::ok) {
5989 *result = tmp;
5990 return true;
5991 }
5992 // On error (invalid_argument, result_out_of_range), *result is unchanged.
5993
5994 return false;
5995}
5996
5997static inline real_t parseReal(const char **token, double default_value = 0.0) {
5998 (*token) += strspn((*token), " \t");
5999 const char *end = (*token) + strcspn((*token), " \t\r");
6000 double val = default_value;
6001 tryParseDouble((*token), end, &val);
6002 real_t f = static_cast<real_t>(val);
6003 (*token) = end;
6004 return f;
6005}
6006
6007static inline bool parseReal(const char **token, real_t *out) {
6008 (*token) += strspn((*token), " \t");
6009 const char *end = (*token) + strcspn((*token), " \t\r");
6010 double val;
6011 bool ret = tryParseDouble((*token), end, &val);
6012 if (ret) {
6013 real_t f = static_cast<real_t>(val);
6014 (*out) = f;
6015 }
6016 (*token) = end;
6017 return ret;
6018}
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//
6051static 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
6175assemble:
6176 *result = (sign == '+' ? 1 : -1) *
6177 (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent)
6178 : mantissa);
6179 return true;
6180fail:
6181 return false;
6182}
6183
6184static 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
6194static 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
6209static inline void parseReal2(real_t *x, real_t *y, const char **token,
6210 const double default_x = 0.0,
6211 const double default_y = 0.0) {
6212 (*x) = parseReal(token, default_x);
6213 (*y) = parseReal(token, default_y);
6214}
6215
6216static 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 const double default_z = 0.0) {
6220 (*x) = parseReal(token, default_x);
6221 (*y) = parseReal(token, default_y);
6222 (*z) = parseReal(token, default_z);
6223}
6224
6225#if 0 // not used
6226static 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)
6241static 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 const double default_z = 0.0) {
6247 // TODO: Check error
6248 (*x) = parseReal(token, default_x);
6249 (*y) = parseReal(token, default_y);
6250 (*z) = parseReal(token, default_z);
6251
6252 // - 4 components(x, y, z, w) ot 6 components
6253 bool has_r = parseReal(token, r);
6254
6255 if (!has_r) {
6256 (*r) = (*g) = (*b) = 1.0;
6257 return 3;
6258 }
6259
6260 bool has_g = parseReal(token, g);
6261
6262 if (!has_g) {
6263 (*g) = (*b) = 1.0;
6264 return 4;
6265 }
6266
6267 bool has_b = parseReal(token, b);
6268
6269 if (!has_b) {
6270 (*r) = (*g) = (*b) = 1.0;
6271 return 3; // treated as xyz
6272 }
6273
6274 return 6;
6275}
6276
6277static inline bool parseOnOff(const char **token, bool default_value = true) {
6278 (*token) += strspn((*token), " \t");
6279 const char *end = (*token) + strcspn((*token), " \t\r");
6280
6281 bool ret = default_value;
6282 if ((0 == strncmp((*token), "on", 2))) {
6283 ret = true;
6284 } else if ((0 == strncmp((*token), "off", 3))) {
6285 ret = false;
6286 }
6287
6288 (*token) = end;
6289 return ret;
6290}
6291
6292static inline texture_type_t parseTextureType(
6293 const char **token, texture_type_t default_value = TEXTURE_TYPE_NONE) {
6294 (*token) += strspn((*token), " \t");
6295 const char *end = (*token) + strcspn((*token), " \t\r");
6296 texture_type_t ty = default_value;
6297
6298 if ((0 == strncmp((*token), "cube_top", strlen("cube_top")))) {
6299 ty = TEXTURE_TYPE_CUBE_TOP;
6300 } else if ((0 == strncmp((*token), "cube_bottom", strlen("cube_bottom")))) {
6301 ty = TEXTURE_TYPE_CUBE_BOTTOM;
6302 } else if ((0 == strncmp((*token), "cube_left", strlen("cube_left")))) {
6303 ty = TEXTURE_TYPE_CUBE_LEFT;
6304 } else if ((0 == strncmp((*token), "cube_right", strlen("cube_right")))) {
6305 ty = TEXTURE_TYPE_CUBE_RIGHT;
6306 } else if ((0 == strncmp((*token), "cube_front", strlen("cube_front")))) {
6307 ty = TEXTURE_TYPE_CUBE_FRONT;
6308 } else if ((0 == strncmp((*token), "cube_back", strlen("cube_back")))) {
6309 ty = TEXTURE_TYPE_CUBE_BACK;
6310 } else if ((0 == strncmp((*token), "sphere", strlen("sphere")))) {
6311 ty = TEXTURE_TYPE_SPHERE;
6312 }
6313
6314 (*token) = end;
6315 return ty;
6316}
6317
6318static tag_sizes parseTagTriple(const char **token) {
6319 tag_sizes ts;
6320
6321 (*token) += strspn((*token), " \t");
6322 ts.num_ints = atoi((*token));
6323 (*token) += strcspn((*token), "/ \t\r");
6324 if ((*token)[0] != '/') {
6325 return ts;
6326 }
6327
6328 (*token)++; // Skip '/'
6329
6330 (*token) += strspn((*token), " \t");
6331 ts.num_reals = atoi((*token));
6332 (*token) += strcspn((*token), "/ \t\r");
6333 if ((*token)[0] != '/') {
6334 return ts;
6335 }
6336 (*token)++; // Skip '/'
6337
6338 ts.num_strings = parseInt(token);
6339
6340 return ts;
6341}
6342
6343// Parse triples with index offsets: i, i/j/k, i//k, i/j
6344static bool parseTriple(const char **token, int vsize, int vnsize, int vtsize,
6345 vertex_index_t *ret, const warning_context &context) {
6346 if (!ret) {
6347 return false;
6348 }
6349
6350 vertex_index_t vi(-1);
6351
6352 if (!fixIndex(atoi((*token)), vsize, &vi.v_idx, false, context)) {
6353 return false;
6354 }
6355
6356 (*token) += strcspn((*token), "/ \t\r");
6357 if ((*token)[0] != '/') {
6358 (*ret) = vi;
6359 return true;
6360 }
6361 (*token)++;
6362
6363 // i//k
6364 if ((*token)[0] == '/') {
6365 (*token)++;
6366 if (!fixIndex(atoi((*token)), vnsize, &vi.vn_idx, true, context)) {
6367 return false;
6368 }
6369 (*token) += strcspn((*token), "/ \t\r");
6370 (*ret) = vi;
6371 return true;
6372 }
6373
6374 // i/j/k or i/j
6375 if (!fixIndex(atoi((*token)), vtsize, &vi.vt_idx, true, context)) {
6376 return false;
6377 }
6378
6379 (*token) += strcspn((*token), "/ \t\r");
6380 if ((*token)[0] != '/') {
6381 (*ret) = vi;
6382 return true;
6383 }
6384
6385 // i/j/k
6386 (*token)++; // skip '/'
6387 if (!fixIndex(atoi((*token)), vnsize, &vi.vn_idx, true, context)) {
6388 return false;
6389 }
6390 (*token) += strcspn((*token), "/ \t\r");
6391
6392 (*ret) = vi;
6393
6394 return true;
6395}
6396
6397// Parse raw triples: i, i/j/k, i//k, i/j
6398static vertex_index_t parseRawTriple(const char **token) {
6399 vertex_index_t vi(static_cast<int>(0)); // 0 is an invalid index in OBJ
6400
6401 vi.v_idx = atoi((*token));
6402 (*token) += strcspn((*token), "/ \t\r");
6403 if ((*token)[0] != '/') {
6404 return vi;
6405 }
6406 (*token)++;
6407
6408 // i//k
6409 if ((*token)[0] == '/') {
6410 (*token)++;
6411 vi.vn_idx = atoi((*token));
6412 (*token) += strcspn((*token), "/ \t\r");
6413 return vi;
6414 }
6415
6416 // i/j/k or i/j
6417 vi.vt_idx = atoi((*token));
6418 (*token) += strcspn((*token), "/ \t\r");
6419 if ((*token)[0] != '/') {
6420 return vi;
6421 }
6422
6423 // i/j/k
6424 (*token)++; // skip '/'
6425 vi.vn_idx = atoi((*token));
6426 (*token) += strcspn((*token), "/ \t\r");
6427 return vi;
6428}
6429
6430// --- Stream-based parse functions ---
6431
6432static inline std::string sr_parseString(StreamReader &sr) {
6433 sr.skip_space();
6434 std::string s;
6435 while (!sr.eof()) {
6436 char c = sr.peek();
6437 if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break;
6438 s += c;
6439 sr.advance(1);
6440 }
6441 return s;
6442}
6443
6444static inline int sr_parseInt(StreamReader &sr) {
6445 sr.skip_space();
6446 const char *start = sr.current_ptr();
6447 size_t rem = sr.remaining();
6448 size_t len = 0;
6449 while (len < rem) {
6450 char c = start[len];
6451 if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break;
6452 len++;
6453 }
6454 int i = 0;
6455 if (len > 0) {
6456 char tmp[64];
6457 size_t copy_len = len < 63 ? len : 63;
6458 if (copy_len != len) {
6459 sr.advance(len);
6460 return 0;
6461 }
6462 memcpy(tmp, start, copy_len);
6463 tmp[copy_len] = '\0';
6464 errno = 0;
6465 char *endptr = NULL;
6466 long val = strtol(tmp, &endptr, 10);
6467 const bool has_error =
6468 (errno == ERANGE || endptr == tmp ||
6469 val > (std::numeric_limits<int>::max)() ||
6470 val < (std::numeric_limits<int>::min)());
6471 if (!has_error) {
6472 i = static_cast<int>(val);
6473 }
6474 }
6475 sr.advance(len);
6476 return i;
6477}
6478
6479static inline real_t sr_parseReal(StreamReader &sr, double default_value = 0.0) {
6480 sr.skip_space();
6481 const char *start = sr.current_ptr();
6482 size_t rem = sr.remaining();
6483 size_t len = 0;
6484 while (len < rem) {
6485 char c = start[len];
6486 if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break;
6487 len++;
6488 }
6489 double val = default_value;
6490 if (len > 0) {
6491 tryParseDouble(start, start + len, &val);
6492 }
6493 sr.advance(len);
6494 return static_cast<real_t>(val);
6495}
6496
6497static inline bool sr_parseReal(StreamReader &sr, real_t *out) {
6498 sr.skip_space();
6499 const char *start = sr.current_ptr();
6500 size_t rem = sr.remaining();
6501 size_t len = 0;
6502 while (len < rem) {
6503 char c = start[len];
6504 if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break;
6505 len++;
6506 }
6507 if (len == 0) return false;
6508 double val;
6509 bool ret = tryParseDouble(start, start + len, &val);
6510 if (ret) {
6511 (*out) = static_cast<real_t>(val);
6512 }
6513 sr.advance(len);
6514 return ret;
6515}
6516
6517static inline void sr_parseReal2(real_t *x, real_t *y, StreamReader &sr,
6518 const double default_x = 0.0,
6519 const double default_y = 0.0) {
6520 (*x) = sr_parseReal(sr, default_x);
6521 (*y) = sr_parseReal(sr, default_y);
6522}
6523
6524static 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 const double default_z = 0.0) {
6529 (*x) = sr_parseReal(sr, default_x);
6530 (*y) = sr_parseReal(sr, default_y);
6531 (*z) = sr_parseReal(sr, default_z);
6532}
6533
6534static 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 const double default_z = 0.0) {
6540 (*x) = sr_parseReal(sr, default_x);
6541 (*y) = sr_parseReal(sr, default_y);
6542 (*z) = sr_parseReal(sr, default_z);
6543
6544 bool has_r = sr_parseReal(sr, r);
6545 if (!has_r) {
6546 (*r) = (*g) = (*b) = 1.0;
6547 return 3;
6548 }
6549
6550 bool has_g = sr_parseReal(sr, g);
6551 if (!has_g) {
6552 (*g) = (*b) = 1.0;
6553 return 4;
6554 }
6555
6556 bool has_b = sr_parseReal(sr, b);
6557 if (!has_b) {
6558 (*r) = (*g) = (*b) = 1.0;
6559 return 3;
6560 }
6561
6562 return 6;
6563}
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
6570static inline bool sr_parseInt(StreamReader &sr, int *out, std::string *err,
6571 const std::string &filename) {
6572 sr.skip_space();
6573 const char *start = sr.current_ptr();
6574 size_t rem = sr.remaining();
6575 size_t len = 0;
6576 while (len < rem) {
6577 char c = start[len];
6578 if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break;
6579 len++;
6580 }
6581 if (len == 0) {
6582 if (err) {
6583 (*err) += sr.format_error(filename, "expected integer value");
6584 }
6585 *out = 0;
6586 return false;
6587 }
6588 char tmp[64];
6589 size_t copy_len = len < 63 ? len : 63;
6590 memcpy(tmp, start, copy_len);
6591 tmp[copy_len] = '\0';
6592 if (copy_len != len) {
6593 if (err) {
6594 (*err) += sr.format_error(filename, "integer value too long");
6595 }
6596 *out = 0;
6597 sr.advance(len);
6598 return false;
6599 }
6600 errno = 0;
6601 char *endptr = NULL;
6602 long val = strtol(tmp, &endptr, 10);
6603 if (errno == ERANGE || val > (std::numeric_limits<int>::max)() ||
6604 val < (std::numeric_limits<int>::min)()) {
6605 if (err) {
6606 (*err) += sr.format_error(filename,
6607 "integer value out of range, got '" + std::string(tmp) + "'");
6608 }
6609 *out = 0;
6610 sr.advance(len);
6611 return false;
6612 }
6613 if (endptr == tmp || (*endptr != '\0' && *endptr != ' ' && *endptr != '\t')) {
6614 if (err) {
6615 (*err) += sr.format_error(filename,
6616 "expected integer, got '" + std::string(tmp) + "'");
6617 }
6618 *out = 0;
6619 sr.advance(len);
6620 return false;
6621 }
6622 *out = static_cast<int>(val);
6623 sr.advance(len);
6624 return true;
6625}
6626
6627static inline bool sr_parseReal(StreamReader &sr, real_t *out,
6628 double default_value,
6629 std::string *err,
6630 const std::string &filename) {
6631 sr.skip_space();
6632 const char *start = sr.current_ptr();
6633 size_t rem = sr.remaining();
6634 size_t len = 0;
6635 while (len < rem) {
6636 char c = start[len];
6637 if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break;
6638 len++;
6639 }
6640 if (len == 0) {
6641 // No token to parse — not necessarily an error (e.g. optional component).
6642 *out = static_cast<real_t>(default_value);
6643 return true;
6644 }
6645 double val;
6646 if (!tryParseDouble(start, start + len, &val)) {
6647 if (err) {
6648 char tmp[64];
6649 size_t copy_len = len < 63 ? len : 63;
6650 memcpy(tmp, start, copy_len);
6651 tmp[copy_len] = '\0';
6652 (*err) += sr.format_error(filename,
6653 "expected number, got '" + std::string(tmp) + "'");
6654 }
6655 *out = static_cast<real_t>(default_value);
6656 sr.advance(len);
6657 return false;
6658 }
6659 *out = static_cast<real_t>(val);
6660 sr.advance(len);
6661 return true;
6662}
6663
6664static 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 const double default_y = 0.0) {
6669 if (!sr_parseReal(sr, x, default_x, err, filename)) return false;
6670 if (!sr_parseReal(sr, y, default_y, err, filename)) return false;
6671 return true;
6672}
6673
6674static 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 const double default_z = 0.0) {
6681 if (!sr_parseReal(sr, x, default_x, err, filename)) return false;
6682 if (!sr_parseReal(sr, y, default_y, err, filename)) return false;
6683 if (!sr_parseReal(sr, z, default_z, err, filename)) return false;
6684 return true;
6685}
6686
6687// Returns number of components parsed (3, 4, or 6) on success, -1 on error.
6688static 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 const double default_z = 0.0) {
6696 if (!sr_parseReal(sr, x, default_x, err, filename)) return -1;
6697 if (!sr_parseReal(sr, y, default_y, err, filename)) return -1;
6698 if (!sr_parseReal(sr, z, default_z, err, filename)) return -1;
6699
6700 bool has_r = sr_parseReal(sr, r);
6701 if (!has_r) {
6702 (*r) = (*g) = (*b) = 1.0;
6703 return 3;
6704 }
6705
6706 bool has_g = sr_parseReal(sr, g);
6707 if (!has_g) {
6708 (*g) = (*b) = 1.0;
6709 return 4;
6710 }
6711
6712 bool has_b = sr_parseReal(sr, b);
6713 if (!has_b) {
6714 (*r) = (*g) = (*b) = 1.0;
6715 return 3;
6716 }
6717
6718 return 6;
6719}
6720
6721static inline int sr_parseIntNoSkip(StreamReader &sr);
6722
6723// Advance past remaining characters in a tag triple field (stops at '/', whitespace, or line end).
6724static inline void sr_skipTagField(StreamReader &sr) {
6725 while (!sr.eof() && !sr.at_line_end() && !IS_SPACE(sr.peek()) &&
6726 sr.peek() != '/') {
6727 sr.advance(1);
6728 }
6729}
6730
6731static tag_sizes sr_parseTagTriple(StreamReader &sr) {
6732 tag_sizes ts;
6733
6734 sr.skip_space();
6735 ts.num_ints = sr_parseIntNoSkip(sr);
6736 sr_skipTagField(sr);
6737 if (!sr.eof() && sr.peek() == '/') {
6738 sr.advance(1);
6739 sr.skip_space();
6740 ts.num_reals = sr_parseIntNoSkip(sr);
6741 sr_skipTagField(sr);
6742 if (!sr.eof() && sr.peek() == '/') {
6743 sr.advance(1);
6744 ts.num_strings = sr_parseInt(sr);
6745 }
6746 }
6747 return ts;
6748}
6749
6750static inline int sr_parseIntNoSkip(StreamReader &sr) {
6751 const char *start = sr.current_ptr();
6752 size_t rem = sr.remaining();
6753 size_t len = 0;
6754 if (len < rem && (start[len] == '+' || start[len] == '-')) len++;
6755 while (len < rem && start[len] >= '0' && start[len] <= '9') len++;
6756 int i = 0;
6757 if (len > 0) {
6758 char tmp[64];
6759 size_t copy_len = len < 63 ? len : 63;
6760 if (copy_len != len) {
6761 sr.advance(len);
6762 return 0;
6763 }
6764 memcpy(tmp, start, copy_len);
6765 tmp[copy_len] = '\0';
6766 errno = 0;
6767 char *endptr = NULL;
6768 long val = strtol(tmp, &endptr, 10);
6769 if (errno == 0 && endptr != tmp && *endptr == '\0' &&
6770 val <= (std::numeric_limits<int>::max)() &&
6771 val >= (std::numeric_limits<int>::min)()) {
6772 i = static_cast<int>(val);
6773 }
6774 }
6775 sr.advance(len);
6776 return i;
6777}
6778
6779static inline void sr_skipUntil(StreamReader &sr, const char *delims) {
6780 while (!sr.eof()) {
6781 char c = sr.peek();
6782 for (const char *d = delims; *d; d++) {
6783 if (c == *d) return;
6784 }
6785 sr.advance(1);
6786 }
6787}
6788
6789static bool sr_parseTriple(StreamReader &sr, int vsize, int vnsize, int vtsize,
6790 vertex_index_t *ret, const warning_context &context) {
6791 if (!ret) return false;
6792
6793 vertex_index_t vi(-1);
6794
6795 sr.skip_space();
6796 if (!fixIndex(sr_parseIntNoSkip(sr), vsize, &vi.v_idx, false, context)) {
6797 return false;
6798 }
6799
6800 sr_skipUntil(sr, "/ \t\r\n");
6801 if (sr.eof() || sr.peek() != '/') {
6802 (*ret) = vi;
6803 return true;
6804 }
6805 sr.advance(1);
6806
6807 // i//k
6808 if (!sr.eof() && sr.peek() == '/') {
6809 sr.advance(1);
6810 if (!fixIndex(sr_parseIntNoSkip(sr), vnsize, &vi.vn_idx, true, context)) {
6811 return false;
6812 }
6813 sr_skipUntil(sr, "/ \t\r\n");
6814 (*ret) = vi;
6815 return true;
6816 }
6817
6818 // i/j/k or i/j
6819 if (!fixIndex(sr_parseIntNoSkip(sr), vtsize, &vi.vt_idx, true, context)) {
6820 return false;
6821 }
6822
6823 sr_skipUntil(sr, "/ \t\r\n");
6824 if (sr.eof() || sr.peek() != '/') {
6825 (*ret) = vi;
6826 return true;
6827 }
6828
6829 // i/j/k
6830 sr.advance(1);
6831 if (!fixIndex(sr_parseIntNoSkip(sr), vnsize, &vi.vn_idx, true, context)) {
6832 return false;
6833 }
6834 sr_skipUntil(sr, "/ \t\r\n");
6835
6836 (*ret) = vi;
6837 return true;
6838}
6839
6840static vertex_index_t sr_parseRawTriple(StreamReader &sr) {
6841 vertex_index_t vi(static_cast<int>(0));
6842
6843 sr.skip_space();
6844 vi.v_idx = sr_parseIntNoSkip(sr);
6845 sr_skipUntil(sr, "/ \t\r\n");
6846 if (sr.eof() || sr.peek() != '/') return vi;
6847 sr.advance(1);
6848
6849 // i//k
6850 if (!sr.eof() && sr.peek() == '/') {
6851 sr.advance(1);
6852 vi.vn_idx = sr_parseIntNoSkip(sr);
6853 sr_skipUntil(sr, "/ \t\r\n");
6854 return vi;
6855 }
6856
6857 // i/j/k or i/j
6858 vi.vt_idx = sr_parseIntNoSkip(sr);
6859 sr_skipUntil(sr, "/ \t\r\n");
6860 if (sr.eof() || sr.peek() != '/') return vi;
6861
6862 sr.advance(1);
6863 vi.vn_idx = sr_parseIntNoSkip(sr);
6864 sr_skipUntil(sr, "/ \t\r\n");
6865 return vi;
6866}
6867
6868bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt,
6869 const char *linebuf) {
6870 // @todo { write more robust lexer and parser. }
6871 bool found_texname = false;
6872 std::string texture_name;
6873
6874 const char *token = linebuf; // Assume line ends with NULL
6875
6876 while (!IS_NEW_LINE((*token))) {
6877 token += strspn(token, " \t"); // skip space
6878 if ((0 == strncmp(token, "-blendu", 7)) && IS_SPACE((token[7]))) {
6879 token += 8;
6880 texopt->blendu = parseOnOff(&token, /* default */ true);
6881 } else if ((0 == strncmp(token, "-blendv", 7)) && IS_SPACE((token[7]))) {
6882 token += 8;
6883 texopt->blendv = parseOnOff(&token, /* default */ true);
6884 } else if ((0 == strncmp(token, "-clamp", 6)) && IS_SPACE((token[6]))) {
6885 token += 7;
6886 texopt->clamp = parseOnOff(&token, /* default */ true);
6887 } else if ((0 == strncmp(token, "-boost", 6)) && IS_SPACE((token[6]))) {
6888 token += 7;
6889 texopt->sharpness = parseReal(&token, 1.0);
6890 } else if ((0 == strncmp(token, "-bm", 3)) && IS_SPACE((token[3]))) {
6891 token += 4;
6892 texopt->bump_multiplier = parseReal(&token, 1.0);
6893 } else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) {
6894 token += 3;
6895 parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]),
6896 &(texopt->origin_offset[2]), &token);
6897 } else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) {
6898 token += 3;
6899 parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]),
6900 &token, 1.0, 1.0, 1.0);
6901 } else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) {
6902 token += 3;
6903 parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]),
6904 &(texopt->turbulence[2]), &token);
6905 } else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) {
6906 token += 5;
6907 texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE);
6908 } else if ((0 == strncmp(token, "-texres", 7)) && IS_SPACE((token[7]))) {
6909 token += 7;
6910 // TODO(syoyo): Check if arg is int type.
6911 texopt->texture_resolution = parseInt(&token);
6912 } else if ((0 == strncmp(token, "-imfchan", 8)) && IS_SPACE((token[8]))) {
6913 token += 9;
6914 token += strspn(token, " \t");
6915 const char *end = token + strcspn(token, " \t\r");
6916 if ((end - token) == 1) { // Assume one char for -imfchan
6917 texopt->imfchan = (*token);
6918 }
6919 token = end;
6920 } else if ((0 == strncmp(token, "-mm", 3)) && IS_SPACE((token[3]))) {
6921 token += 4;
6922 parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0);
6923 } else if ((0 == strncmp(token, "-colorspace", 11)) &&
6924 IS_SPACE((token[11]))) {
6925 token += 12;
6926 texopt->colorspace = parseString(&token);
6927 } 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 texture_name = std::string(token);
6939 token += texture_name.length();
6940#endif
6941
6942 found_texname = true;
6943 }
6944 }
6945
6946 if (found_texname) {
6947 (*texname) = texture_name;
6948 return true;
6949 } else {
6950 return false;
6951 }
6952}
6953
6954static void InitTexOpt(texture_option_t *texopt, const bool is_bump) {
6955 if (is_bump) {
6956 texopt->imfchan = 'l';
6957 } else {
6958 texopt->imfchan = 'm';
6959 }
6960 texopt->bump_multiplier = static_cast<real_t>(1.0);
6961 texopt->clamp = false;
6962 texopt->blendu = true;
6963 texopt->blendv = true;
6964 texopt->sharpness = static_cast<real_t>(1.0);
6965 texopt->brightness = static_cast<real_t>(0.0);
6966 texopt->contrast = static_cast<real_t>(1.0);
6967 texopt->origin_offset[0] = static_cast<real_t>(0.0);
6968 texopt->origin_offset[1] = static_cast<real_t>(0.0);
6969 texopt->origin_offset[2] = static_cast<real_t>(0.0);
6970 texopt->scale[0] = static_cast<real_t>(1.0);
6971 texopt->scale[1] = static_cast<real_t>(1.0);
6972 texopt->scale[2] = static_cast<real_t>(1.0);
6973 texopt->turbulence[0] = static_cast<real_t>(0.0);
6974 texopt->turbulence[1] = static_cast<real_t>(0.0);
6975 texopt->turbulence[2] = static_cast<real_t>(0.0);
6976 texopt->texture_resolution = -1;
6977 texopt->type = TEXTURE_TYPE_NONE;
6978}
6979
6980static void InitMaterial(material_t *material) {
6981 InitTexOpt(&material->ambient_texopt, /* is_bump */ false);
6982 InitTexOpt(&material->diffuse_texopt, /* is_bump */ false);
6983 InitTexOpt(&material->specular_texopt, /* is_bump */ false);
6984 InitTexOpt(&material->specular_highlight_texopt, /* is_bump */ false);
6985 InitTexOpt(&material->bump_texopt, /* is_bump */ true);
6986 InitTexOpt(&material->displacement_texopt, /* is_bump */ false);
6987 InitTexOpt(&material->alpha_texopt, /* is_bump */ false);
6988 InitTexOpt(&material->reflection_texopt, /* is_bump */ false);
6989 InitTexOpt(&material->roughness_texopt, /* is_bump */ false);
6990 InitTexOpt(&material->metallic_texopt, /* is_bump */ false);
6991 InitTexOpt(&material->sheen_texopt, /* is_bump */ false);
6992 InitTexOpt(&material->emissive_texopt, /* is_bump */ false);
6993 InitTexOpt(&material->normal_texopt,
6994 /* is_bump */ false); // @fixme { is_bump will be true? }
6995 material->name = "";
6996 material->ambient_texname = "";
6997 material->diffuse_texname = "";
6998 material->specular_texname = "";
6999 material->specular_highlight_texname = "";
7000 material->bump_texname = "";
7001 material->displacement_texname = "";
7002 material->reflection_texname = "";
7003 material->alpha_texname = "";
7004 for (int i = 0; i < 3; i++) {
7005 material->ambient[i] = static_cast<real_t>(0.0);
7006 material->diffuse[i] = static_cast<real_t>(0.0);
7007 material->specular[i] = static_cast<real_t>(0.0);
7008 material->transmittance[i] = static_cast<real_t>(0.0);
7009 material->emission[i] = static_cast<real_t>(0.0);
7010 }
7011 material->illum = 0;
7012 material->dissolve = static_cast<real_t>(1.0);
7013 material->shininess = static_cast<real_t>(1.0);
7014 material->ior = static_cast<real_t>(1.0);
7015
7016 material->roughness = static_cast<real_t>(0.0);
7017 material->metallic = static_cast<real_t>(0.0);
7018 material->sheen = static_cast<real_t>(0.0);
7019 material->clearcoat_thickness = static_cast<real_t>(0.0);
7020 material->clearcoat_roughness = static_cast<real_t>(0.0);
7021 material->anisotropy_rotation = static_cast<real_t>(0.0);
7022 material->anisotropy = static_cast<real_t>(0.0);
7023 material->roughness_texname = "";
7024 material->metallic_texname = "";
7025 material->sheen_texname = "";
7026 material->emissive_texname = "";
7027 material->normal_texname = "";
7028
7029 material->unknown_parameter.clear();
7030}
7031
7032// code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
7033template <typename T>
7034static int pnpoly(int nvert, T *vertx, T *verty, T testx, T testy) {
7035 int i, j, c = 0;
7036 for (i = 0, j = nvert - 1; i < nvert; j = i++) {
7037 if (((verty[i] > testy) != (verty[j] > testy)) &&
7038 (testx <
7039 (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) +
7040 vertx[i]))
7041 c = !c;
7042 }
7043 return c;
7044}
7045
7046struct TinyObjPoint {
7047 real_t x, y, z;
7048 TinyObjPoint() : x(0), y(0), z(0) {}
7049 TinyObjPoint(real_t x_, real_t y_, real_t z_) : x(x_), y(y_), z(z_) {}
7050};
7051
7052inline TinyObjPoint cross(const TinyObjPoint &v1, const TinyObjPoint &v2) {
7053 return TinyObjPoint(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z,
7054 v1.x * v2.y - v1.y * v2.x);
7055}
7056
7057inline real_t dot(const TinyObjPoint &v1, const TinyObjPoint &v2) {
7058 return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z);
7059}
7060
7061inline real_t GetLength(TinyObjPoint &e) {
7062 return std::sqrt(e.x * e.x + e.y * e.y + e.z * e.z);
7063}
7064
7065inline TinyObjPoint Normalize(TinyObjPoint e) {
7066 real_t len = GetLength(e);
7067 if (len <= real_t(0)) return TinyObjPoint(real_t(0), real_t(0), real_t(0));
7068 real_t inv_length = real_t(1) / len;
7069 return TinyObjPoint(e.x * inv_length, e.y * inv_length, e.z * inv_length);
7070}
7071
7072inline TinyObjPoint WorldToLocal(const TinyObjPoint &a, const TinyObjPoint &u,
7073 const TinyObjPoint &v, const TinyObjPoint &w) {
7074 return TinyObjPoint(dot(a, u), dot(a, v), dot(a, w));
7075}
7076
7077// TODO(syoyo): refactor function.
7078static 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 std::string *warn) {
7083 if (prim_group.IsEmpty()) {
7084 return false;
7085 }
7086
7087 shape->name = name;
7088
7089 // polygon
7090 if (!prim_group.faceGroup.empty()) {
7091 // Flatten vertices and indices
7092 for (size_t i = 0; i < prim_group.faceGroup.size(); i++) {
7093 const face_t &face = prim_group.faceGroup[i];
7094
7095 size_t npolys = face.vertex_indices.size();
7096
7097 if (npolys < 3) {
7098 // Face must have 3+ vertices.
7099 if (warn) {
7100 (*warn) += "Degenerated face found\n.";
7101 }
7102 continue;
7103 }
7104
7105 if (triangulate && npolys != 3) {
7106 if (npolys == 4) {
7107 vertex_index_t i0 = face.vertex_indices[0];
7108 vertex_index_t i1 = face.vertex_indices[1];
7109 vertex_index_t i2 = face.vertex_indices[2];
7110 vertex_index_t i3 = face.vertex_indices[3];
7111
7112 if (i0.v_idx < 0 || i1.v_idx < 0 || i2.v_idx < 0 || i3.v_idx < 0) {
7113 if (warn) {
7114 (*warn) += "Face with invalid vertex index found.\n";
7115 }
7116 continue;
7117 }
7118
7119 size_t vi0 = size_t(i0.v_idx);
7120 size_t vi1 = size_t(i1.v_idx);
7121 size_t vi2 = size_t(i2.v_idx);
7122 size_t vi3 = size_t(i3.v_idx);
7123
7124 if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) ||
7125 ((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 if (warn) {
7129 (*warn) += "Face with invalid vertex index found.\n";
7130 }
7131 continue;
7132 }
7133
7134 real_t v0x = v[vi0 * 3 + 0];
7135 real_t v0y = v[vi0 * 3 + 1];
7136 real_t v0z = v[vi0 * 3 + 2];
7137 real_t v1x = v[vi1 * 3 + 0];
7138 real_t v1y = v[vi1 * 3 + 1];
7139 real_t v1z = v[vi1 * 3 + 2];
7140 real_t v2x = v[vi2 * 3 + 0];
7141 real_t v2y = v[vi2 * 3 + 1];
7142 real_t v2z = v[vi2 * 3 + 2];
7143 real_t v3x = v[vi3 * 3 + 0];
7144 real_t v3y = v[vi3 * 3 + 1];
7145 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 real_t e02x = v2x - v0x;
7166 real_t e02y = v2y - v0y;
7167 real_t e02z = v2z - v0z;
7168 real_t e13x = v3x - v1x;
7169 real_t e13y = v3y - v1y;
7170 real_t e13z = v3z - v1z;
7171
7172 real_t sqr02 = e02x * e02x + e02y * e02y + e02z * e02z;
7173 real_t sqr13 = e13x * e13x + e13y * e13y + e13z * e13z;
7174
7175 index_t idx0, idx1, idx2, idx3;
7176
7177 idx0.vertex_index = i0.v_idx;
7178 idx0.normal_index = i0.vn_idx;
7179 idx0.texcoord_index = i0.vt_idx;
7180 idx1.vertex_index = i1.v_idx;
7181 idx1.normal_index = i1.vn_idx;
7182 idx1.texcoord_index = i1.vt_idx;
7183 idx2.vertex_index = i2.v_idx;
7184 idx2.normal_index = i2.vn_idx;
7185 idx2.texcoord_index = i2.vt_idx;
7186 idx3.vertex_index = i3.v_idx;
7187 idx3.normal_index = i3.vn_idx;
7188 idx3.texcoord_index = i3.vt_idx;
7189
7190 if (sqr02 < sqr13) {
7191 // [0, 1, 2], [0, 2, 3]
7192 shape->mesh.indices.push_back(idx0);
7193 shape->mesh.indices.push_back(idx1);
7194 shape->mesh.indices.push_back(idx2);
7195
7196 shape->mesh.indices.push_back(idx0);
7197 shape->mesh.indices.push_back(idx2);
7198 shape->mesh.indices.push_back(idx3);
7199 } else {
7200 // [0, 1, 3], [1, 2, 3]
7201 shape->mesh.indices.push_back(idx0);
7202 shape->mesh.indices.push_back(idx1);
7203 shape->mesh.indices.push_back(idx3);
7204
7205 shape->mesh.indices.push_back(idx1);
7206 shape->mesh.indices.push_back(idx2);
7207 shape->mesh.indices.push_back(idx3);
7208 }
7209
7210 // Two triangle faces
7211 shape->mesh.num_face_vertices.push_back(3);
7212 shape->mesh.num_face_vertices.push_back(3);
7213
7214 shape->mesh.material_ids.push_back(material_id);
7215 shape->mesh.material_ids.push_back(material_id);
7216
7217 shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id);
7218 shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id);
7219
7220 } 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 vertex_index_t i0 = face.vertex_indices[0];
7363 vertex_index_t i1(-1);
7364 vertex_index_t i2 = face.vertex_indices[1];
7365
7366 // find the two axes to work in
7367 size_t axes[2] = {1, 2};
7368 for (size_t k = 0; k < npolys; ++k) {
7369 i0 = face.vertex_indices[(k + 0) % npolys];
7370 i1 = face.vertex_indices[(k + 1) % npolys];
7371 i2 = face.vertex_indices[(k + 2) % npolys];
7372 size_t vi0 = size_t(i0.v_idx);
7373 size_t vi1 = size_t(i1.v_idx);
7374 size_t vi2 = size_t(i2.v_idx);
7375
7376 if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) ||
7377 ((3 * vi2 + 2) >= v.size())) {
7378 // Invalid triangle.
7379 // FIXME(syoyo): Is it ok to simply skip this invalid triangle?
7380 continue;
7381 }
7382 real_t v0x = v[vi0 * 3 + 0];
7383 real_t v0y = v[vi0 * 3 + 1];
7384 real_t v0z = v[vi0 * 3 + 2];
7385 real_t v1x = v[vi1 * 3 + 0];
7386 real_t v1y = v[vi1 * 3 + 1];
7387 real_t v1z = v[vi1 * 3 + 2];
7388 real_t v2x = v[vi2 * 3 + 0];
7389 real_t v2y = v[vi2 * 3 + 1];
7390 real_t v2z = v[vi2 * 3 + 2];
7391 real_t e0x = v1x - v0x;
7392 real_t e0y = v1y - v0y;
7393 real_t e0z = v1z - v0z;
7394 real_t e1x = v2x - v1x;
7395 real_t e1y = v2y - v1y;
7396 real_t e1z = v2z - v1z;
7397 real_t cx = std::fabs(e0y * e1z - e0z * e1y);
7398 real_t cy = std::fabs(e0z * e1x - e0x * e1z);
7399 real_t cz = std::fabs(e0x * e1y - e0y * e1x);
7400 const real_t epsilon = std::numeric_limits<real_t>::epsilon();
7401 // std::cout << "cx " << cx << ", cy " << cy << ", cz " << cz <<
7402 // "\n";
7403 if (cx > epsilon || cy > epsilon || cz > epsilon) {
7404 // std::cout << "corner\n";
7405 // found a corner
7406 if (cx > cy && cx > cz) {
7407 // std::cout << "pattern0\n";
7408 } else {
7409 // std::cout << "axes[0] = 0\n";
7410 axes[0] = 0;
7411 if (cz > cx && cz > cy) {
7412 // std::cout << "axes[1] = 1\n";
7413 axes[1] = 1;
7414 }
7415 }
7416 break;
7417 }
7418 }
7419
7420 face_t remainingFace = face; // copy
7421 size_t guess_vert = 0;
7422 vertex_index_t ind[3];
7423 real_t vx[3];
7424 real_t vy[3];
7425
7426 // How many iterations can we do without decreasing the remaining
7427 // vertices.
7428 size_t remainingIterations = face.vertex_indices.size();
7429 size_t previousRemainingVertices =
7430 remainingFace.vertex_indices.size();
7431
7432 while (remainingFace.vertex_indices.size() > 3 &&
7433 remainingIterations > 0) {
7434 // std::cout << "remainingIterations " << remainingIterations <<
7435 // "\n";
7436
7437 npolys = remainingFace.vertex_indices.size();
7438 if (guess_vert >= npolys) {
7439 guess_vert -= npolys;
7440 }
7441
7442 if (previousRemainingVertices != npolys) {
7443 // The number of remaining vertices decreased. Reset counters.
7444 previousRemainingVertices = npolys;
7445 remainingIterations = npolys;
7446 } else {
7447 // We didn't consume a vertex on previous iteration, reduce the
7448 // available iterations.
7449 remainingIterations--;
7450 }
7451
7452 for (size_t k = 0; k < 3; k++) {
7453 ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys];
7454 size_t vi = size_t(ind[k].v_idx);
7455 if (((vi * 3 + axes[0]) >= v.size()) ||
7456 ((vi * 3 + axes[1]) >= v.size())) {
7457 // ???
7458 vx[k] = static_cast<real_t>(0.0);
7459 vy[k] = static_cast<real_t>(0.0);
7460 } else {
7461 vx[k] = v[vi * 3 + axes[0]];
7462 vy[k] = v[vi * 3 + axes[1]];
7463 }
7464 }
7465
7466 //
7467 // area is calculated per face
7468 //
7469 real_t e0x = vx[1] - vx[0];
7470 real_t e0y = vy[1] - vy[0];
7471 real_t e1x = vx[2] - vx[1];
7472 real_t e1y = vy[2] - vy[1];
7473 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 real_t area =
7479 (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 if (cross * area < static_cast<real_t>(0.0)) {
7483 // std::cout << "internal \n";
7484 guess_vert += 1;
7485 // std::cout << "guess vert : " << guess_vert << "\n";
7486 continue;
7487 }
7488
7489 // check all other verts in case they are inside this triangle
7490 bool overlap = false;
7491 for (size_t otherVert = 3; otherVert < npolys; ++otherVert) {
7492 size_t idx = (guess_vert + otherVert) % npolys;
7493
7494 if (idx >= remainingFace.vertex_indices.size()) {
7495 // std::cout << "???0\n";
7496 // ???
7497 continue;
7498 }
7499
7500 size_t ovi = size_t(remainingFace.vertex_indices[idx].v_idx);
7501
7502 if (((ovi * 3 + axes[0]) >= v.size()) ||
7503 ((ovi * 3 + axes[1]) >= v.size())) {
7504 // std::cout << "???1\n";
7505 // ???
7506 continue;
7507 }
7508 real_t tx = v[ovi * 3 + axes[0]];
7509 real_t ty = v[ovi * 3 + axes[1]];
7510 if (pnpoly(3, vx, vy, tx, ty)) {
7511 // std::cout << "overlap\n";
7512 overlap = true;
7513 break;
7514 }
7515 }
7516
7517 if (overlap) {
7518 // std::cout << "overlap2\n";
7519 guess_vert += 1;
7520 continue;
7521 }
7522
7523 // this triangle is an ear
7524 {
7525 index_t idx0, idx1, idx2;
7526 idx0.vertex_index = ind[0].v_idx;
7527 idx0.normal_index = ind[0].vn_idx;
7528 idx0.texcoord_index = ind[0].vt_idx;
7529 idx1.vertex_index = ind[1].v_idx;
7530 idx1.normal_index = ind[1].vn_idx;
7531 idx1.texcoord_index = ind[1].vt_idx;
7532 idx2.vertex_index = ind[2].v_idx;
7533 idx2.normal_index = ind[2].vn_idx;
7534 idx2.texcoord_index = ind[2].vt_idx;
7535
7536 shape->mesh.indices.push_back(idx0);
7537 shape->mesh.indices.push_back(idx1);
7538 shape->mesh.indices.push_back(idx2);
7539
7540 shape->mesh.num_face_vertices.push_back(3);
7541 shape->mesh.material_ids.push_back(material_id);
7542 shape->mesh.smoothing_group_ids.push_back(
7543 face.smoothing_group_id);
7544 }
7545
7546 // remove v1 from the list
7547 size_t removed_vert_index = (guess_vert + 1) % npolys;
7548 while (removed_vert_index + 1 < npolys) {
7549 remainingFace.vertex_indices[removed_vert_index] =
7550 remainingFace.vertex_indices[removed_vert_index + 1];
7551 removed_vert_index += 1;
7552 }
7553 remainingFace.vertex_indices.pop_back();
7554 }
7555
7556 // std::cout << "remainingFace.vi.size = " <<
7557 // remainingFace.vertex_indices.size() << "\n";
7558 if (remainingFace.vertex_indices.size() == 3) {
7559 i0 = remainingFace.vertex_indices[0];
7560 i1 = remainingFace.vertex_indices[1];
7561 i2 = remainingFace.vertex_indices[2];
7562 {
7563 index_t idx0, idx1, idx2;
7564 idx0.vertex_index = i0.v_idx;
7565 idx0.normal_index = i0.vn_idx;
7566 idx0.texcoord_index = i0.vt_idx;
7567 idx1.vertex_index = i1.v_idx;
7568 idx1.normal_index = i1.vn_idx;
7569 idx1.texcoord_index = i1.vt_idx;
7570 idx2.vertex_index = i2.v_idx;
7571 idx2.normal_index = i2.vn_idx;
7572 idx2.texcoord_index = i2.vt_idx;
7573
7574 shape->mesh.indices.push_back(idx0);
7575 shape->mesh.indices.push_back(idx1);
7576 shape->mesh.indices.push_back(idx2);
7577
7578 shape->mesh.num_face_vertices.push_back(3);
7579 shape->mesh.material_ids.push_back(material_id);
7580 shape->mesh.smoothing_group_ids.push_back(
7581 face.smoothing_group_id);
7582 }
7583 }
7584#endif
7585 } // npolys
7586 } else {
7587 for (size_t k = 0; k < npolys; k++) {
7588 index_t idx;
7589 idx.vertex_index = face.vertex_indices[k].v_idx;
7590 idx.normal_index = face.vertex_indices[k].vn_idx;
7591 idx.texcoord_index = face.vertex_indices[k].vt_idx;
7592 shape->mesh.indices.push_back(idx);
7593 }
7594
7595 shape->mesh.num_face_vertices.push_back(
7596 static_cast<unsigned int>(npolys));
7597 shape->mesh.material_ids.push_back(material_id); // per face
7598 shape->mesh.smoothing_group_ids.push_back(
7599 face.smoothing_group_id); // per face
7600 }
7601 }
7602
7603 shape->mesh.tags = tags;
7604 }
7605
7606 // line
7607 if (!prim_group.lineGroup.empty()) {
7608 // Flatten indices
7609 for (size_t i = 0; i < prim_group.lineGroup.size(); i++) {
7610 for (size_t j = 0; j < prim_group.lineGroup[i].vertex_indices.size();
7611 j++) {
7612 const vertex_index_t &vi = prim_group.lineGroup[i].vertex_indices[j];
7613
7614 index_t idx;
7615 idx.vertex_index = vi.v_idx;
7616 idx.normal_index = vi.vn_idx;
7617 idx.texcoord_index = vi.vt_idx;
7618
7619 shape->lines.indices.push_back(idx);
7620 }
7621
7622 shape->lines.num_line_vertices.push_back(
7623 int(prim_group.lineGroup[i].vertex_indices.size()));
7624 }
7625 }
7626
7627 // points
7628 if (!prim_group.pointsGroup.empty()) {
7629 // Flatten & convert indices
7630 for (size_t i = 0; i < prim_group.pointsGroup.size(); i++) {
7631 for (size_t j = 0; j < prim_group.pointsGroup[i].vertex_indices.size();
7632 j++) {
7633 const vertex_index_t &vi = prim_group.pointsGroup[i].vertex_indices[j];
7634
7635 index_t idx;
7636 idx.vertex_index = vi.v_idx;
7637 idx.normal_index = vi.vn_idx;
7638 idx.texcoord_index = vi.vt_idx;
7639
7640 shape->points.indices.push_back(idx);
7641 }
7642 }
7643 }
7644
7645 return true;
7646}
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
7650static void SplitString(const std::string &s, char delim, char escape,
7651 std::vector<std::string> &elems) {
7652 std::string token;
7653
7654 bool escaping = false;
7655 for (size_t i = 0; i < s.size(); ++i) {
7656 char ch = s[i];
7657 if (escaping) {
7658 escaping = false;
7659 } else if (ch == escape) {
7660 if ((i + 1) < s.size()) {
7661 const char next = s[i + 1];
7662 if ((next == delim) || (next == escape)) {
7663 escaping = true;
7664 continue;
7665 }
7666 }
7667 } else if (ch == delim) {
7668 if (!token.empty()) {
7669 elems.push_back(token);
7670 }
7671 token.clear();
7672 continue;
7673 }
7674 token += ch;
7675 }
7676
7677 elems.push_back(token);
7678}
7679
7680static void RemoveEmptyTokens(std::vector<std::string> *tokens) {
7681 if (!tokens) return;
7682
7683 const std::vector<std::string> &src = *tokens;
7684 std::vector<std::string> filtered;
7685 filtered.reserve(src.size());
7686 for (size_t i = 0; i < src.size(); i++) {
7687 if (!src[i].empty()) {
7688 filtered.push_back(src[i]);
7689 }
7690 }
7691 tokens->swap(filtered);
7692}
7693
7694static std::string JoinPath(const std::string &dir,
7695 const std::string &filename) {
7696 if (dir.empty()) {
7697 return filename;
7698 } else {
7699 // check '/'
7700 char lastChar = *dir.rbegin();
7701 if (lastChar != '/') {
7702 return dir + std::string("/") + filename;
7703 } else {
7704 return dir + filename;
7705 }
7706 }
7707}
7708
7709static 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 const std::string &filename = "<stream>") {
7714 if (sr.has_errors()) {
7715 if (err) {
7716 (*err) += sr.get_errors();
7717 }
7718 return false;
7719 }
7720
7721 material_t material;
7722 InitMaterial(&material);
7723
7724 // Issue 43. `d` wins against `Tr` since `Tr` is not in the MTL specification.
7725 bool has_d = false;
7726 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 bool has_kd = false;
7731
7732 std::stringstream warn_ss;
7733
7734 // Handle BOM
7735 if (sr.remaining() >= 3 &&
7736 static_cast<unsigned char>(sr.peek()) == 0xEF &&
7737 static_cast<unsigned char>(sr.peek_at(1)) == 0xBB &&
7738 static_cast<unsigned char>(sr.peek_at(2)) == 0xBF) {
7739 sr.advance(3);
7740 }
7741
7742 while (!sr.eof()) {
7743 sr.skip_space();
7744 if (sr.at_line_end()) { sr.skip_line(); continue; }
7745 if (sr.peek() == '#') { sr.skip_line(); continue; }
7746
7747 size_t line_num = sr.line_num();
7748
7749 // new mtl
7750 if (sr.match("newmtl", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
7751 // flush previous material.
7752 if (!material.name.empty()) {
7753 material_map->insert(std::pair<std::string, int>(
7754 material.name, static_cast<int>(materials->size())));
7755 materials->push_back(material);
7756 }
7757
7758 InitMaterial(&material);
7759
7760 has_d = false;
7761 has_tr = false;
7762 has_kd = false;
7763
7764 sr.advance(7);
7765 {
7766 std::string namebuf = sr_parseString(sr);
7767 if (namebuf.empty()) {
7768 if (warning) {
7769 (*warning) += "empty material name in `newmtl`\n";
7770 }
7771 }
7772 material.name = namebuf;
7773 }
7774 sr.skip_line();
7775 continue;
7776 }
7777
7778 // ambient
7779 if (sr.peek() == 'K' && sr.peek_at(1) == 'a' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
7780 sr.advance(2);
7781 real_t r, g, b;
7782 if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false;
7783 material.ambient[0] = r;
7784 material.ambient[1] = g;
7785 material.ambient[2] = b;
7786 sr.skip_line();
7787 continue;
7788 }
7789
7790 // diffuse
7791 if (sr.peek() == 'K' && sr.peek_at(1) == 'd' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
7792 sr.advance(2);
7793 real_t r, g, b;
7794 if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false;
7795 material.diffuse[0] = r;
7796 material.diffuse[1] = g;
7797 material.diffuse[2] = b;
7798 has_kd = true;
7799 sr.skip_line();
7800 continue;
7801 }
7802
7803 // specular
7804 if (sr.peek() == 'K' && sr.peek_at(1) == 's' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
7805 sr.advance(2);
7806 real_t r, g, b;
7807 if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false;
7808 material.specular[0] = r;
7809 material.specular[1] = g;
7810 material.specular[2] = b;
7811 sr.skip_line();
7812 continue;
7813 }
7814
7815 // transmittance
7816 if ((sr.peek() == 'K' && sr.peek_at(1) == 't' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) ||
7817 (sr.peek() == 'T' && sr.peek_at(1) == 'f' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t'))) {
7818 sr.advance(2);
7819 real_t r, g, b;
7820 if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false;
7821 material.transmittance[0] = r;
7822 material.transmittance[1] = g;
7823 material.transmittance[2] = b;
7824 sr.skip_line();
7825 continue;
7826 }
7827
7828 // ior(index of refraction)
7829 if (sr.peek() == 'N' && sr.peek_at(1) == 'i' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
7830 sr.advance(2);
7831 if (!sr_parseReal(sr, &material.ior, 0.0, err, filename)) return false;
7832 sr.skip_line();
7833 continue;
7834 }
7835
7836 // emission
7837 if (sr.peek() == 'K' && sr.peek_at(1) == 'e' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
7838 sr.advance(2);
7839 real_t r, g, b;
7840 if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false;
7841 material.emission[0] = r;
7842 material.emission[1] = g;
7843 material.emission[2] = b;
7844 sr.skip_line();
7845 continue;
7846 }
7847
7848 // shininess
7849 if (sr.peek() == 'N' && sr.peek_at(1) == 's' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
7850 sr.advance(2);
7851 if (!sr_parseReal(sr, &material.shininess, 0.0, err, filename)) return false;
7852 sr.skip_line();
7853 continue;
7854 }
7855
7856 // illum model
7857 if (sr.match("illum", 5) && (sr.peek_at(5) == ' ' || sr.peek_at(5) == '\t')) {
7858 sr.advance(6);
7859 if (!sr_parseInt(sr, &material.illum, err, filename)) return false;
7860 sr.skip_line();
7861 continue;
7862 }
7863
7864 // dissolve
7865 if (sr.peek() == 'd' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
7866 sr.advance(1);
7867 if (!sr_parseReal(sr, &material.dissolve, 0.0, err, filename)) return false;
7868
7869 if (has_tr) {
7870 warn_ss << "Both `d` and `Tr` parameters defined for \""
7871 << material.name
7872 << "\". Use the value of `d` for dissolve (line " << line_num
7873 << " in .mtl.)\n";
7874 }
7875 has_d = true;
7876 sr.skip_line();
7877 continue;
7878 }
7879 if (sr.peek() == 'T' && sr.peek_at(1) == 'r' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
7880 sr.advance(2);
7881 if (has_d) {
7882 warn_ss << "Both `d` and `Tr` parameters defined for \""
7883 << material.name
7884 << "\". Use the value of `d` for dissolve (line " << line_num
7885 << " in .mtl.)\n";
7886 } else {
7887 real_t tr_val;
7888 if (!sr_parseReal(sr, &tr_val, 0.0, err, filename)) return false;
7889 material.dissolve = static_cast<real_t>(1.0) - tr_val;
7890 }
7891 has_tr = true;
7892 sr.skip_line();
7893 continue;
7894 }
7895
7896 // PBR: roughness
7897 if (sr.peek() == 'P' && sr.peek_at(1) == 'r' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
7898 sr.advance(2);
7899 if (!sr_parseReal(sr, &material.roughness, 0.0, err, filename)) return false;
7900 sr.skip_line();
7901 continue;
7902 }
7903
7904 // PBR: metallic
7905 if (sr.peek() == 'P' && sr.peek_at(1) == 'm' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
7906 sr.advance(2);
7907 if (!sr_parseReal(sr, &material.metallic, 0.0, err, filename)) return false;
7908 sr.skip_line();
7909 continue;
7910 }
7911
7912 // PBR: sheen
7913 if (sr.peek() == 'P' && sr.peek_at(1) == 's' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
7914 sr.advance(2);
7915 if (!sr_parseReal(sr, &material.sheen, 0.0, err, filename)) return false;
7916 sr.skip_line();
7917 continue;
7918 }
7919
7920 // PBR: clearcoat thickness
7921 if (sr.peek() == 'P' && sr.peek_at(1) == 'c' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
7922 sr.advance(2);
7923 if (!sr_parseReal(sr, &material.clearcoat_thickness, 0.0, err, filename)) return false;
7924 sr.skip_line();
7925 continue;
7926 }
7927
7928 // PBR: clearcoat roughness
7929 if (sr.match("Pcr", 3) && (sr.peek_at(3) == ' ' || sr.peek_at(3) == '\t')) {
7930 sr.advance(4);
7931 if (!sr_parseReal(sr, &material.clearcoat_roughness, 0.0, err, filename)) return false;
7932 sr.skip_line();
7933 continue;
7934 }
7935
7936 // PBR: anisotropy
7937 if (sr.match("aniso", 5) && (sr.peek_at(5) == ' ' || sr.peek_at(5) == '\t')) {
7938 sr.advance(6);
7939 if (!sr_parseReal(sr, &material.anisotropy, 0.0, err, filename)) return false;
7940 sr.skip_line();
7941 continue;
7942 }
7943
7944 // PBR: anisotropy rotation
7945 if (sr.match("anisor", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
7946 sr.advance(7);
7947 if (!sr_parseReal(sr, &material.anisotropy_rotation, 0.0, err, filename)) return false;
7948 sr.skip_line();
7949 continue;
7950 }
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 if (sr.match("map_Ka", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
7957 sr.advance(7);
7958 std::string line_rest = trimTrailingWhitespace(sr.read_line());
7959 ParseTextureNameAndOption(&(material.ambient_texname),
7960 &(material.ambient_texopt), line_rest.c_str());
7961 sr.skip_line();
7962 continue;
7963 }
7964
7965 // diffuse texture
7966 if (sr.match("map_Kd", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
7967 sr.advance(7);
7968 std::string line_rest = trimTrailingWhitespace(sr.read_line());
7969 ParseTextureNameAndOption(&(material.diffuse_texname),
7970 &(material.diffuse_texopt), line_rest.c_str());
7971 if (!has_kd) {
7972 material.diffuse[0] = static_cast<real_t>(0.6);
7973 material.diffuse[1] = static_cast<real_t>(0.6);
7974 material.diffuse[2] = static_cast<real_t>(0.6);
7975 }
7976 sr.skip_line();
7977 continue;
7978 }
7979
7980 // specular texture
7981 if (sr.match("map_Ks", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
7982 sr.advance(7);
7983 std::string line_rest = trimTrailingWhitespace(sr.read_line());
7984 ParseTextureNameAndOption(&(material.specular_texname),
7985 &(material.specular_texopt), line_rest.c_str());
7986 sr.skip_line();
7987 continue;
7988 }
7989
7990 // specular highlight texture
7991 if (sr.match("map_Ns", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
7992 sr.advance(7);
7993 std::string line_rest = trimTrailingWhitespace(sr.read_line());
7994 ParseTextureNameAndOption(&(material.specular_highlight_texname),
7995 &(material.specular_highlight_texopt), line_rest.c_str());
7996 sr.skip_line();
7997 continue;
7998 }
7999
8000 // bump texture
8001 if ((sr.match("map_bump", 8) || sr.match("map_Bump", 8)) &&
8002 (sr.peek_at(8) == ' ' || sr.peek_at(8) == '\t')) {
8003 sr.advance(9);
8004 std::string line_rest = trimTrailingWhitespace(sr.read_line());
8005 ParseTextureNameAndOption(&(material.bump_texname),
8006 &(material.bump_texopt), line_rest.c_str());
8007 sr.skip_line();
8008 continue;
8009 }
8010
8011 // bump texture (short form)
8012 if (sr.match("bump", 4) && (sr.peek_at(4) == ' ' || sr.peek_at(4) == '\t')) {
8013 sr.advance(5);
8014 std::string line_rest = trimTrailingWhitespace(sr.read_line());
8015 ParseTextureNameAndOption(&(material.bump_texname),
8016 &(material.bump_texopt), line_rest.c_str());
8017 sr.skip_line();
8018 continue;
8019 }
8020
8021 // alpha texture
8022 if (sr.match("map_d", 5) && (sr.peek_at(5) == ' ' || sr.peek_at(5) == '\t')) {
8023 sr.advance(6);
8024 std::string line_rest = trimTrailingWhitespace(sr.read_line());
8025 ParseTextureNameAndOption(&(material.alpha_texname),
8026 &(material.alpha_texopt), line_rest.c_str());
8027 sr.skip_line();
8028 continue;
8029 }
8030
8031 // displacement texture
8032 if ((sr.match("map_disp", 8) || sr.match("map_Disp", 8)) &&
8033 (sr.peek_at(8) == ' ' || sr.peek_at(8) == '\t')) {
8034 sr.advance(9);
8035 std::string line_rest = trimTrailingWhitespace(sr.read_line());
8036 ParseTextureNameAndOption(&(material.displacement_texname),
8037 &(material.displacement_texopt), line_rest.c_str());
8038 sr.skip_line();
8039 continue;
8040 }
8041
8042 // displacement texture (short form)
8043 if (sr.match("disp", 4) && (sr.peek_at(4) == ' ' || sr.peek_at(4) == '\t')) {
8044 sr.advance(5);
8045 std::string line_rest = trimTrailingWhitespace(sr.read_line());
8046 ParseTextureNameAndOption(&(material.displacement_texname),
8047 &(material.displacement_texopt), line_rest.c_str());
8048 sr.skip_line();
8049 continue;
8050 }
8051
8052 // reflection map
8053 if (sr.match("refl", 4) && (sr.peek_at(4) == ' ' || sr.peek_at(4) == '\t')) {
8054 sr.advance(5);
8055 std::string line_rest = trimTrailingWhitespace(sr.read_line());
8056 ParseTextureNameAndOption(&(material.reflection_texname),
8057 &(material.reflection_texopt), line_rest.c_str());
8058 sr.skip_line();
8059 continue;
8060 }
8061
8062 // PBR: roughness texture
8063 if (sr.match("map_Pr", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8064 sr.advance(7);
8065 std::string line_rest = trimTrailingWhitespace(sr.read_line());
8066 ParseTextureNameAndOption(&(material.roughness_texname),
8067 &(material.roughness_texopt), line_rest.c_str());
8068 sr.skip_line();
8069 continue;
8070 }
8071
8072 // PBR: metallic texture
8073 if (sr.match("map_Pm", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8074 sr.advance(7);
8075 std::string line_rest = trimTrailingWhitespace(sr.read_line());
8076 ParseTextureNameAndOption(&(material.metallic_texname),
8077 &(material.metallic_texopt), line_rest.c_str());
8078 sr.skip_line();
8079 continue;
8080 }
8081
8082 // PBR: sheen texture
8083 if (sr.match("map_Ps", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8084 sr.advance(7);
8085 std::string line_rest = trimTrailingWhitespace(sr.read_line());
8086 ParseTextureNameAndOption(&(material.sheen_texname),
8087 &(material.sheen_texopt), line_rest.c_str());
8088 sr.skip_line();
8089 continue;
8090 }
8091
8092 // PBR: emissive texture
8093 if (sr.match("map_Ke", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8094 sr.advance(7);
8095 std::string line_rest = trimTrailingWhitespace(sr.read_line());
8096 ParseTextureNameAndOption(&(material.emissive_texname),
8097 &(material.emissive_texopt), line_rest.c_str());
8098 sr.skip_line();
8099 continue;
8100 }
8101
8102 // PBR: normal map texture
8103 if (sr.match("norm", 4) && (sr.peek_at(4) == ' ' || sr.peek_at(4) == '\t')) {
8104 sr.advance(5);
8105 std::string line_rest = trimTrailingWhitespace(sr.read_line());
8106 ParseTextureNameAndOption(&(material.normal_texname),
8107 &(material.normal_texopt), line_rest.c_str());
8108 sr.skip_line();
8109 continue;
8110 }
8111
8112 // unknown parameter
8113 {
8114 std::string line_rest = trimTrailingWhitespace(sr.read_line());
8115 const char *_lp = line_rest.c_str();
8116 const char *_space = strchr(_lp, ' ');
8117 if (!_space) {
8118 _space = strchr(_lp, '\t');
8119 }
8120 if (_space) {
8121 std::ptrdiff_t len = _space - _lp;
8122 std::string key(_lp, static_cast<size_t>(len));
8123 std::string value = _space + 1;
8124 material.unknown_parameter.insert(
8125 std::pair<std::string, std::string>(key, value));
8126 }
8127 }
8128 sr.skip_line();
8129 }
8130 // flush last material (only if it was actually defined).
8131 if (!material.name.empty()) {
8132 material_map->insert(std::pair<std::string, int>(
8133 material.name, static_cast<int>(materials->size())));
8134 materials->push_back(material);
8135 }
8136
8137 if (warning) {
8138 (*warning) += warn_ss.str();
8139 }
8140
8141 return true;
8142}
8143
8144void LoadMtl(std::map<std::string, int> *material_map,
8145 std::vector<material_t> *materials, std::istream *inStream,
8146 std::string *warning, std::string *err) {
8147 StreamReader sr(*inStream);
8148 LoadMtlInternal(material_map, materials, sr, warning, err);
8149}
8150
8151
8152bool MaterialFileReader::operator()(const std::string &matId,
8153 std::vector<material_t> *materials,
8154 std::map<std::string, int> *matMap,
8155 std::string *warn, std::string *err) {
8156 if (!m_mtlBaseDir.empty()) {
8157#ifdef _WIN32
8158 char sep = ';';
8159#else
8160 char sep = ':';
8161#endif
8162
8163 // https://stackoverflow.com/questions/5167625/splitting-a-c-stdstring-using-tokens-e-g
8164 std::vector<std::string> paths;
8165 std::istringstream f(m_mtlBaseDir);
8166
8167 std::string s;
8168 while (getline(f, s, sep)) {
8169 paths.push_back(s);
8170 }
8171
8172 for (size_t i = 0; i < paths.size(); i++) {
8173 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 std::ifstream matIStream(filepath.c_str());
8197#endif
8198 if (matIStream) {
8199 StreamReader mtl_sr(matIStream);
8200 return LoadMtlInternal(matMap, materials, mtl_sr, warn, err, filepath);
8201 }
8202#endif // TINYOBJLOADER_USE_MMAP
8203 }
8204
8205 std::stringstream ss;
8206 ss << "Material file [ " << matId
8207 << " ] not found in a path : " << m_mtlBaseDir << "\n";
8208 if (warn) {
8209 (*warn) += ss.str();
8210 }
8211 return false;
8212
8213 } else {
8214 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 std::ifstream matIStream(filepath.c_str());
8239#endif
8240 if (matIStream) {
8241 StreamReader mtl_sr(matIStream);
8242 return LoadMtlInternal(matMap, materials, mtl_sr, warn, err, filepath);
8243 }
8244#endif // TINYOBJLOADER_USE_MMAP
8245
8246 std::stringstream ss;
8247 ss << "Material file [ " << filepath
8248 << " ] not found in a path : " << m_mtlBaseDir << "\n";
8249 if (warn) {
8250 (*warn) += ss.str();
8251 }
8252
8253 return false;
8254 }
8255}
8256
8257bool MaterialStreamReader::operator()(const std::string &matId,
8258 std::vector<material_t> *materials,
8259 std::map<std::string, int> *matMap,
8260 std::string *warn, std::string *err) {
8261 (void)matId;
8262 if (!m_inStream) {
8263 std::stringstream ss;
8264 ss << "Material stream in error state. \n";
8265 if (warn) {
8266 (*warn) += ss.str();
8267 }
8268 return false;
8269 }
8270
8271 StreamReader mtl_sr(m_inStream);
8272 return LoadMtlInternal(matMap, materials, mtl_sr, warn, err, "<stream>");
8273}
8274
8275static 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 const std::string &filename = "<stream>") {
8282 if (sr.has_errors()) {
8283 if (err) {
8284 (*err) += sr.get_errors();
8285 }
8286 return false;
8287 }
8288
8289 std::vector<real_t> v;
8290 std::vector<real_t> vertex_weights;
8291 std::vector<real_t> vn;
8292 std::vector<real_t> vt;
8293 std::vector<real_t> vt_w; // optional [w] component in `vt`
8294 std::vector<real_t> vc;
8295 std::vector<skin_weight_t> vw;
8296 std::vector<tag_t> tags;
8297 PrimGroup prim_group;
8298 std::string name;
8299
8300 // material
8301 std::set<std::string> material_filenames;
8302 std::map<std::string, int> material_map;
8303 int material = -1;
8304
8305 unsigned int current_smoothing_id = 0;
8306
8307 int greatest_v_idx = -1;
8308 int greatest_vn_idx = -1;
8309 int greatest_vt_idx = -1;
8310
8311 shape_t shape;
8312
8313 bool found_all_colors = true;
8314
8315 // Handle BOM
8316 if (sr.remaining() >= 3 &&
8317 static_cast<unsigned char>(sr.peek()) == 0xEF &&
8318 static_cast<unsigned char>(sr.peek_at(1)) == 0xBB &&
8319 static_cast<unsigned char>(sr.peek_at(2)) == 0xBF) {
8320 sr.advance(3);
8321 }
8322
8323 warning_context context;
8324 context.warn = warn;
8325 context.filename = filename;
8326
8327 while (!sr.eof()) {
8328 sr.skip_space();
8329 if (sr.at_line_end()) { sr.skip_line(); continue; }
8330 if (sr.peek() == '#') { sr.skip_line(); continue; }
8331
8332 size_t line_num = sr.line_num();
8333
8334 // vertex
8335 if (sr.peek() == 'v' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
8336 sr.advance(2);
8337 real_t x, y, z;
8338 real_t r, g, b;
8339
8340 int num_components = sr_parseVertexWithColor(&x, &y, &z, &r, &g, &b, sr, err, filename);
8341 if (num_components < 0) return false;
8342 found_all_colors &= (num_components == 6);
8343
8344 v.push_back(x);
8345 v.push_back(y);
8346 v.push_back(z);
8347
8348 vertex_weights.push_back(r);
8349
8350 if ((num_components == 6) || default_vcols_fallback) {
8351 vc.push_back(r);
8352 vc.push_back(g);
8353 vc.push_back(b);
8354 }
8355
8356 sr.skip_line();
8357 continue;
8358 }
8359
8360 // normal
8361 if (sr.peek() == 'v' && sr.peek_at(1) == 'n' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8362 sr.advance(3);
8363 real_t x, y, z;
8364 if (!sr_parseReal3(&x, &y, &z, sr, err, filename)) return false;
8365 vn.push_back(x);
8366 vn.push_back(y);
8367 vn.push_back(z);
8368 sr.skip_line();
8369 continue;
8370 }
8371
8372 // texcoord
8373 if (sr.peek() == 'v' && sr.peek_at(1) == 't' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8374 sr.advance(3);
8375 real_t x, y;
8376 if (!sr_parseReal2(&x, &y, sr, err, filename)) return false;
8377 vt.push_back(x);
8378 vt.push_back(y);
8379
8380 // Parse optional w component
8381 real_t w = static_cast<real_t>(0.0);
8382 sr_parseReal(sr, &w);
8383 vt_w.push_back(w);
8384
8385 sr.skip_line();
8386 continue;
8387 }
8388
8389 // skin weight. tinyobj extension
8390 if (sr.peek() == 'v' && sr.peek_at(1) == 'w' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8391 sr.advance(3);
8392
8393 int vid;
8394 if (!sr_parseInt(sr, &vid, err, filename)) return false;
8395
8396 skin_weight_t sw;
8397 sw.vertex_id = vid;
8398
8399 size_t vw_loop_max = sr.remaining() + 1;
8400 size_t vw_loop_iter = 0;
8401 while (!sr.at_line_end() && sr.peek() != '#' &&
8402 vw_loop_iter < vw_loop_max) {
8403 real_t j, w;
8404 sr_parseReal2(&j, &w, sr, -1.0);
8405
8406 if (j < static_cast<real_t>(0)) {
8407 if (err) {
8408 (*err) += sr.format_error(filename,
8409 "failed to parse `vw' line: joint_id is negative");
8410 }
8411 return false;
8412 }
8413
8414 joint_and_weight_t jw;
8415 jw.joint_id = int(j);
8416 jw.weight = w;
8417
8418 sw.weightValues.push_back(jw);
8419 sr.skip_space_and_cr();
8420 vw_loop_iter++;
8421 }
8422
8423 vw.push_back(sw);
8424 sr.skip_line();
8425 continue;
8426 }
8427
8428 context.line_number = line_num;
8429
8430 // line
8431 if (sr.peek() == 'l' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
8432 sr.advance(2);
8433
8434 __line_t line;
8435
8436 size_t l_loop_max = sr.remaining() + 1;
8437 size_t l_loop_iter = 0;
8438 while (!sr.at_line_end() && sr.peek() != '#' &&
8439 l_loop_iter < l_loop_max) {
8440 vertex_index_t vi;
8441 if (!sr_parseTriple(sr, size_to_int(v.size() / 3),
8442 size_to_int(vn.size() / 3),
8443 size_to_int(vt.size() / 2), &vi, context)) {
8444 if (err) {
8445 (*err) += sr.format_error(filename,
8446 "failed to parse `l' line (invalid vertex index)");
8447 }
8448 return false;
8449 }
8450
8451 line.vertex_indices.push_back(vi);
8452 sr.skip_space_and_cr();
8453 l_loop_iter++;
8454 }
8455
8456 prim_group.lineGroup.push_back(line);
8457 sr.skip_line();
8458 continue;
8459 }
8460
8461 // points
8462 if (sr.peek() == 'p' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
8463 sr.advance(2);
8464
8465 __points_t pts;
8466
8467 size_t p_loop_max = sr.remaining() + 1;
8468 size_t p_loop_iter = 0;
8469 while (!sr.at_line_end() && sr.peek() != '#' &&
8470 p_loop_iter < p_loop_max) {
8471 vertex_index_t vi;
8472 if (!sr_parseTriple(sr, size_to_int(v.size() / 3),
8473 size_to_int(vn.size() / 3),
8474 size_to_int(vt.size() / 2), &vi, context)) {
8475 if (err) {
8476 (*err) += sr.format_error(filename,
8477 "failed to parse `p' line (invalid vertex index)");
8478 }
8479 return false;
8480 }
8481
8482 pts.vertex_indices.push_back(vi);
8483 sr.skip_space_and_cr();
8484 p_loop_iter++;
8485 }
8486
8487 prim_group.pointsGroup.push_back(pts);
8488 sr.skip_line();
8489 continue;
8490 }
8491
8492 // face
8493 if (sr.peek() == 'f' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
8494 sr.advance(2);
8495 sr.skip_space();
8496
8497 face_t face;
8498
8499 face.smoothing_group_id = current_smoothing_id;
8500 face.vertex_indices.reserve(3);
8501
8502 size_t f_loop_max = sr.remaining() + 1;
8503 size_t f_loop_iter = 0;
8504 while (!sr.at_line_end() && sr.peek() != '#' &&
8505 f_loop_iter < f_loop_max) {
8506 vertex_index_t vi;
8507 if (!sr_parseTriple(sr, size_to_int(v.size() / 3),
8508 size_to_int(vn.size() / 3),
8509 size_to_int(vt.size() / 2), &vi, context)) {
8510 if (err) {
8511 (*err) += sr.format_error(filename,
8512 "failed to parse `f' line (invalid vertex index)");
8513 }
8514 return false;
8515 }
8516
8517 greatest_v_idx = greatest_v_idx > vi.v_idx ? greatest_v_idx : vi.v_idx;
8518 greatest_vn_idx =
8519 greatest_vn_idx > vi.vn_idx ? greatest_vn_idx : vi.vn_idx;
8520 greatest_vt_idx =
8521 greatest_vt_idx > vi.vt_idx ? greatest_vt_idx : vi.vt_idx;
8522
8523 face.vertex_indices.push_back(vi);
8524 sr.skip_space_and_cr();
8525 f_loop_iter++;
8526 }
8527
8528 prim_group.faceGroup.push_back(face);
8529 sr.skip_line();
8530 continue;
8531 }
8532
8533 // use mtl
8534 if (sr.match("usemtl", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8535 sr.advance(6);
8536 std::string namebuf = sr_parseString(sr);
8537
8538 int newMaterialId = -1;
8539 std::map<std::string, int>::const_iterator it =
8540 material_map.find(namebuf);
8541 if (it != material_map.end()) {
8542 newMaterialId = it->second;
8543 } else {
8544 if (warn) {
8545 (*warn) += "material [ '" + namebuf + "' ] not found in .mtl\n";
8546 }
8547 }
8548
8549 if (newMaterialId != material) {
8550 exportGroupsToShape(&shape, prim_group, tags, material, name,
8551 triangulate, v, warn);
8552 prim_group.faceGroup.clear();
8553 material = newMaterialId;
8554 }
8555
8556 sr.skip_line();
8557 continue;
8558 }
8559
8560 // load mtl
8561 if (sr.match("mtllib", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8562 if (readMatFn) {
8563 sr.advance(7);
8564
8565 std::string line_rest = trimTrailingWhitespace(sr.read_line());
8566 std::vector<std::string> filenames;
8567 SplitString(line_rest, ' ', '\\', filenames);
8568 RemoveEmptyTokens(&filenames);
8569
8570 if (filenames.empty()) {
8571 if (warn) {
8572 std::stringstream ss;
8573 ss << "Looks like empty filename for mtllib. Use default "
8574 "material (line "
8575 << line_num << ".)\n";
8576
8577 (*warn) += ss.str();
8578 }
8579 } else {
8580 bool found = false;
8581 for (size_t s = 0; s < filenames.size(); s++) {
8582 if (material_filenames.count(filenames[s]) > 0) {
8583 found = true;
8584 continue;
8585 }
8586
8587 std::string warn_mtl;
8588 std::string err_mtl;
8589 bool ok = (*readMatFn)(filenames[s].c_str(), materials,
8590 &material_map, &warn_mtl, &err_mtl);
8591 if (warn && (!warn_mtl.empty())) {
8592 (*warn) += warn_mtl;
8593 }
8594
8595 if (err && (!err_mtl.empty())) {
8596 (*err) += err_mtl;
8597 }
8598
8599 if (ok) {
8600 found = true;
8601 material_filenames.insert(filenames[s]);
8602 break;
8603 }
8604 }
8605
8606 if (!found) {
8607 if (warn) {
8608 (*warn) +=
8609 "Failed to load material file(s). Use default "
8610 "material.\n";
8611 }
8612 }
8613 }
8614 }
8615
8616 sr.skip_line();
8617 continue;
8618 }
8619
8620 // group name
8621 if (sr.peek() == 'g' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
8622 // flush previous face group.
8623 bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name,
8624 triangulate, v, warn);
8625 (void)ret;
8626
8627 if (shape.mesh.indices.size() > 0) {
8628 shapes->push_back(shape);
8629 }
8630
8631 shape = shape_t();
8632
8633 // material = -1;
8634 prim_group.clear();
8635
8636 std::vector<std::string> names;
8637
8638 size_t g_loop_max = sr.remaining() + 1;
8639 size_t g_loop_iter = 0;
8640 while (!sr.at_line_end() && sr.peek() != '#' &&
8641 g_loop_iter < g_loop_max) {
8642 std::string str = sr_parseString(sr);
8643 names.push_back(str);
8644 sr.skip_space_and_cr();
8645 g_loop_iter++;
8646 }
8647
8648 // names[0] must be 'g'
8649
8650 if (names.size() < 2) {
8651 // 'g' with empty names
8652 if (warn) {
8653 std::stringstream ss;
8654 ss << "Empty group name. line: " << line_num << "\n";
8655 (*warn) += ss.str();
8656 name = "";
8657 }
8658 } else {
8659 std::stringstream ss;
8660 ss << names[1];
8661
8662 for (size_t i = 2; i < names.size(); i++) {
8663 ss << " " << names[i];
8664 }
8665
8666 name = ss.str();
8667 }
8668
8669 sr.skip_line();
8670 continue;
8671 }
8672
8673 // object name
8674 if (sr.peek() == 'o' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
8675 // flush previous face group.
8676 bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name,
8677 triangulate, v, warn);
8678 (void)ret;
8679
8680 if (shape.mesh.indices.size() > 0 || shape.lines.indices.size() > 0 ||
8681 shape.points.indices.size() > 0) {
8682 shapes->push_back(shape);
8683 }
8684
8685 // material = -1;
8686 prim_group.clear();
8687 shape = shape_t();
8688
8689 sr.advance(2);
8690 std::string rest = sr.read_line();
8691 name = rest;
8692
8693 sr.skip_line();
8694 continue;
8695 }
8696
8697 if (sr.peek() == 't' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
8698 const int max_tag_nums = 8192;
8699 tag_t tag;
8700
8701 sr.advance(2);
8702
8703 tag.name = sr_parseString(sr);
8704
8705 tag_sizes ts = sr_parseTagTriple(sr);
8706
8707 if (ts.num_ints < 0) {
8708 ts.num_ints = 0;
8709 }
8710 if (ts.num_ints > max_tag_nums) {
8711 ts.num_ints = max_tag_nums;
8712 }
8713
8714 if (ts.num_reals < 0) {
8715 ts.num_reals = 0;
8716 }
8717 if (ts.num_reals > max_tag_nums) {
8718 ts.num_reals = max_tag_nums;
8719 }
8720
8721 if (ts.num_strings < 0) {
8722 ts.num_strings = 0;
8723 }
8724 if (ts.num_strings > max_tag_nums) {
8725 ts.num_strings = max_tag_nums;
8726 }
8727
8728 tag.intValues.resize(static_cast<size_t>(ts.num_ints));
8729
8730 for (size_t i = 0; i < static_cast<size_t>(ts.num_ints); ++i) {
8731 tag.intValues[i] = sr_parseInt(sr);
8732 }
8733
8734 tag.floatValues.resize(static_cast<size_t>(ts.num_reals));
8735 for (size_t i = 0; i < static_cast<size_t>(ts.num_reals); ++i) {
8736 tag.floatValues[i] = sr_parseReal(sr);
8737 }
8738
8739 tag.stringValues.resize(static_cast<size_t>(ts.num_strings));
8740 for (size_t i = 0; i < static_cast<size_t>(ts.num_strings); ++i) {
8741 tag.stringValues[i] = sr_parseString(sr);
8742 }
8743
8744 tags.push_back(tag);
8745
8746 sr.skip_line();
8747 continue;
8748 }
8749
8750 if (sr.peek() == 's' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
8751 // smoothing group id
8752 sr.advance(2);
8753 sr.skip_space();
8754
8755 if (sr.at_line_end()) {
8756 sr.skip_line();
8757 continue;
8758 }
8759
8760 if (sr.peek() == '\r') {
8761 sr.skip_line();
8762 continue;
8763 }
8764
8765 if (sr.remaining() >= 3 && sr.match("off", 3)) {
8766 current_smoothing_id = 0;
8767 } else {
8768 int smGroupId = sr_parseInt(sr);
8769 if (smGroupId < 0) {
8770 current_smoothing_id = 0;
8771 } else {
8772 current_smoothing_id = static_cast<unsigned int>(smGroupId);
8773 }
8774 }
8775
8776 sr.skip_line();
8777 continue;
8778 }
8779
8780 // Ignore unknown command.
8781 sr.skip_line();
8782 }
8783
8784 // not all vertices have colors, no default colors desired? -> clear colors
8785 if (!found_all_colors && !default_vcols_fallback) {
8786 vc.clear();
8787 }
8788
8789 if (greatest_v_idx >= size_to_int(v.size() / 3)) {
8790 if (warn) {
8791 std::stringstream ss;
8792 ss << "Vertex indices out of bounds (line " << sr.line_num() << ".)\n\n";
8793 (*warn) += ss.str();
8794 }
8795 }
8796 if (greatest_vn_idx >= size_to_int(vn.size() / 3)) {
8797 if (warn) {
8798 std::stringstream ss;
8799 ss << "Vertex normal indices out of bounds (line " << sr.line_num()
8800 << ".)\n\n";
8801 (*warn) += ss.str();
8802 }
8803 }
8804 if (greatest_vt_idx >= size_to_int(vt.size() / 2)) {
8805 if (warn) {
8806 std::stringstream ss;
8807 ss << "Vertex texcoord indices out of bounds (line " << sr.line_num()
8808 << ".)\n\n";
8809 (*warn) += ss.str();
8810 }
8811 }
8812
8813 bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name,
8814 triangulate, v, warn);
8815 if (ret || shape.mesh.indices.size()) {
8816 shapes->push_back(shape);
8817 }
8818 prim_group.clear();
8819
8820 attrib->vertices.swap(v);
8821 attrib->vertex_weights.swap(vertex_weights);
8822 attrib->normals.swap(vn);
8823 attrib->texcoords.swap(vt);
8824 attrib->texcoord_ws.swap(vt_w);
8825 attrib->colors.swap(vc);
8826 attrib->skin_weights.swap(vw);
8827
8828 return true;
8829}
8830
8831bool 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 bool triangulate, bool default_vcols_fallback) {
8835 attrib->vertices.clear();
8836 attrib->vertex_weights.clear();
8837 attrib->normals.clear();
8838 attrib->texcoords.clear();
8839 attrib->texcoord_ws.clear();
8840 attrib->colors.clear();
8841 attrib->skin_weights.clear();
8842 shapes->clear();
8843
8844 std::string baseDir = mtl_basedir ? mtl_basedir : "";
8845 if (!baseDir.empty()) {
8846#ifndef _WIN32
8847 const char dirsep = '/';
8848#else
8849 const char dirsep = '\\';
8850#endif
8851 if (baseDir[baseDir.length() - 1] != dirsep) baseDir += dirsep;
8852 }
8853 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 std::ifstream ifs(filename);
8886#endif
8887 if (!ifs) {
8888 if (err) {
8889 std::stringstream ss;
8890 ss << "Cannot open file [" << filename << "]\n";
8891 (*err) = ss.str();
8892 }
8893 return false;
8894 }
8895 {
8896 StreamReader sr(ifs);
8897 return LoadObjInternal(attrib, shapes, materials, warn, err, sr,
8898 &matFileReader, triangulate, default_vcols_fallback,
8899 filename);
8900 }
8901#endif // TINYOBJLOADER_USE_MMAP
8902}
8903
8904bool 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 bool default_vcols_fallback) {
8909 attrib->vertices.clear();
8910 attrib->vertex_weights.clear();
8911 attrib->normals.clear();
8912 attrib->texcoords.clear();
8913 attrib->texcoord_ws.clear();
8914 attrib->colors.clear();
8915 attrib->skin_weights.clear();
8916 shapes->clear();
8917
8918 StreamReader sr(*inStream);
8919 return LoadObjInternal(attrib, shapes, materials, warn, err, sr,
8920 readMatFn, triangulate, default_vcols_fallback);
8921}
8922
8923
8924static bool LoadObjWithCallbackInternal(StreamReader &sr,
8925 const callback_t &callback,
8926 void *user_data,
8927 MaterialReader *readMatFn,
8928 std::string *warn,
8929 std::string *err) {
8930 if (sr.has_errors()) {
8931 if (err) {
8932 (*err) += sr.get_errors();
8933 }
8934 return false;
8935 }
8936
8937 // material
8938 std::set<std::string> material_filenames;
8939 std::map<std::string, int> material_map;
8940 int material_id = -1;
8941
8942 std::vector<index_t> indices;
8943 std::vector<material_t> materials;
8944 std::vector<std::string> names;
8945 names.reserve(2);
8946 std::vector<const char *> names_out;
8947
8948 // Handle BOM
8949 if (sr.remaining() >= 3 &&
8950 static_cast<unsigned char>(sr.peek()) == 0xEF &&
8951 static_cast<unsigned char>(sr.peek_at(1)) == 0xBB &&
8952 static_cast<unsigned char>(sr.peek_at(2)) == 0xBF) {
8953 sr.advance(3);
8954 }
8955
8956 while (!sr.eof()) {
8957 sr.skip_space();
8958 if (sr.at_line_end()) { sr.skip_line(); continue; }
8959 if (sr.peek() == '#') { sr.skip_line(); continue; }
8960
8961 // vertex
8962 if (sr.peek() == 'v' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
8963 sr.advance(2);
8964 real_t x, y, z;
8965 real_t r, g, b;
8966
8967 int num_components = sr_parseVertexWithColor(&x, &y, &z, &r, &g, &b, sr, err, std::string());
8968 if (num_components < 0) {
8969 return false;
8970 }
8971 if (callback.vertex_cb) {
8972 callback.vertex_cb(user_data, x, y, z, r);
8973 }
8974 if (callback.vertex_color_cb) {
8975 bool found_color = (num_components == 6);
8976 callback.vertex_color_cb(user_data, x, y, z, r, g, b, found_color);
8977 }
8978 sr.skip_line();
8979 continue;
8980 }
8981
8982 // normal
8983 if (sr.peek() == 'v' && sr.peek_at(1) == 'n' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8984 sr.advance(3);
8985 real_t x, y, z;
8986 sr_parseReal3(&x, &y, &z, sr);
8987 if (callback.normal_cb) {
8988 callback.normal_cb(user_data, x, y, z);
8989 }
8990 sr.skip_line();
8991 continue;
8992 }
8993
8994 // texcoord
8995 if (sr.peek() == 'v' && sr.peek_at(1) == 't' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8996 sr.advance(3);
8997 real_t x, y, z;
8998 sr_parseReal3(&x, &y, &z, sr);
8999 if (callback.texcoord_cb) {
9000 callback.texcoord_cb(user_data, x, y, z);
9001 }
9002 sr.skip_line();
9003 continue;
9004 }
9005
9006 // face
9007 if (sr.peek() == 'f' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9008 sr.advance(2);
9009 sr.skip_space();
9010
9011 indices.clear();
9012 size_t cf_loop_max = sr.remaining() + 1;
9013 size_t cf_loop_iter = 0;
9014 while (!sr.at_line_end() && sr.peek() != '#' &&
9015 cf_loop_iter < cf_loop_max) {
9016 vertex_index_t vi = sr_parseRawTriple(sr);
9017
9018 index_t idx;
9019 idx.vertex_index = vi.v_idx;
9020 idx.normal_index = vi.vn_idx;
9021 idx.texcoord_index = vi.vt_idx;
9022
9023 indices.push_back(idx);
9024 sr.skip_space_and_cr();
9025 cf_loop_iter++;
9026 }
9027
9028 if (callback.index_cb && indices.size() > 0) {
9029 callback.index_cb(user_data, &indices.at(0),
9030 static_cast<int>(indices.size()));
9031 }
9032
9033 sr.skip_line();
9034 continue;
9035 }
9036
9037 // use mtl
9038 if (sr.match("usemtl", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
9039 sr.advance(6);
9040 std::string namebuf = sr_parseString(sr);
9041
9042 int newMaterialId = -1;
9043 std::map<std::string, int>::const_iterator it =
9044 material_map.find(namebuf);
9045 if (it != material_map.end()) {
9046 newMaterialId = it->second;
9047 } else {
9048 if (warn && (!callback.usemtl_cb)) {
9049 (*warn) += "material [ " + namebuf + " ] not found in .mtl\n";
9050 }
9051 }
9052
9053 if (newMaterialId != material_id) {
9054 material_id = newMaterialId;
9055 }
9056
9057 if (callback.usemtl_cb) {
9058 callback.usemtl_cb(user_data, namebuf.c_str(), material_id);
9059 }
9060
9061 sr.skip_line();
9062 continue;
9063 }
9064
9065 // load mtl
9066 if (sr.match("mtllib", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
9067 if (readMatFn) {
9068 sr.advance(7);
9069
9070 std::string line_rest = trimTrailingWhitespace(sr.read_line());
9071 std::vector<std::string> filenames;
9072 SplitString(line_rest, ' ', '\\', filenames);
9073 RemoveEmptyTokens(&filenames);
9074
9075 if (filenames.empty()) {
9076 if (warn) {
9077 (*warn) +=
9078 "Looks like empty filename for mtllib. Use default "
9079 "material. \n";
9080 }
9081 } else {
9082 bool found = false;
9083 for (size_t s = 0; s < filenames.size(); s++) {
9084 if (material_filenames.count(filenames[s]) > 0) {
9085 found = true;
9086 continue;
9087 }
9088
9089 std::string warn_mtl;
9090 std::string err_mtl;
9091 bool ok = (*readMatFn)(filenames[s].c_str(), &materials,
9092 &material_map, &warn_mtl, &err_mtl);
9093
9094 if (warn && (!warn_mtl.empty())) {
9095 (*warn) += warn_mtl;
9096 }
9097
9098 if (err && (!err_mtl.empty())) {
9099 (*err) += err_mtl;
9100 }
9101
9102 if (ok) {
9103 found = true;
9104 material_filenames.insert(filenames[s]);
9105 break;
9106 }
9107 }
9108
9109 if (!found) {
9110 if (warn) {
9111 (*warn) +=
9112 "Failed to load material file(s). Use default "
9113 "material.\n";
9114 }
9115 } else {
9116 if (callback.mtllib_cb && !materials.empty()) {
9117 callback.mtllib_cb(user_data, &materials.at(0),
9118 static_cast<int>(materials.size()));
9119 }
9120 }
9121 }
9122 }
9123
9124 sr.skip_line();
9125 continue;
9126 }
9127
9128 // group name
9129 if (sr.peek() == 'g' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9130 names.clear();
9131
9132 size_t cg_loop_max = sr.remaining() + 1;
9133 size_t cg_loop_iter = 0;
9134 while (!sr.at_line_end() && sr.peek() != '#' &&
9135 cg_loop_iter < cg_loop_max) {
9136 std::string str = sr_parseString(sr);
9137 names.push_back(str);
9138 sr.skip_space_and_cr();
9139 cg_loop_iter++;
9140 }
9141
9142 assert(names.size() > 0);
9143
9144 if (callback.group_cb) {
9145 if (names.size() > 1) {
9146 names_out.resize(names.size() - 1);
9147 for (size_t j = 0; j < names_out.size(); j++) {
9148 names_out[j] = names[j + 1].c_str();
9149 }
9150 callback.group_cb(user_data, &names_out.at(0),
9151 static_cast<int>(names_out.size()));
9152
9153 } else {
9154 callback.group_cb(user_data, NULL, 0);
9155 }
9156 }
9157
9158 sr.skip_line();
9159 continue;
9160 }
9161
9162 // object name
9163 if (sr.peek() == 'o' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9164 sr.advance(2);
9165 std::string object_name = sr.read_line();
9166
9167 if (callback.object_cb) {
9168 callback.object_cb(user_data, object_name.c_str());
9169 }
9170
9171 sr.skip_line();
9172 continue;
9173 }
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 sr.skip_line();
9206 }
9207
9208 return true;
9209}
9210
9211bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
9212 void *user_data /*= NULL*/,
9213 MaterialReader *readMatFn /*= NULL*/,
9214 std::string *warn, /* = NULL*/
9215 std::string *err /*= NULL*/) {
9216 StreamReader sr(inStream);
9217 return LoadObjWithCallbackInternal(sr, callback, user_data, readMatFn,
9218 warn, err);
9219}
9220
9221bool ObjReader::ParseFromFile(const std::string &filename,
9222 const ObjReaderConfig &config) {
9223 std::string mtl_search_path;
9224
9225 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 size_t pos = filename.find_last_of("/\\");
9231 if (pos != std::string::npos) {
9232 mtl_search_path = filename.substr(0, pos);
9233 }
9234 } else {
9235 mtl_search_path = config.mtl_search_path;
9236 }
9237
9238 valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_,
9239 filename.c_str(), mtl_search_path.c_str(),
9240 config.triangulate, config.vertex_color);
9241
9242 return valid_;
9243}
9244
9245bool ObjReader::ParseFromString(const std::string &obj_text,
9246 const std::string &mtl_text,
9247 const ObjReaderConfig &config) {
9248 std::stringbuf obj_buf(obj_text);
9249 std::stringbuf mtl_buf(mtl_text);
9250
9251 std::istream obj_ifs(&obj_buf);
9252 std::istream mtl_ifs(&mtl_buf);
9253
9254 MaterialStreamReader mtl_ss(mtl_ifs);
9255
9256 valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_,
9257 &obj_ifs, &mtl_ss, config.triangulate, config.vertex_color);
9258
9259 return valid_;
9260}
9261
9262#ifdef __clang__
9263#pragma clang diagnostic pop
9264#endif
9265} // namespace tinyobj
9266
9267#endif
bool ParseFromFile(const std::string &filename, const ObjReaderConfig &config=ObjReaderConfig())
const std::string & Warning() const
Definition tiny_obj_loader.h:575
const std::string & Error() const
Definition tiny_obj_loader.h:580
bool ParseFromString(const std::string &obj_text, const std::string &mtl_text, const ObjReaderConfig &config=ObjReaderConfig())
bool Valid() const
Definition tiny_obj_loader.h:564
Definition tiny_obj_loader.h:508
bool vertex_color
Definition tiny_obj_loader.h:520
std::string mtl_search_path
Definition tiny_obj_loader.h:527
Definition tiny_obj_loader.h:390
Definition tiny_obj_loader.h:350
Definition tiny_obj_loader.h:336
Definition tiny_obj_loader.h:372
Definition tiny_obj_loader.h:184
Definition tiny_obj_loader.h:356
Definition tiny_obj_loader.h:378
Definition tiny_obj_loader.h:382
Definition tiny_obj_loader.h:341
Definition tiny_obj_loader.h:328
Definition tiny_obj_loader.h:163