Coverage Report

Created: 2026-07-25 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tinyobjloader/tiny_obj_loader.h
Line
Count
Source
1
/*
2
The MIT License (MIT)
3
4
Copyright (c) 2012-Present, Syoyo Fujita and many contributors.
5
6
Permission is hereby granted, free of charge, to any person obtaining a copy
7
of this software and associated documentation files (the "Software"), to deal
8
in the Software without restriction, including without limitation the rights
9
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
copies of the Software, and to permit persons to whom the Software is
11
furnished to do so, subject to the following conditions:
12
13
The above copyright notice and this permission notice shall be included in
14
all copies or substantial portions of the Software.
15
16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
THE SOFTWARE.
23
*/
24
25
//
26
// version 2.0.0 : Add new object oriented API. 1.x API is still provided.
27
//                 * Add python binding.
28
//                 * Support line primitive.
29
//                 * Support points primitive.
30
//                 * Support multiple search path for .mtl(v1 API).
31
//                 * Support vertex skinning weight `vw`(as an tinyobj
32
//                 extension). Note that this differs vertex weight([w]
33
//                 component in `v` line)
34
//                 * Support escaped whitespece in mtllib
35
//                 * Add robust triangulation using Mapbox
36
//                 earcut(TINYOBJLOADER_USE_MAPBOX_EARCUT).
37
// version 1.4.0 : Modifed ParseTextureNameAndOption API
38
// version 1.3.1 : Make ParseTextureNameAndOption API public
39
// version 1.3.0 : Separate warning and error message(breaking API of LoadObj)
40
// version 1.2.3 : Added color space extension('-colorspace') to tex opts.
41
// version 1.2.2 : Parse multiple group names.
42
// version 1.2.1 : Added initial support for line('l') primitive(PR #178)
43
// version 1.2.0 : Hardened implementation(#175)
44
// version 1.1.1 : Support smoothing groups(#162)
45
// version 1.1.0 : Support parsing vertex color(#144)
46
// version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138)
47
// version 1.0.7 : Support multiple tex options(#126)
48
// version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124)
49
// version 1.0.5 : Ignore `Tr` when `d` exists in MTL(#43)
50
// version 1.0.4 : Support multiple filenames for 'mtllib'(#112)
51
// version 1.0.3 : Support parsing texture options(#85)
52
// version 1.0.2 : Improve parsing speed by about a factor of 2 for large
53
// files(#105)
54
// version 1.0.1 : Fixes a shape is lost if obj ends with a 'usemtl'(#104)
55
// version 1.0.0 : Change data structure. Change license from BSD to MIT.
56
//
57
58
//
59
// Use this in *one* .cc
60
//   #define TINYOBJLOADER_IMPLEMENTATION
61
//   #include "tiny_obj_loader.h"
62
//
63
64
#ifndef TINY_OBJ_LOADER_H_
65
#define TINY_OBJ_LOADER_H_
66
67
#include <algorithm>
68
#include <map>
69
#include <string>
70
#include <vector>
71
72
#include <cstdint>
73
#include <cstring>
74
#include <memory>
75
#include <new>
76
#include <type_traits>
77
78
namespace tinyobj {
79
80
// C++11 is now the minimum required standard.
81
#if __cplusplus < 201103L && (!defined(_MSVC_LANG) || _MSVC_LANG < 201103L)
82
#error "tinyobjloader requires C++11 or later. Compile with -std=c++11 or higher."
83
#endif
84
85
#ifdef __clang__
86
#pragma clang diagnostic push
87
#if __has_warning("-Wzero-as-null-pointer-constant")
88
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
89
#endif
90
91
#pragma clang diagnostic ignored "-Wpadded"
92
93
#endif
94
95
// https://en.wikipedia.org/wiki/Wavefront_.obj_file says ...
96
//
97
//  -blendu on | off                       # set horizontal texture blending
98
//  (default on)
99
//  -blendv on | off                       # set vertical texture blending
100
//  (default on)
101
//  -boost real_value                      # boost mip-map sharpness
102
//  -mm base_value gain_value              # modify texture map values (default
103
//  0 1)
104
//                                         #     base_value = brightness,
105
//                                         gain_value = contrast
106
//  -o u [v [w]]                           # Origin offset             (default
107
//  0 0 0)
108
//  -s u [v [w]]                           # Scale                     (default
109
//  1 1 1)
110
//  -t u [v [w]]                           # Turbulence                (default
111
//  0 0 0)
112
//  -texres resolution                     # texture resolution to create
113
//  -clamp on | off                        # only render texels in the clamped
114
//  0-1 range (default off)
115
//                                         #   When unclamped, textures are
116
//                                         repeated across a surface,
117
//                                         #   when clamped, only texels which
118
//                                         fall within the 0-1
119
//                                         #   range are rendered.
120
//  -bm mult_value                         # bump multiplier (for bump maps
121
//  only)
122
//
123
//  -imfchan r | g | b | m | l | z         # specifies which channel of the file
124
//  is used to
125
//                                         # create a scalar or bump texture.
126
//                                         r:red, g:green,
127
//                                         # b:blue, m:matte, l:luminance,
128
//                                         z:z-depth..
129
//                                         # (the default for bump is 'l' and
130
//                                         for decal is 'm')
131
//  bump -imfchan r bumpmap.tga            # says to use the red channel of
132
//  bumpmap.tga as the bumpmap
133
//
134
// For reflection maps...
135
//
136
//   -type sphere                           # specifies a sphere for a "refl"
137
//   reflection map
138
//   -type cube_top    | cube_bottom |      # when using a cube map, the texture
139
//   file for each
140
//         cube_front  | cube_back   |      # side of the cube is specified
141
//         separately
142
//         cube_left   | cube_right
143
//
144
// TinyObjLoader extension.
145
//
146
//   -colorspace SPACE                      # Color space of the texture. e.g.
147
//   'sRGB` or 'linear'
148
//
149
150
#ifdef TINYOBJLOADER_USE_DOUBLE
151
//#pragma message "using double"
152
typedef double real_t;
153
#else
154
//#pragma message "using float"
155
typedef float real_t;
156
#endif
157
158
typedef enum {
159
  TEXTURE_TYPE_NONE,  // default
160
  TEXTURE_TYPE_SPHERE,
161
  TEXTURE_TYPE_CUBE_TOP,
162
  TEXTURE_TYPE_CUBE_BOTTOM,
163
  TEXTURE_TYPE_CUBE_FRONT,
164
  TEXTURE_TYPE_CUBE_BACK,
165
  TEXTURE_TYPE_CUBE_LEFT,
166
  TEXTURE_TYPE_CUBE_RIGHT
167
} texture_type_t;
168
169
struct texture_option_t {
170
  texture_type_t type;      // -type (default TEXTURE_TYPE_NONE)
171
  real_t sharpness;         // -boost (default 1.0?)
172
  real_t brightness;        // base_value in -mm option (default 0)
173
  real_t contrast;          // gain_value in -mm option (default 1)
174
  real_t origin_offset[3];  // -o u [v [w]] (default 0 0 0)
175
  real_t scale[3];          // -s u [v [w]] (default 1 1 1)
176
  real_t turbulence[3];     // -t u [v [w]] (default 0 0 0)
177
  int texture_resolution;   // -texres resolution (No default value in the spec.
178
                            // We'll use -1)
179
  bool clamp;               // -clamp (default false)
180
  char imfchan;  // -imfchan (the default for bump is 'l' and for decal is 'm')
181
  bool blendu;   // -blendu (default on)
182
  bool blendv;   // -blendv (default on)
183
  real_t bump_multiplier;  // -bm (for bump maps only, default 1.0)
184
185
  // extension
186
  std::string colorspace;  // Explicitly specify color space of stored texel
187
                           // value. Usually `sRGB` or `linear` (default empty).
188
};
189
190
struct material_t {
191
  std::string name;
192
193
  real_t ambient[3];
194
  real_t diffuse[3];
195
  real_t specular[3];
196
  real_t transmittance[3];
197
  real_t emission[3];
198
  real_t shininess;
199
  real_t ior;       // index of refraction
200
  real_t dissolve;  // 1 == opaque; 0 == fully transparent
201
  // illumination model (see http://www.fileformat.info/format/material/)
202
  int illum;
203
204
  int dummy;  // Suppress padding warning.
205
206
  std::string ambient_texname;   // map_Ka. For ambient or ambient occlusion.
207
  std::string diffuse_texname;   // map_Kd
208
  std::string specular_texname;  // map_Ks
209
  std::string specular_highlight_texname;  // map_Ns
210
  std::string bump_texname;                // map_bump, map_Bump, bump
211
  std::string displacement_texname;        // disp
212
  std::string alpha_texname;               // map_d
213
  std::string reflection_texname;          // refl
214
215
  texture_option_t ambient_texopt;
216
  texture_option_t diffuse_texopt;
217
  texture_option_t specular_texopt;
218
  texture_option_t specular_highlight_texopt;
219
  texture_option_t bump_texopt;
220
  texture_option_t displacement_texopt;
221
  texture_option_t alpha_texopt;
222
  texture_option_t reflection_texopt;
223
224
  // PBR extension
225
  // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr
226
  real_t roughness;            // [0, 1] default 0
227
  real_t metallic;             // [0, 1] default 0
228
  real_t sheen;                // [0, 1] default 0
229
  real_t clearcoat_thickness;  // [0, 1] default 0
230
  real_t clearcoat_roughness;  // [0, 1] default 0
231
  real_t anisotropy;           // aniso. [0, 1] default 0
232
  real_t anisotropy_rotation;  // anisor. [0, 1] default 0
233
  real_t pad0;
234
  std::string roughness_texname;  // map_Pr
235
  std::string metallic_texname;   // map_Pm
236
  std::string sheen_texname;      // map_Ps
237
  std::string emissive_texname;   // map_Ke
238
  std::string normal_texname;     // norm. For normal mapping.
239
240
  texture_option_t roughness_texopt;
241
  texture_option_t metallic_texopt;
242
  texture_option_t sheen_texopt;
243
  texture_option_t emissive_texopt;
244
  texture_option_t normal_texopt;
245
246
  int pad2;
247
248
  std::map<std::string, std::string> unknown_parameter;
249
250
#ifdef TINY_OBJ_LOADER_PYTHON_BINDING
251
  // For pybind11
252
  std::array<double, 3> GetDiffuse() {
253
    std::array<double, 3> values;
254
    values[0] = double(diffuse[0]);
255
    values[1] = double(diffuse[1]);
256
    values[2] = double(diffuse[2]);
257
258
    return values;
259
  }
260
261
  std::array<double, 3> GetSpecular() {
262
    std::array<double, 3> values;
263
    values[0] = double(specular[0]);
264
    values[1] = double(specular[1]);
265
    values[2] = double(specular[2]);
266
267
    return values;
268
  }
269
270
  std::array<double, 3> GetTransmittance() {
271
    std::array<double, 3> values;
272
    values[0] = double(transmittance[0]);
273
    values[1] = double(transmittance[1]);
274
    values[2] = double(transmittance[2]);
275
276
    return values;
277
  }
278
279
  std::array<double, 3> GetEmission() {
280
    std::array<double, 3> values;
281
    values[0] = double(emission[0]);
282
    values[1] = double(emission[1]);
283
    values[2] = double(emission[2]);
284
285
    return values;
286
  }
287
288
  std::array<double, 3> GetAmbient() {
289
    std::array<double, 3> values;
290
    values[0] = double(ambient[0]);
291
    values[1] = double(ambient[1]);
292
    values[2] = double(ambient[2]);
293
294
    return values;
295
  }
296
297
  void SetDiffuse(std::array<double, 3> &a) {
298
    diffuse[0] = real_t(a[0]);
299
    diffuse[1] = real_t(a[1]);
300
    diffuse[2] = real_t(a[2]);
301
  }
302
303
  void SetAmbient(std::array<double, 3> &a) {
304
    ambient[0] = real_t(a[0]);
305
    ambient[1] = real_t(a[1]);
306
    ambient[2] = real_t(a[2]);
307
  }
308
309
  void SetSpecular(std::array<double, 3> &a) {
310
    specular[0] = real_t(a[0]);
311
    specular[1] = real_t(a[1]);
312
    specular[2] = real_t(a[2]);
313
  }
314
315
  void SetTransmittance(std::array<double, 3> &a) {
316
    transmittance[0] = real_t(a[0]);
317
    transmittance[1] = real_t(a[1]);
318
    transmittance[2] = real_t(a[2]);
319
  }
320
321
  std::string GetCustomParameter(const std::string &key) {
322
    std::map<std::string, std::string>::const_iterator it =
323
        unknown_parameter.find(key);
324
325
    if (it != unknown_parameter.end()) {
326
      return it->second;
327
    }
328
    return std::string();
329
  }
330
331
#endif
332
};
333
334
template <typename Alloc = std::allocator<char>>
335
struct basic_tag_t {
336
  using allocator_type = Alloc;
337
  using char_alloc =
338
      typename std::allocator_traits<Alloc>::template rebind_alloc<char>;
339
  using string_type =
340
      std::basic_string<char, std::char_traits<char>, char_alloc>;
341
  using int_alloc =
342
      typename std::allocator_traits<Alloc>::template rebind_alloc<int>;
343
  using real_alloc =
344
      typename std::allocator_traits<Alloc>::template rebind_alloc<real_t>;
345
  using string_alloc =
346
      typename std::allocator_traits<Alloc>::template rebind_alloc<string_type>;
347
348
  string_type name;
349
  std::vector<int, int_alloc> intValues;
350
  std::vector<real_t, real_alloc> floatValues;
351
  std::vector<string_type, string_alloc> stringValues;
352
353
  basic_tag_t()
354
903k
      : name(),
355
903k
        intValues(),
356
903k
        floatValues(),
357
903k
        stringValues() {}
358
359
  explicit basic_tag_t(const allocator_type &alloc)
360
      : name(char_alloc(alloc)),
361
        intValues(int_alloc(alloc)),
362
        floatValues(real_alloc(alloc)),
363
        stringValues(string_alloc(alloc)) {}
364
365
  template <typename OtherAlloc>
366
  basic_tag_t(const basic_tag_t<OtherAlloc> &rhs) : name(rhs.name) {
367
    intValues.assign(rhs.intValues.begin(), rhs.intValues.end());
368
    floatValues.assign(rhs.floatValues.begin(), rhs.floatValues.end());
369
    stringValues.assign(rhs.stringValues.begin(), rhs.stringValues.end());
370
  }
371
372
  template <typename OtherAlloc>
373
  basic_tag_t(const basic_tag_t<OtherAlloc> &rhs, const allocator_type &alloc)
374
      : name(rhs.name.begin(), rhs.name.end(), char_alloc(alloc)),
375
        intValues(int_alloc(alloc)),
376
        floatValues(real_alloc(alloc)),
377
        stringValues(string_alloc(alloc)) {
378
    intValues.assign(rhs.intValues.begin(), rhs.intValues.end());
379
    floatValues.assign(rhs.floatValues.begin(), rhs.floatValues.end());
380
    for (size_t i = 0; i < rhs.stringValues.size(); i++) {
381
      stringValues.emplace_back(rhs.stringValues[i].begin(),
382
                                rhs.stringValues[i].end(), char_alloc(alloc));
383
    }
384
  }
385
386
  template <typename OtherAlloc>
387
  basic_tag_t &operator=(const basic_tag_t<OtherAlloc> &rhs) {
388
    name = rhs.name;
389
    intValues.assign(rhs.intValues.begin(), rhs.intValues.end());
390
    floatValues.assign(rhs.floatValues.begin(), rhs.floatValues.end());
391
    stringValues.assign(rhs.stringValues.begin(), rhs.stringValues.end());
392
    return *this;
393
  }
394
};
395
396
using tag_t = basic_tag_t<>;
397
398
struct joint_and_weight_t {
399
  int joint_id;
400
  real_t weight;
401
};
402
403
template <typename Alloc = std::allocator<char>>
404
struct basic_skin_weight_t {
405
  using allocator_type = Alloc;
406
  using joint_weight_alloc =
407
      typename std::allocator_traits<Alloc>::template rebind_alloc<
408
          joint_and_weight_t>;
409
410
  int vertex_id;  // Corresponding vertex index in `attrib_t::vertices`.
411
                  // Compared to `index_t`, this index must be positive and
412
                  // start with 0(does not allow relative indexing)
413
  std::vector<joint_and_weight_t, joint_weight_alloc> weightValues;
414
415
79.3k
  basic_skin_weight_t() : vertex_id(0) {}
416
417
  explicit basic_skin_weight_t(const allocator_type &alloc)
418
      : vertex_id(0), weightValues(joint_weight_alloc(alloc)) {}
419
420
  template <typename OtherAlloc>
421
  basic_skin_weight_t(const basic_skin_weight_t<OtherAlloc> &rhs)
422
      : vertex_id(rhs.vertex_id) {
423
    weightValues.assign(rhs.weightValues.begin(), rhs.weightValues.end());
424
  }
425
426
  template <typename OtherAlloc>
427
  basic_skin_weight_t(const basic_skin_weight_t<OtherAlloc> &rhs,
428
                      const allocator_type &alloc)
429
      : vertex_id(rhs.vertex_id), weightValues(joint_weight_alloc(alloc)) {
430
    weightValues.assign(rhs.weightValues.begin(), rhs.weightValues.end());
431
  }
432
433
  template <typename OtherAlloc>
434
  basic_skin_weight_t &operator=(const basic_skin_weight_t<OtherAlloc> &rhs) {
435
    vertex_id = rhs.vertex_id;
436
    weightValues.assign(rhs.weightValues.begin(), rhs.weightValues.end());
437
    return *this;
438
  }
439
};
440
441
using skin_weight_t = basic_skin_weight_t<>;
442
443
// Index struct to support different indices for vtx/normal/texcoord.
444
// -1 means not used.
445
struct index_t {
446
  int vertex_index;
447
  int normal_index;
448
  int texcoord_index;
449
};
450
451
struct mesh_t {
452
  std::vector<index_t> indices;
453
  std::vector<unsigned int>
454
      num_face_vertices;          // The number of vertices per
455
                                  // face. 3 = triangle, 4 = quad, ...
456
  std::vector<int> material_ids;  // per-face material ID
457
  std::vector<unsigned int> smoothing_group_ids;  // per-face smoothing group
458
                                                  // ID(0 = off. positive value
459
                                                  // = group id)
460
  std::vector<tag_t> tags;                        // SubD tag
461
};
462
463
// struct path_t {
464
//  std::vector<int> indices;  // pairs of indices for lines
465
//};
466
467
struct lines_t {
468
  // Linear flattened indices.
469
  std::vector<index_t> indices;        // indices for vertices(poly lines)
470
  std::vector<int> num_line_vertices;  // The number of vertices per line.
471
};
472
473
struct points_t {
474
  std::vector<index_t> indices;  // indices for points
475
};
476
477
struct shape_t {
478
  std::string name;
479
  mesh_t mesh;
480
  lines_t lines;
481
  points_t points;
482
};
483
484
// Vertex attributes
485
struct attrib_t {
486
  std::vector<real_t> vertices;  // 'v'(xyz)
487
488
  // For backward compatibility, we store vertex weight in separate array.
489
  std::vector<real_t> vertex_weights;  // 'v'(w)
490
  std::vector<real_t> normals;         // 'vn'
491
  std::vector<real_t> texcoords;       // 'vt'(uv)
492
493
  // For backward compatibility, we store texture coordinate 'w' in separate
494
  // array.
495
  std::vector<real_t> texcoord_ws;  // 'vt'(w)
496
  std::vector<real_t> colors;       // extension: vertex colors
497
498
  //
499
  // TinyObj extension.
500
  //
501
502
  // NOTE(syoyo): array index is based on the appearance order.
503
  // To get a corresponding skin weight for a specific vertex id `vid`,
504
  // Need to reconstruct a look up table: `skin_weight_t::vertex_id` == `vid`
505
  // (e.g. using std::map, std::unordered_map)
506
  std::vector<skin_weight_t> skin_weights;
507
508
11.4k
  attrib_t() {}
509
510
  //
511
  // For pybind11
512
  //
513
0
  const std::vector<real_t> &GetVertices() const { return vertices; }
514
515
0
  const std::vector<real_t> &GetVertexWeights() const { return vertex_weights; }
516
};
517
518
struct callback_t {
519
  // W is optional and set to 1 if there is no `w` item in `v` line
520
  void (*vertex_cb)(void *user_data, real_t x, real_t y, real_t z, real_t w);
521
  void (*vertex_color_cb)(void *user_data, real_t x, real_t y, real_t z,
522
                          real_t r, real_t g, real_t b, bool has_color);
523
  void (*normal_cb)(void *user_data, real_t x, real_t y, real_t z);
524
525
  // y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in
526
  // `vt` line.
527
  void (*texcoord_cb)(void *user_data, real_t x, real_t y, real_t z);
528
529
  // called per 'f' line. num_indices is the number of face indices(e.g. 3 for
530
  // triangle, 4 for quad)
531
  // 0 will be passed for undefined index in index_t members.
532
  void (*index_cb)(void *user_data, index_t *indices, int num_indices);
533
  // `name` material name, `material_id` = the array index of material_t[]. -1
534
  // if
535
  // a material not found in .mtl
536
  void (*usemtl_cb)(void *user_data, const char *name, int material_id);
537
  // `materials` = parsed material data.
538
  void (*mtllib_cb)(void *user_data, const material_t *materials,
539
                    int num_materials);
540
  // There may be multiple group names
541
  void (*group_cb)(void *user_data, const char **names, int num_names);
542
  void (*object_cb)(void *user_data, const char *name);
543
544
  callback_t()
545
      : vertex_cb(NULL),
546
        vertex_color_cb(NULL),
547
        normal_cb(NULL),
548
        texcoord_cb(NULL),
549
        index_cb(NULL),
550
        usemtl_cb(NULL),
551
        mtllib_cb(NULL),
552
        group_cb(NULL),
553
0
        object_cb(NULL) {}
554
};
555
556
class MaterialReader {
557
 public:
558
11.4k
  MaterialReader() {}
559
  virtual ~MaterialReader();
560
561
  virtual bool operator()(const std::string &matId,
562
                          std::vector<material_t> *materials,
563
                          std::map<std::string, int> *matMap, std::string *warn,
564
                          std::string *err) = 0;
565
};
566
567
///
568
/// Read .mtl from a file.
569
///
570
class MaterialFileReader : public MaterialReader {
571
 public:
572
  // Path could contain separator(';' in Windows, ':' in Posix)
573
  explicit MaterialFileReader(const std::string &mtl_basedir)
574
0
      : m_mtlBaseDir(mtl_basedir) {}
575
0
  virtual ~MaterialFileReader() override {}
576
  virtual bool operator()(const std::string &matId,
577
                          std::vector<material_t> *materials,
578
                          std::map<std::string, int> *matMap, std::string *warn,
579
                          std::string *err) override;
580
581
 private:
582
  std::string m_mtlBaseDir;
583
};
584
585
///
586
/// Read .mtl from a stream.
587
///
588
class MaterialStreamReader : public MaterialReader {
589
 public:
590
  explicit MaterialStreamReader(std::istream &inStream)
591
11.4k
      : m_inStream(inStream) {}
592
0
  virtual ~MaterialStreamReader() override {}
593
  virtual bool operator()(const std::string &matId,
594
                          std::vector<material_t> *materials,
595
                          std::map<std::string, int> *matMap, std::string *warn,
596
                          std::string *err) override;
597
598
 private:
599
  std::istream &m_inStream;
600
};
601
602
// v2 API
603
struct ObjReaderConfig {
604
  bool triangulate;  // triangulate polygon?
605
606
  // Currently not used.
607
  // "simple" or empty: Create triangle fan
608
  // "earcut": Use the algorithm based on Ear clipping
609
  std::string triangulation_method;
610
611
  /// Parse vertex color.
612
  /// If vertex color is not present, its filled with default value.
613
  /// false = no vertex color
614
  /// This will increase memory of parsed .obj
615
  bool vertex_color;
616
617
  ///
618
  /// Search path to .mtl file.
619
  /// Default = "" = search from the same directory of .obj file.
620
  /// Valid only when loading .obj from a file.
621
  ///
622
  std::string mtl_search_path;
623
624
  ObjReaderConfig()
625
11.4k
      : triangulate(true), triangulation_method("simple"), vertex_color(true) {}
626
};
627
628
///
629
/// Wavefront .obj reader class(v2 API)
630
///
631
class ObjReader {
632
 public:
633
11.4k
  ObjReader() : valid_(false) {}
634
635
  ///
636
  /// Load .obj and .mtl from a file.
637
  ///
638
  /// @param[in] filename wavefront .obj filename
639
  /// @param[in] config Reader configuration
640
  ///
641
  bool ParseFromFile(const std::string &filename,
642
                     const ObjReaderConfig &config = ObjReaderConfig());
643
644
  ///
645
  /// Parse .obj from a text string.
646
  /// Need to supply .mtl text string by `mtl_text`.
647
  /// This function ignores `mtllib` line in .obj text.
648
  ///
649
  /// @param[in] obj_text wavefront .obj filename
650
  /// @param[in] mtl_text wavefront .mtl filename
651
  /// @param[in] config Reader configuration
652
  ///
653
  bool ParseFromString(const std::string &obj_text, const std::string &mtl_text,
654
                       const ObjReaderConfig &config = ObjReaderConfig());
655
656
  ///
657
  /// .obj was loaded or parsed correctly.
658
  ///
659
0
  bool Valid() const { return valid_; }
660
661
0
  const attrib_t &GetAttrib() const { return attrib_; }
662
663
0
  const std::vector<shape_t> &GetShapes() const { return shapes_; }
664
665
0
  const std::vector<material_t> &GetMaterials() const { return materials_; }
666
667
  ///
668
  /// Warning message(may be filled after `Load` or `Parse`)
669
  ///
670
0
  const std::string &Warning() const { return warning_; }
671
672
  ///
673
  /// Error message(filled when `Load` or `Parse` failed)
674
  ///
675
0
  const std::string &Error() const { return error_; }
676
677
 private:
678
  bool valid_;
679
680
  attrib_t attrib_;
681
  std::vector<shape_t> shapes_;
682
  std::vector<material_t> materials_;
683
684
  std::string warning_;
685
  std::string error_;
686
};
687
688
/// ==>>========= Legacy v1 API =============================================
689
690
/// Loads .obj from a file.
691
/// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data
692
/// 'shapes' will be filled with parsed shape data
693
/// Returns true when loading .obj become success.
694
/// Returns warning message into `warn`, and error message into `err`
695
/// 'mtl_basedir' is optional, and used for base directory for .mtl file.
696
/// In default(`NULL'), .mtl file is searched from an application's working
697
/// directory.
698
/// 'triangulate' is optional, and used whether triangulate polygon face in .obj
699
/// or not.
700
/// Option 'default_vcols_fallback' specifies whether vertex colors should
701
/// always be defined, even if no colors are given (fallback to white).
702
bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
703
             std::vector<material_t> *materials, std::string *warn,
704
             std::string *err, const char *filename,
705
             const char *mtl_basedir = NULL, bool triangulate = true,
706
             bool default_vcols_fallback = true);
707
708
/// Loads .obj from a file with custom user callback.
709
/// .mtl is loaded as usual and parsed material_t data will be passed to
710
/// `callback.mtllib_cb`.
711
/// Returns true when loading .obj/.mtl become success.
712
/// Returns warning message into `warn`, and error message into `err`
713
/// See `examples/callback_api/` for how to use this function.
714
bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
715
                         void *user_data = NULL,
716
                         MaterialReader *readMatFn = NULL,
717
                         std::string *warn = NULL, std::string *err = NULL);
718
719
/// Loads object from a std::istream, uses `readMatFn` to retrieve
720
/// std::istream for materials.
721
/// Returns true when loading .obj become success.
722
/// Returns warning and error message into `err`
723
bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
724
             std::vector<material_t> *materials, std::string *warn,
725
             std::string *err, std::istream *inStream,
726
             MaterialReader *readMatFn = NULL, bool triangulate = true,
727
             bool default_vcols_fallback = true);
728
729
/// Loads materials into std::map
730
void LoadMtl(std::map<std::string, int> *material_map,
731
             std::vector<material_t> *materials, std::istream *inStream,
732
             std::string *warning, std::string *err);
733
734
///
735
/// Parse texture name and texture option for custom texture parameter through
736
/// material::unknown_parameter
737
///
738
/// @param[out] texname Parsed texture name
739
/// @param[out] texopt Parsed texopt
740
/// @param[in] linebuf Input string
741
///
742
bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt,
743
                               const char *linebuf);
744
745
/// =<<========== Legacy v1 API =============================================
746
747
/// ==>>========= Optimized API (C++11 required) ============================
748
///
749
/// Optional compile options (all DISABLED by default — define the macro to
750
/// opt in):
751
///   TINYOBJLOADER_USE_MULTITHREADING - multi-threaded parsing
752
///   TINYOBJLOADER_USE_SIMD           - SIMD-accelerated line scanning
753
///                                      (SSE2/AVX2 on x86, NEON on ARM)
754
///   TINYOBJLOADER_ENABLE_EXCEPTION   - throw std::bad_alloc on allocation
755
///                                      failure instead of returning nullptr
756
///
757
/// With none of these defined, the parser runs single-threaded, scalar, and
758
/// exception-free.  These features require C++11 or later.
759
///
760
761
///
762
/// Arena-based memory allocator for reduced allocation overhead when
763
/// loading huge meshes.  Memory is freed in bulk when the arena is
764
/// destroyed or reset().  Individual deallocate() calls are no-ops.
765
///
766
class ArenaAllocator {
767
 public:
768
  explicit ArenaAllocator(size_t block_size = 1024 * 1024)
769
0
      : head_(nullptr), default_block_size_(block_size) {}
770
771
0
  ~ArenaAllocator() { destroy(); }
772
773
  ArenaAllocator(const ArenaAllocator &) = delete;
774
  ArenaAllocator &operator=(const ArenaAllocator &) = delete;
775
776
  ArenaAllocator(ArenaAllocator &&o) noexcept
777
0
      : head_(o.head_), default_block_size_(o.default_block_size_) {
778
0
    o.head_ = nullptr;
779
0
  }
780
0
  ArenaAllocator &operator=(ArenaAllocator &&o) noexcept {
781
0
    if (this != &o) {
782
0
      destroy();
783
0
      head_ = o.head_;
784
0
      default_block_size_ = o.default_block_size_;
785
0
      o.head_ = nullptr;
786
0
    }
787
0
    return *this;
788
0
  }
789
790
  void *allocate(size_t bytes,
791
                 size_t alignment = sizeof(void *));
792
793
  /// Free all memory at once.
794
  void reset();
795
796
 private:
797
  struct Block {
798
    unsigned char *data;
799
    size_t capacity;
800
    size_t used;
801
    Block *next;
802
  };
803
804
  Block *head_;
805
  size_t default_block_size_;
806
807
  Block *new_block(size_t min_bytes);
808
  void destroy();
809
};
810
811
///
812
/// STL-compatible allocator adapter backed by an ArenaAllocator.
813
/// deallocate() is a no-op — memory is released when the arena is reset.
814
///
815
template <typename T>
816
class arena_adapter {
817
 public:
818
  using value_type = T;
819
  using pointer = T *;
820
  using const_pointer = const T *;
821
  using size_type = std::size_t;
822
  using difference_type = std::ptrdiff_t;
823
  using propagate_on_container_copy_assignment = std::true_type;
824
  using propagate_on_container_move_assignment = std::true_type;
825
  using propagate_on_container_swap = std::true_type;
826
827
  explicit arena_adapter(ArenaAllocator *arena = nullptr) noexcept
828
      : arena_(arena) {}
829
830
  template <typename U>
831
  arena_adapter(const arena_adapter<U> &other) noexcept
832
      : arena_(other.arena()) {}
833
834
  T *allocate(size_t n) {
835
    if (n > SIZE_MAX / sizeof(T)) {
836
#ifdef TINYOBJLOADER_ENABLE_EXCEPTION
837
      throw std::bad_alloc();
838
#else
839
      return nullptr;
840
#endif
841
    }
842
    if (arena_) {
843
      return static_cast<T *>(arena_->allocate(n * sizeof(T), alignof(T)));
844
    }
845
#ifdef TINYOBJLOADER_ENABLE_EXCEPTION
846
    return static_cast<T *>(::operator new(n * sizeof(T)));
847
#else
848
    return static_cast<T *>(::operator new(n * sizeof(T), std::nothrow));
849
#endif
850
  }
851
852
  void deallocate(T *p, size_t) noexcept {
853
    if (!arena_) {
854
      ::operator delete(p);
855
      return;
856
    }
857
    // Arena deallocation is a no-op; memory freed in bulk via reset().
858
  }
859
860
  ArenaAllocator *arena() const noexcept { return arena_; }
861
862
  template <typename U>
863
  bool operator==(const arena_adapter<U> &o) const noexcept {
864
    return arena_ == o.arena();
865
  }
866
  template <typename U>
867
  bool operator!=(const arena_adapter<U> &o) const noexcept {
868
    return arena_ != o.arena();
869
  }
870
871
  template <typename U>
872
  struct rebind {
873
    using other = arena_adapter<U>;
874
  };
875
876
 private:
877
  ArenaAllocator *arena_;
878
};
879
880
///
881
/// Template mesh type supporting custom allocators.
882
/// Note: Alloc must be default-constructible (stateless). For stateful
883
/// allocators like arena_adapter, construct vectors individually with
884
/// an allocator instance.
885
///
886
template <typename Alloc = std::allocator<char>>
887
struct basic_mesh_t {
888
  using index_alloc =
889
      typename std::allocator_traits<Alloc>::template rebind_alloc<index_t>;
890
  using uint_alloc =
891
      typename std::allocator_traits<Alloc>::template rebind_alloc<unsigned int>;
892
  using int_alloc =
893
      typename std::allocator_traits<Alloc>::template rebind_alloc<int>;
894
  using tag_alloc =
895
      typename std::allocator_traits<Alloc>::template rebind_alloc<
896
          basic_tag_t<Alloc> >;
897
898
  std::vector<index_t, index_alloc> indices;
899
  std::vector<unsigned int, uint_alloc> num_face_vertices;
900
  std::vector<int, int_alloc> material_ids;
901
  std::vector<unsigned int, uint_alloc> smoothing_group_ids;
902
  std::vector<basic_tag_t<Alloc>, tag_alloc> tags;
903
};
904
905
template <typename Alloc = std::allocator<char>>
906
struct basic_lines_t {
907
  using index_alloc =
908
      typename std::allocator_traits<Alloc>::template rebind_alloc<index_t>;
909
  using int_alloc =
910
      typename std::allocator_traits<Alloc>::template rebind_alloc<int>;
911
912
  std::vector<index_t, index_alloc> indices;
913
  std::vector<int, int_alloc> num_line_vertices;
914
};
915
916
template <typename Alloc = std::allocator<char>>
917
struct basic_points_t {
918
  using index_alloc =
919
      typename std::allocator_traits<Alloc>::template rebind_alloc<index_t>;
920
921
  std::vector<index_t, index_alloc> indices;
922
};
923
924
///
925
/// Template shape type supporting custom allocators.
926
/// `name` always uses the default allocator; only mesh buffers are
927
/// allocator-aware.
928
///
929
template <typename Alloc = std::allocator<char>>
930
struct basic_shape_t {
931
  std::string name;
932
  basic_mesh_t<Alloc> mesh;
933
  basic_lines_t<Alloc> lines;
934
  basic_points_t<Alloc> points;
935
};
936
937
///
938
/// Template attrib type supporting custom allocators.
939
/// Flat arrays: vertices(xyz), normals(xyz), texcoords(uv).
940
/// Note: Alloc must be default-constructible (stateless). For stateful
941
/// allocators like arena_adapter, construct vectors individually with
942
/// an allocator instance.
943
///
944
template <typename Alloc = std::allocator<char>>
945
struct basic_attrib_t {
946
  using real_alloc =
947
      typename std::allocator_traits<Alloc>::template rebind_alloc<real_t>;
948
  using int_alloc =
949
      typename std::allocator_traits<Alloc>::template rebind_alloc<int>;
950
  using index_alloc =
951
      typename std::allocator_traits<Alloc>::template rebind_alloc<index_t>;
952
  using skin_weight_alloc =
953
      typename std::allocator_traits<Alloc>::template rebind_alloc<
954
          basic_skin_weight_t<Alloc> >;
955
956
  std::vector<real_t, real_alloc> vertices;   // xyz
957
  std::vector<real_t, real_alloc> vertex_weights;  // optional w for `v`
958
  std::vector<real_t, real_alloc> normals;    // xyz
959
  std::vector<real_t, real_alloc> texcoords;  // uv
960
  std::vector<real_t, real_alloc> texcoord_ws;  // optional w for `vt`
961
  std::vector<real_t, real_alloc> colors;     // rgb (optional)
962
  std::vector<basic_skin_weight_t<Alloc>, skin_weight_alloc> skin_weights;
963
  std::vector<index_t, index_alloc> indices;  // face indices
964
  std::vector<int, int_alloc> face_num_verts; // verts per face
965
  std::vector<int, int_alloc> material_ids;   // per-face material
966
};
967
968
///
969
/// Configuration for the optimized loader.
970
///
971
struct OptLoadConfig {
972
  /// Number of threads.  -1 = hardware_concurrency, 0 or 1 = single-threaded.
973
  /// Effective only when TINYOBJLOADER_USE_MULTITHREADING is defined.
974
  int num_threads;
975
976
  bool triangulate;  ///< Triangulate polygons (fan triangulation).
977
  bool verbose;      ///< Reserved for future use (currently has no effect).
978
979
  /// Enable trie-based float string → value cache.
980
  /// Speeds up files with many repeated coordinate values (e.g. "0.0", "1.0").
981
  bool float_cache;
982
983
  /// Maximum trie nodes per thread (controls cache memory).
984
  /// Each node is ~36 bytes, so 1024 nodes ≈ 36 KB (fits in L1 cache).
985
  int float_cache_max_nodes;
986
987
  /// Use fp32-length keys for the float cache (max 8 chars).
988
  /// When true, only tokens up to `float::digits10 + 2` characters are cached.
989
  /// When false, uses `real_t::digits10 + 2` (longer keys, fewer hits).
990
  /// Has no effect when float_cache is false.
991
  bool fp32_cache;
992
993
  OptLoadConfig()
994
      : num_threads(-1), triangulate(true), verbose(false),
995
0
        float_cache(false), float_cache_max_nodes(1024), fp32_cache(true) {}
996
};
997
998
/// Optimized loader — parse from a raw memory buffer.
999
/// Supports multi-threading (TINYOBJLOADER_USE_MULTITHREADING) and
1000
/// SIMD line scanning (TINYOBJLOADER_USE_SIMD).
1001
bool LoadObjOpt(basic_attrib_t<> *attrib,
1002
                std::vector<basic_shape_t<>> *shapes,
1003
                std::vector<material_t> *materials,
1004
                std::string *warn, std::string *err,
1005
                const char *buf, size_t buf_len,
1006
                const OptLoadConfig &config = OptLoadConfig());
1007
1008
/// Optimized loader — load from a file.
1009
bool LoadObjOpt(basic_attrib_t<> *attrib,
1010
                std::vector<basic_shape_t<>> *shapes,
1011
                std::vector<material_t> *materials,
1012
                std::string *warn, std::string *err,
1013
                const char *filename,
1014
                const char *mtl_basedir = nullptr,
1015
                const OptLoadConfig &config = OptLoadConfig());
1016
1017
// ---- TypedArray-based zero-copy API for maximum efficiency ----
1018
1019
///
1020
/// Non-owning, contiguous array view allocated from an ArenaAllocator.
1021
/// No capacity tracking, no realloc.  Lifetime is tied to the arena.
1022
///
1023
template <typename T>
1024
class TypedArray {
1025
  static_assert(std::is_trivially_copyable<T>::value,
1026
                "TypedArray<T> requires T to be trivially copyable "
1027
                "(uses memset for initialization).");
1028
1029
 public:
1030
0
  TypedArray() : data_(nullptr), size_(0) {}
Unexecuted instantiation: tinyobj::TypedArray<float>::TypedArray()
Unexecuted instantiation: tinyobj::TypedArray<tinyobj::index_t>::TypedArray()
Unexecuted instantiation: tinyobj::TypedArray<int>::TypedArray()
Unexecuted instantiation: tinyobj::TypedArray<unsigned int>::TypedArray()
Unexecuted instantiation: tinyobj::TypedArray<unsigned long>::TypedArray()
1031
1032
0
  T *data() { return data_; }
Unexecuted instantiation: tinyobj::TypedArray<float>::data()
Unexecuted instantiation: tinyobj::TypedArray<tinyobj::index_t>::data()
Unexecuted instantiation: tinyobj::TypedArray<int>::data()
1033
  const T *data() const { return data_; }
1034
0
  size_t size() const { return size_; }
Unexecuted instantiation: tinyobj::TypedArray<float>::size() const
Unexecuted instantiation: tinyobj::TypedArray<int>::size() const
1035
  bool empty() const { return size_ == 0; }
1036
1037
0
  T &operator[](size_t i) { return data_[i]; }
Unexecuted instantiation: tinyobj::TypedArray<unsigned int>::operator[](unsigned long)
Unexecuted instantiation: tinyobj::TypedArray<float>::operator[](unsigned long)
Unexecuted instantiation: tinyobj::TypedArray<int>::operator[](unsigned long)
Unexecuted instantiation: tinyobj::TypedArray<tinyobj::index_t>::operator[](unsigned long)
Unexecuted instantiation: tinyobj::TypedArray<unsigned long>::operator[](unsigned long)
1038
  const T &operator[](size_t i) const { return data_[i]; }
1039
1040
  T *begin() { return data_; }
1041
  T *end() { return data_ + size_; }
1042
  const T *begin() const { return data_; }
1043
  const T *end() const { return data_ + size_; }
1044
1045
  /// Allocate `count` elements from `arena`.  Previous contents are abandoned.
1046
  /// Elements are zero-initialized via memset (T must be trivially copyable).
1047
  /// Returns false if allocation fails (only possible when exceptions are
1048
  /// disabled via TINYOBJLOADER_ENABLE_EXCEPTION not being defined).
1049
0
  bool allocate(ArenaAllocator &arena, size_t count) {
1050
0
    if (count == 0) {
1051
0
      data_ = nullptr;
1052
0
      size_ = 0;
1053
0
      return true;
1054
0
    }
1055
    // Guard against size_t overflow in count * sizeof(T).
1056
0
    if (count > SIZE_MAX / sizeof(T)) {
1057
#ifdef TINYOBJLOADER_ENABLE_EXCEPTION
1058
      throw std::bad_alloc();
1059
#else
1060
0
      data_ = nullptr;
1061
0
      size_ = 0;
1062
0
      return false;
1063
0
#endif
1064
0
    }
1065
0
    void *p = arena.allocate(count * sizeof(T), alignof(T));
1066
0
    if (!p) {
1067
0
      data_ = nullptr;
1068
0
      size_ = 0;
1069
0
      return false;
1070
0
    }
1071
0
    data_ = static_cast<T *>(p);
1072
0
    size_ = count;
1073
0
    std::memset(data_, 0, count * sizeof(T));
1074
0
    return true;
1075
0
  }
Unexecuted instantiation: tinyobj::TypedArray<float>::allocate(tinyobj::ArenaAllocator&, unsigned long)
Unexecuted instantiation: tinyobj::TypedArray<tinyobj::index_t>::allocate(tinyobj::ArenaAllocator&, unsigned long)
Unexecuted instantiation: tinyobj::TypedArray<int>::allocate(tinyobj::ArenaAllocator&, unsigned long)
Unexecuted instantiation: tinyobj::TypedArray<unsigned int>::allocate(tinyobj::ArenaAllocator&, unsigned long)
Unexecuted instantiation: tinyobj::TypedArray<unsigned long>::allocate(tinyobj::ArenaAllocator&, unsigned long)
1076
1077
  /// Wrap an existing arena-allocated pointer.  Caller must ensure ptr
1078
  /// points into a live arena and count is within bounds.
1079
  void set(T *ptr, size_t count) {
1080
    data_ = ptr;
1081
    size_ = count;
1082
  }
1083
1084
  /// Shrink the logical size (no memory freed — arena owns it).
1085
0
  void truncate(size_t new_size) {
1086
0
    if (new_size < size_) size_ = new_size;
1087
0
  }
Unexecuted instantiation: tinyobj::TypedArray<tinyobj::index_t>::truncate(unsigned long)
Unexecuted instantiation: tinyobj::TypedArray<int>::truncate(unsigned long)
Unexecuted instantiation: tinyobj::TypedArray<unsigned int>::truncate(unsigned long)
1088
1089
  void clear() {
1090
    data_ = nullptr;
1091
    size_ = 0;
1092
  }
1093
1094
 private:
1095
  T *data_;
1096
  size_t size_;
1097
};
1098
1099
///
1100
/// A shape expressed as a range into the flat attrib arrays.
1101
/// No owned memory — just offsets + counts.
1102
///
1103
struct OptShapeRange {
1104
  std::string name;
1105
  size_t face_offset;       ///< Start index into OptAttrib::face_num_verts / material_ids
1106
  size_t face_count;        ///< Number of faces in this shape
1107
  size_t index_offset;      ///< Start index into OptAttrib::indices
1108
  size_t index_count;       ///< Number of index_t entries for this shape
1109
1110
  OptShapeRange()
1111
0
      : face_offset(0), face_count(0), index_offset(0), index_count(0) {}
1112
};
1113
1114
///
1115
/// Flat attribute storage using TypedArray (arena-backed).
1116
/// All arrays are allocated from the OptResult's arena.
1117
///
1118
/// Unlike basic_attrib_t (used by LoadObjOpt), optional arrays like
1119
/// vertex_weights, texcoord_ws, colors, and smoothing_group_ids are only
1120
/// allocated when the input actually contains the corresponding data.
1121
/// Check .empty() before accessing.
1122
///
1123
struct OptAttrib {
1124
  TypedArray<real_t> vertices;        ///< xyz, length = num_vertices * 3
1125
  TypedArray<real_t> vertex_weights;  ///< w per vertex (empty if no `v` line has w)
1126
  TypedArray<real_t> normals;         ///< xyz, length = num_normals * 3
1127
  TypedArray<real_t> texcoords;       ///< uv, length = num_texcoords * 2
1128
  TypedArray<real_t> texcoord_ws;     ///< w per texcoord (empty if no `vt` has w)
1129
  TypedArray<real_t> colors;          ///< rgb per vertex (empty if not all verts have color)
1130
1131
  TypedArray<index_t> indices;        ///< flattened face indices
1132
  TypedArray<int> face_num_verts;     ///< vertices per face
1133
  TypedArray<int> material_ids;       ///< per-face material id
1134
  TypedArray<unsigned int> smoothing_group_ids; ///< per-face smoothing group
1135
};
1136
1137
///
1138
/// Complete result from the TypedArray-based optimized loader.
1139
/// Owns the ArenaAllocator — all TypedArray pointers are valid as long
1140
/// as this object is alive.  Move-only.
1141
///
1142
struct OptResult {
1143
  ArenaAllocator arena;
1144
  OptAttrib attrib;
1145
  std::vector<OptShapeRange> shapes;  ///< views into attrib arrays
1146
  std::vector<material_t> materials;
1147
  bool valid;
1148
1149
0
  OptResult() : arena(4 * 1024 * 1024), valid(false) {}
1150
1151
  OptResult(OptResult &&o) noexcept
1152
      : arena(std::move(o.arena)),
1153
        attrib(o.attrib),
1154
        shapes(std::move(o.shapes)),
1155
        materials(std::move(o.materials)),
1156
0
        valid(o.valid) {
1157
0
    o.attrib = OptAttrib();  // clear dangling pointers
1158
0
    o.valid = false;
1159
0
  }
1160
1161
0
  OptResult &operator=(OptResult &&o) noexcept {
1162
0
    if (this != &o) {
1163
0
      arena = std::move(o.arena);
1164
0
      attrib = o.attrib;
1165
0
      shapes = std::move(o.shapes);
1166
0
      materials = std::move(o.materials);
1167
0
      valid = o.valid;
1168
0
      o.attrib = OptAttrib();
1169
0
      o.valid = false;
1170
0
    }
1171
0
    return *this;
1172
0
  }
1173
1174
  OptResult(const OptResult &) = delete;
1175
  OptResult &operator=(const OptResult &) = delete;
1176
};
1177
1178
/// TypedArray-based optimized loader — parse from a raw memory buffer.
1179
/// Returns OptResult that owns all allocated memory via its arena.
1180
OptResult LoadObjOptTyped(const char *buf, size_t buf_len,
1181
                          std::string *warn, std::string *err,
1182
                          const OptLoadConfig &config = OptLoadConfig());
1183
1184
/// TypedArray-based optimized loader — load from a file.
1185
OptResult LoadObjOptTyped(const char *filename,
1186
                          std::string *warn, std::string *err,
1187
                          const char *mtl_basedir = nullptr,
1188
                          const OptLoadConfig &config = OptLoadConfig());
1189
1190
/// =<<========== Optimized API =============================================
1191
1192
}  // namespace tinyobj
1193
1194
#endif  // TINY_OBJ_LOADER_H_
1195
1196
// Guard the implementation against double inclusion within a single
1197
// translation unit (e.g. when another header that also `#include`s this file
1198
// is pulled in after TINYOBJLOADER_IMPLEMENTATION is defined).
1199
#if defined(TINYOBJLOADER_IMPLEMENTATION) && \
1200
    !defined(TINYOBJLOADER_IMPLEMENTATION_DEFINED)
1201
#define TINYOBJLOADER_IMPLEMENTATION_DEFINED
1202
#include <cassert>
1203
#include <cctype>
1204
#include <climits>
1205
#include <cmath>
1206
#include <cstddef>
1207
#include <cstdint>
1208
#include <cerrno>
1209
#include <cstdlib>
1210
#include <cstring>
1211
#include <fstream>
1212
#include <limits>
1213
1214
#ifdef _WIN32
1215
#ifndef WIN32_LEAN_AND_MEAN
1216
#define WIN32_LEAN_AND_MEAN
1217
#endif
1218
#ifndef NOMINMAX
1219
#define NOMINMAX
1220
#endif
1221
#include <windows.h>
1222
#endif
1223
1224
#ifdef TINYOBJLOADER_USE_MMAP
1225
#if !defined(_WIN32)
1226
// POSIX headers for mmap
1227
#include <fcntl.h>
1228
#include <sys/mman.h>
1229
#include <sys/stat.h>
1230
#include <unistd.h>
1231
#endif
1232
#endif  // TINYOBJLOADER_USE_MMAP
1233
#include <set>
1234
#include <sstream>
1235
#include <utility>
1236
1237
#ifdef TINYOBJLOADER_USE_MAPBOX_EARCUT
1238
1239
#ifdef TINYOBJLOADER_DONOT_INCLUDE_MAPBOX_EARCUT
1240
// Assume earcut.hpp is included outside of tiny_obj_loader.h
1241
#else
1242
1243
#ifdef __clang__
1244
#pragma clang diagnostic push
1245
#pragma clang diagnostic ignored "-Weverything"
1246
#endif
1247
1248
#include <array>
1249
1250
#include "mapbox/earcut.hpp"
1251
1252
#ifdef __clang__
1253
#pragma clang diagnostic pop
1254
#endif
1255
1256
#endif
1257
1258
#endif  // TINYOBJLOADER_USE_MAPBOX_EARCUT
1259
1260
#ifdef _WIN32
1261
// Converts a UTF-8 encoded string to a UTF-16 wide string for use with
1262
// Windows file APIs that support Unicode paths (including paths longer than
1263
// MAX_PATH when combined with the extended-length path prefix).
1264
static std::wstring UTF8ToWchar(const std::string &str) {
1265
  if (str.empty()) return std::wstring();
1266
  int size_needed =
1267
      MultiByteToWideChar(CP_UTF8, 0, str.c_str(),
1268
                          static_cast<int>(str.size()), NULL, 0);
1269
  if (size_needed == 0) return std::wstring();
1270
  std::wstring wstr(static_cast<size_t>(size_needed), L'\0');
1271
  int result =
1272
      MultiByteToWideChar(CP_UTF8, 0, str.c_str(),
1273
                          static_cast<int>(str.size()), &wstr[0], size_needed);
1274
  if (result == 0) return std::wstring();
1275
  return wstr;
1276
}
1277
1278
// Prepends the Windows extended-length path prefix ("\\?\") to an absolute
1279
// path when the path length meets or exceeds MAX_PATH (260 characters).
1280
// This allows Windows APIs to handle paths up to 32767 characters long.
1281
// UNC paths (starting with "\\") are converted to "\\?\UNC\" form.
1282
static std::wstring LongPathW(const std::wstring &wpath) {
1283
  const std::wstring kLongPathPrefix = L"\\\\?\\";
1284
  const std::wstring kUNCPrefix = L"\\\\";
1285
  const std::wstring kLongUNCPathPrefix = L"\\\\?\\UNC\\";
1286
1287
  // Already has the extended-length prefix; return as-is.
1288
  if (wpath.size() >= kLongPathPrefix.size() &&
1289
      wpath.substr(0, kLongPathPrefix.size()) == kLongPathPrefix) {
1290
    return wpath;
1291
  }
1292
1293
  // Only add the prefix when the path is long enough to require it.
1294
  if (wpath.size() < MAX_PATH) {
1295
    return wpath;
1296
  }
1297
1298
  // Normalize forward slashes to backslashes: the extended-length "\\?\"
1299
  // prefix requires backslash separators only.
1300
  std::wstring normalized = wpath;
1301
  for (std::wstring::size_type i = 0; i < normalized.size(); ++i) {
1302
    if (normalized[i] == L'/') normalized[i] = L'\\';
1303
  }
1304
1305
  // UNC path: "\\server\share\..." -> "\\?\UNC\server\share\..."
1306
  if (normalized.size() >= kUNCPrefix.size() &&
1307
      normalized.substr(0, kUNCPrefix.size()) == kUNCPrefix) {
1308
    return kLongUNCPathPrefix + normalized.substr(kUNCPrefix.size());
1309
  }
1310
1311
  // Absolute path with drive letter: "C:\..." -> "\\?\C:\..."
1312
  if (normalized.size() >= 2 && normalized[1] == L':') {
1313
    return kLongPathPrefix + normalized;
1314
  }
1315
1316
  return normalized;
1317
}
1318
#endif  // _WIN32
1319
1320
#ifdef TINYOBJLOADER_USE_MULTITHREADING
1321
#include <atomic>
1322
#include <thread>
1323
#endif
1324
#ifdef TINYOBJLOADER_USE_SIMD
1325
#if defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) || \
1326
    (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
1327
#define TINYOBJLOADER_SIMD_SSE2 1
1328
#include <emmintrin.h>
1329
#if defined(__AVX2__)
1330
#define TINYOBJLOADER_SIMD_AVX2 1
1331
#include <immintrin.h>
1332
#endif
1333
#elif defined(__ARM_NEON) || defined(__ARM_NEON__)
1334
#define TINYOBJLOADER_SIMD_NEON 1
1335
#include <arm_neon.h>
1336
#endif
1337
#endif  // TINYOBJLOADER_USE_SIMD
1338
1339
// --------------------------------------------------------------------------
1340
// Embedded fast_float v8.0.2 for high-performance, bit-exact float parsing.
1341
// Disable by defining TINYOBJLOADER_DISABLE_FAST_FLOAT before including
1342
// this file with TINYOBJLOADER_IMPLEMENTATION.
1343
// --------------------------------------------------------------------------
1344
#ifndef TINYOBJLOADER_DISABLE_FAST_FLOAT
1345
1346
// Standard headers needed by the embedded fast_float.
1347
#include <cfloat>
1348
#include <cstdint>
1349
1350
namespace tinyobj_ff {
1351
1352
// --- integral_constant, true_type, false_type ---
1353
template <typename T, T V>
1354
struct integral_constant {
1355
  static const T value = V;
1356
  typedef T value_type;
1357
  typedef integral_constant type;
1358
  operator value_type() const { return value; }
1359
};
1360
typedef integral_constant<bool, true>  true_type;
1361
typedef integral_constant<bool, false> false_type;
1362
1363
// --- is_same ---
1364
template <typename T, typename U> struct is_same       : false_type {};
1365
template <typename T>             struct is_same<T, T> : true_type  {};
1366
1367
// --- enable_if ---
1368
template <bool B, typename T = void> struct enable_if {};
1369
template <typename T>                struct enable_if<true, T> { typedef T type; };
1370
1371
// --- conditional ---
1372
template <bool B, typename T, typename F> struct conditional              { typedef T type; };
1373
template <typename T, typename F>         struct conditional<false, T, F> { typedef F type; };
1374
1375
// --- is_integral ---
1376
template <typename T> struct is_integral : false_type {};
1377
template <> struct is_integral<bool>               : true_type {};
1378
template <> struct is_integral<char>               : true_type {};
1379
template <> struct is_integral<signed char>        : true_type {};
1380
template <> struct is_integral<unsigned char>      : true_type {};
1381
template <> struct is_integral<short>              : true_type {};
1382
template <> struct is_integral<unsigned short>     : true_type {};
1383
template <> struct is_integral<int>                : true_type {};
1384
template <> struct is_integral<unsigned int>       : true_type {};
1385
template <> struct is_integral<long>               : true_type {};
1386
template <> struct is_integral<unsigned long>      : true_type {};
1387
template <> struct is_integral<long long>          : true_type {};
1388
template <> struct is_integral<unsigned long long> : true_type {};
1389
template <> struct is_integral<wchar_t>            : true_type {};
1390
template <> struct is_integral<char16_t>           : true_type {};
1391
template <> struct is_integral<char32_t>           : true_type {};
1392
1393
// --- is_signed ---
1394
template <typename T> struct is_signed : integral_constant<bool, T(-1) < T(0)> {};
1395
1396
// --- underlying_type (uses compiler builtin) ---
1397
template <typename T> struct underlying_type {
1398
  typedef __underlying_type(T) type;
1399
};
1400
1401
// --- ff_errc (replaces std::errc, our own enum - no system_error needed) ---
1402
enum class ff_errc { ok = 0, invalid_argument = 22, result_out_of_range = 34 };
1403
1404
// --- min_val (replaces std::min, avoids Windows min/max macro conflicts) ---
1405
template <typename T>
1406
4.19k
inline T min_val(T a, T b) { return (b < a) ? b : a; }
1407
1408
// --- copy_n ---
1409
template <typename InputIt, typename Size, typename OutputIt>
1410
243k
inline OutputIt copy_n(InputIt first, Size count, OutputIt result) {
1411
2.72M
  for (Size i = 0; i < count; ++i) *result++ = *first++;
1412
243k
  return result;
1413
243k
}
1414
1415
// --- copy_backward ---
1416
template <typename BidirIt1, typename BidirIt2>
1417
20.4k
inline BidirIt2 copy_backward(BidirIt1 first, BidirIt1 last, BidirIt2 d_last) {
1418
313k
  while (first != last) *(--d_last) = *(--last);
1419
20.4k
  return d_last;
1420
20.4k
}
1421
1422
// --- fill ---
1423
template <typename ForwardIt, typename T>
1424
213k
inline void fill(ForwardIt first, ForwardIt last, const T &value) {
1425
529k
  for (; first != last; ++first) *first = value;
1426
213k
}
void tinyobj_ff::fill<unsigned long*, unsigned long>(unsigned long*, unsigned long*, unsigned long const&)
Line
Count
Source
1424
193k
inline void fill(ForwardIt first, ForwardIt last, const T &value) {
1425
388k
  for (; first != last; ++first) *first = value;
1426
193k
}
void tinyobj_ff::fill<unsigned long*, int>(unsigned long*, unsigned long*, int const&)
Line
Count
Source
1424
20.4k
inline void fill(ForwardIt first, ForwardIt last, const T &value) {
1425
141k
  for (; first != last; ++first) *first = value;
1426
20.4k
}
1427
1428
// --- distance ---
1429
template <typename It>
1430
inline typename conditional<true, long long, It>::type
1431
2.40M
distance(It first, It last) {
1432
2.40M
  return last - first;
1433
2.40M
}
1434
1435
}  // namespace tinyobj_ff
1436
1437
// --- Begin embedded fast_float v8.0.2 (MIT / Apache-2.0 / BSL-1.0) ---
1438
// https://github.com/fastfloat/fast_float
1439
// fast_float by Daniel Lemire
1440
// fast_float by João Paulo Magalhaes
1441
//
1442
//
1443
// with contributions from Eugene Golushkov
1444
// with contributions from Maksim Kita
1445
// with contributions from Marcin Wojdyr
1446
// with contributions from Neal Richardson
1447
// with contributions from Tim Paine
1448
// with contributions from Fabio Pellacini
1449
// with contributions from Lénárd Szolnoki
1450
// with contributions from Jan Pharago
1451
// with contributions from Maya Warrier
1452
// with contributions from Taha Khokhar
1453
// with contributions from Anders Dalvander
1454
//
1455
//
1456
// Licensed under the Apache License, Version 2.0, or the
1457
// MIT License or the Boost License. This file may not be copied,
1458
// modified, or distributed except according to those terms.
1459
//
1460
// MIT License Notice
1461
//
1462
//    MIT License
1463
//
1464
//    Copyright (c) 2021 The fast_float authors
1465
//
1466
//    Permission is hereby granted, free of charge, to any
1467
//    person obtaining a copy of this software and associated
1468
//    documentation files (the "Software"), to deal in the
1469
//    Software without restriction, including without
1470
//    limitation the rights to use, copy, modify, merge,
1471
//    publish, distribute, sublicense, and/or sell copies of
1472
//    the Software, and to permit persons to whom the Software
1473
//    is furnished to do so, subject to the following
1474
//    conditions:
1475
//
1476
//    The above copyright notice and this permission notice
1477
//    shall be included in all copies or substantial portions
1478
//    of the Software.
1479
//
1480
//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
1481
//    ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
1482
//    TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
1483
//    PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
1484
//    SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
1485
//    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
1486
//    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
1487
//    IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1488
//    DEALINGS IN THE SOFTWARE.
1489
//
1490
// Apache License (Version 2.0) Notice
1491
//
1492
//    Copyright 2021 The fast_float authors
1493
//    Licensed under the Apache License, Version 2.0 (the "License");
1494
//    you may not use this file except in compliance with the License.
1495
//    You may obtain a copy of the License at
1496
//
1497
//    http://www.apache.org/licenses/LICENSE-2.0
1498
//
1499
//    Unless required by applicable law or agreed to in writing, software
1500
//    distributed under the License is distributed on an "AS IS" BASIS,
1501
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1502
//    See the License for the specific language governing permissions and
1503
//
1504
// BOOST License Notice
1505
//
1506
//    Boost Software License - Version 1.0 - August 17th, 2003
1507
//
1508
//    Permission is hereby granted, free of charge, to any person or organization
1509
//    obtaining a copy of the software and accompanying documentation covered by
1510
//    this license (the "Software") to use, reproduce, display, distribute,
1511
//    execute, and transmit the Software, and to prepare derivative works of the
1512
//    Software, and to permit third-parties to whom the Software is furnished to
1513
//    do so, all subject to the following:
1514
//
1515
//    The copyright notices in the Software and this entire statement, including
1516
//    the above license grant, this restriction and the following disclaimer,
1517
//    must be included in all copies of the Software, in whole or in part, and
1518
//    all derivative works of the Software, unless such copies or derivative
1519
//    works are solely in the form of machine-executable object code generated by
1520
//    a source language processor.
1521
//
1522
//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1523
//    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1524
//    FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
1525
//    SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
1526
//    FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
1527
//    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1528
//    DEALINGS IN THE SOFTWARE.
1529
//
1530
1531
#ifndef FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
1532
#define FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
1533
1534
#ifdef __has_include
1535
#if __has_include(<version>)
1536
#include <version>
1537
#endif
1538
#endif
1539
1540
// Testing for https://wg21.link/N3652, adopted in C++14
1541
#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
1542
#define FASTFLOAT_CONSTEXPR14 constexpr
1543
#else
1544
#define FASTFLOAT_CONSTEXPR14
1545
#endif
1546
1547
#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
1548
#define FASTFLOAT_HAS_BIT_CAST 1
1549
#else
1550
#define FASTFLOAT_HAS_BIT_CAST 0
1551
#endif
1552
1553
#if defined(__cpp_lib_is_constant_evaluated) &&                                \
1554
    __cpp_lib_is_constant_evaluated >= 201811L
1555
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1
1556
#else
1557
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
1558
#endif
1559
1560
#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
1561
#define FASTFLOAT_IF_CONSTEXPR17(x) if constexpr (x)
1562
#else
1563
3.71M
#define FASTFLOAT_IF_CONSTEXPR17(x) if (x)
1564
#endif
1565
1566
// Testing for relevant C++20 constexpr library features
1567
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST &&           \
1568
    defined(__cpp_lib_constexpr_algorithms) &&                                 \
1569
    __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
1570
#define FASTFLOAT_CONSTEXPR20 constexpr
1571
#define FASTFLOAT_IS_CONSTEXPR 1
1572
#else
1573
#define FASTFLOAT_CONSTEXPR20
1574
#define FASTFLOAT_IS_CONSTEXPR 0
1575
#endif
1576
1577
#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
1578
#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 0
1579
#else
1580
#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 1
1581
#endif
1582
1583
#endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
1584
1585
#ifndef FASTFLOAT_FLOAT_COMMON_H
1586
#define FASTFLOAT_FLOAT_COMMON_H
1587
1588
#include <cassert>
1589
#include <cstring>
1590
#include <limits>
1591
#ifdef __has_include
1592
#if __has_include(<stdfloat>) && (__cplusplus > 202002L || (defined(_MSVC_LANG) && (_MSVC_LANG > 202002L)))
1593
#include <stdfloat>
1594
#endif
1595
#endif
1596
1597
#define FASTFLOAT_VERSION_MAJOR 8
1598
#define FASTFLOAT_VERSION_MINOR 0
1599
#define FASTFLOAT_VERSION_PATCH 2
1600
1601
#define FASTFLOAT_STRINGIZE_IMPL(x) #x
1602
#define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x)
1603
1604
#define FASTFLOAT_VERSION_STR                                                  \
1605
  FASTFLOAT_STRINGIZE(FASTFLOAT_VERSION_MAJOR)                                 \
1606
  "." FASTFLOAT_STRINGIZE(FASTFLOAT_VERSION_MINOR) "." FASTFLOAT_STRINGIZE(    \
1607
      FASTFLOAT_VERSION_PATCH)
1608
1609
#define FASTFLOAT_VERSION                                                      \
1610
  (FASTFLOAT_VERSION_MAJOR * 10000 + FASTFLOAT_VERSION_MINOR * 100 +           \
1611
   FASTFLOAT_VERSION_PATCH)
1612
1613
namespace fast_float {
1614
1615
enum class chars_format : uint64_t;
1616
1617
namespace detail {
1618
constexpr chars_format basic_json_fmt = chars_format(1 << 5);
1619
constexpr chars_format basic_fortran_fmt = chars_format(1 << 6);
1620
} // namespace detail
1621
1622
enum class chars_format : uint64_t {
1623
  scientific = 1 << 0,
1624
  fixed = 1 << 2,
1625
  hex = 1 << 3,
1626
  no_infnan = 1 << 4,
1627
  // RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6
1628
  json = uint64_t(detail::basic_json_fmt) | fixed | scientific | no_infnan,
1629
  // Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed.
1630
  json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific,
1631
  fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific,
1632
  general = fixed | scientific,
1633
  allow_leading_plus = 1 << 7,
1634
  skip_white_space = 1 << 8,
1635
};
1636
1637
template <typename UC> struct from_chars_result_t {
1638
  UC const *ptr;
1639
  tinyobj_ff::ff_errc ec;
1640
};
1641
1642
using from_chars_result = from_chars_result_t<char>;
1643
1644
template <typename UC> struct parse_options_t {
1645
  constexpr explicit parse_options_t(chars_format fmt = chars_format::general,
1646
                                     UC dot = UC('.'), int b = 10)
1647
1.87M
      : format(fmt), decimal_point(dot), base(b) {}
1648
1649
  /** Which number formats are accepted */
1650
  chars_format format;
1651
  /** The character used as decimal point */
1652
  UC decimal_point;
1653
  /** The base used for integers */
1654
  int base;
1655
};
1656
1657
using parse_options = parse_options_t<char>;
1658
1659
} // namespace fast_float
1660
1661
#if FASTFLOAT_HAS_BIT_CAST
1662
#include <bit>
1663
#endif
1664
1665
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) ||            \
1666
     defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) ||          \
1667
     defined(__MINGW64__) || defined(__s390x__) ||                             \
1668
     (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) ||      \
1669
      defined(__PPC64LE__)) ||                                                 \
1670
     defined(__loongarch64))
1671
#define FASTFLOAT_64BIT 1
1672
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) ||             \
1673
       defined(__arm__) || defined(_M_ARM) || defined(__ppc__) ||              \
1674
       defined(__MINGW32__) || defined(__EMSCRIPTEN__))
1675
#define FASTFLOAT_32BIT 1
1676
#else
1677
  // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
1678
// We can never tell the register width, but the SIZE_MAX is a good
1679
// approximation. UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max
1680
// portability.
1681
#if SIZE_MAX == 0xffff
1682
#error Unknown platform (16-bit, unsupported)
1683
#elif SIZE_MAX == 0xffffffff
1684
#define FASTFLOAT_32BIT 1
1685
#elif SIZE_MAX == 0xffffffffffffffff
1686
#define FASTFLOAT_64BIT 1
1687
#else
1688
#error Unknown platform (not 32-bit, not 64-bit?)
1689
#endif
1690
#endif
1691
1692
#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) ||           \
1693
    (defined(_M_ARM64) && !defined(__MINGW32__))
1694
#include <intrin.h>
1695
#endif
1696
1697
#if defined(_MSC_VER) && !defined(__clang__)
1698
#define FASTFLOAT_VISUAL_STUDIO 1
1699
#endif
1700
1701
#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__
1702
#define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
1703
#elif defined _WIN32
1704
#define FASTFLOAT_IS_BIG_ENDIAN 0
1705
#else
1706
#if defined(__APPLE__) || defined(__FreeBSD__)
1707
#include <machine/endian.h>
1708
#elif defined(sun) || defined(__sun)
1709
#include <sys/byteorder.h>
1710
#elif defined(__MVS__)
1711
#include <sys/endian.h>
1712
#else
1713
#ifdef __has_include
1714
#if __has_include(<endian.h>)
1715
#include <endian.h>
1716
#endif //__has_include(<endian.h>)
1717
#endif //__has_include
1718
#endif
1719
#
1720
#ifndef __BYTE_ORDER__
1721
// safe choice
1722
#define FASTFLOAT_IS_BIG_ENDIAN 0
1723
#endif
1724
#
1725
#ifndef __ORDER_LITTLE_ENDIAN__
1726
// safe choice
1727
#define FASTFLOAT_IS_BIG_ENDIAN 0
1728
#endif
1729
#
1730
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1731
#define FASTFLOAT_IS_BIG_ENDIAN 0
1732
#else
1733
#define FASTFLOAT_IS_BIG_ENDIAN 1
1734
#endif
1735
#endif
1736
1737
#if defined(__SSE2__) || (defined(FASTFLOAT_VISUAL_STUDIO) &&                  \
1738
                          (defined(_M_AMD64) || defined(_M_X64) ||             \
1739
                           (defined(_M_IX86_FP) && _M_IX86_FP == 2)))
1740
#define FASTFLOAT_SSE2 1
1741
#endif
1742
1743
#if defined(__aarch64__) || defined(_M_ARM64)
1744
#define FASTFLOAT_NEON 1
1745
#endif
1746
1747
#if defined(FASTFLOAT_SSE2) || defined(FASTFLOAT_NEON)
1748
#define FASTFLOAT_HAS_SIMD 1
1749
#endif
1750
1751
#if defined(__GNUC__)
1752
// disable -Wcast-align=strict (GCC only)
1753
#define FASTFLOAT_SIMD_DISABLE_WARNINGS                                        \
1754
  _Pragma("GCC diagnostic push")                                               \
1755
      _Pragma("GCC diagnostic ignored \"-Wcast-align\"")
1756
#else
1757
#define FASTFLOAT_SIMD_DISABLE_WARNINGS
1758
#endif
1759
1760
#if defined(__GNUC__)
1761
#define FASTFLOAT_SIMD_RESTORE_WARNINGS _Pragma("GCC diagnostic pop")
1762
#else
1763
#define FASTFLOAT_SIMD_RESTORE_WARNINGS
1764
#endif
1765
1766
#ifdef FASTFLOAT_VISUAL_STUDIO
1767
#define fastfloat_really_inline __forceinline
1768
#else
1769
#define fastfloat_really_inline inline __attribute__((always_inline))
1770
#endif
1771
1772
#ifndef FASTFLOAT_ASSERT
1773
#define FASTFLOAT_ASSERT(x)                                                    \
1774
106k
  { ((void)(x)); }
1775
#endif
1776
1777
#ifndef FASTFLOAT_DEBUG_ASSERT
1778
#define FASTFLOAT_DEBUG_ASSERT(x)                                              \
1779
27.8M
  { ((void)(x)); }
1780
#endif
1781
1782
// rust style `try!()` macro, or `?` operator
1783
#define FASTFLOAT_TRY(x)                                                       \
1784
1.74M
  {                                                                            \
1785
1.74M
    if (!(x))                                                                  \
1786
1.74M
      return false;                                                            \
1787
1.74M
  }
1788
1789
#define FASTFLOAT_ENABLE_IF(...)                                               \
1790
  typename tinyobj_ff::enable_if<(__VA_ARGS__), int>::type
1791
1792
namespace fast_float {
1793
1794
0
fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() {
1795
0
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED
1796
0
  return std::is_constant_evaluated();
1797
0
#else
1798
0
  return false;
1799
0
#endif
1800
0
}
1801
1802
template <typename T>
1803
struct is_supported_float_type
1804
    : tinyobj_ff::integral_constant<
1805
          bool, tinyobj_ff::is_same<T, double>::value || tinyobj_ff::is_same<T, float>::value
1806
#ifdef __STDCPP_FLOAT64_T__
1807
                    || tinyobj_ff::is_same<T, std::float64_t>::value
1808
#endif
1809
#ifdef __STDCPP_FLOAT32_T__
1810
                    || tinyobj_ff::is_same<T, std::float32_t>::value
1811
#endif
1812
#ifdef __STDCPP_FLOAT16_T__
1813
                    || tinyobj_ff::is_same<T, std::float16_t>::value
1814
#endif
1815
#ifdef __STDCPP_BFLOAT16_T__
1816
                    || tinyobj_ff::is_same<T, std::bfloat16_t>::value
1817
#endif
1818
          > {
1819
};
1820
1821
template <typename T>
1822
using equiv_uint_t = typename tinyobj_ff::conditional<
1823
    sizeof(T) == 1, uint8_t,
1824
    typename tinyobj_ff::conditional<
1825
        sizeof(T) == 2, uint16_t,
1826
        typename tinyobj_ff::conditional<sizeof(T) == 4, uint32_t,
1827
                                  uint64_t>::type>::type>::type;
1828
1829
template <typename T> struct is_supported_integer_type : tinyobj_ff::is_integral<T> {};
1830
1831
template <typename UC>
1832
struct is_supported_char_type
1833
    : tinyobj_ff::integral_constant<bool, tinyobj_ff::is_same<UC, char>::value ||
1834
                                       tinyobj_ff::is_same<UC, wchar_t>::value ||
1835
                                       tinyobj_ff::is_same<UC, char16_t>::value ||
1836
                                       tinyobj_ff::is_same<UC, char32_t>::value
1837
#ifdef __cpp_char8_t
1838
                                       || tinyobj_ff::is_same<UC, char8_t>::value
1839
#endif
1840
                             > {
1841
};
1842
1843
// Compares two ASCII strings in a case insensitive manner.
1844
template <typename UC>
1845
inline FASTFLOAT_CONSTEXPR14 bool
1846
fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
1847
130k
                      size_t length) {
1848
156k
  for (size_t i = 0; i < length; ++i) {
1849
156k
    UC const actual = actual_mixedcase[i];
1850
156k
    if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) {
1851
130k
      return false;
1852
130k
    }
1853
156k
  }
1854
0
  return true;
1855
130k
}
1856
1857
#ifndef FLT_EVAL_METHOD
1858
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
1859
#endif
1860
1861
// a pointer and a length to a contiguous block of memory
1862
template <typename T> struct span {
1863
  T const *ptr;
1864
  size_t length;
1865
1866
2.24M
  constexpr span(T const *_ptr, size_t _length) : ptr(_ptr), length(_length) {}
fast_float::span<char const>::span(char const*, unsigned long)
Line
Count
Source
1866
1.91M
  constexpr span(T const *_ptr, size_t _length) : ptr(_ptr), length(_length) {}
fast_float::span<unsigned long>::span(unsigned long const*, unsigned long)
Line
Count
Source
1866
327k
  constexpr span(T const *_ptr, size_t _length) : ptr(_ptr), length(_length) {}
1867
1868
4.03M
  constexpr span() : ptr(nullptr), length(0) {}
1869
1870
3.99M
  constexpr size_t len() const noexcept { return length; }
fast_float::span<char const>::len() const
Line
Count
Source
1870
161k
  constexpr size_t len() const noexcept { return length; }
fast_float::span<unsigned long>::len() const
Line
Count
Source
1870
3.83M
  constexpr size_t len() const noexcept { return length; }
1871
1872
2.42M
  FASTFLOAT_CONSTEXPR14 const T &operator[](size_t index) const noexcept {
1873
2.42M
    FASTFLOAT_DEBUG_ASSERT(index < length);
1874
2.42M
    return ptr[index];
1875
2.42M
  }
1876
};
1877
1878
struct value128 {
1879
  uint64_t low;
1880
  uint64_t high;
1881
1882
0
  constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
1883
1884
209k
  constexpr value128() : low(0), high(0) {}
1885
};
1886
1887
/* Helper C++14 constexpr generic implementation of leading_zeroes */
1888
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int
1889
0
leading_zeroes_generic(uint64_t input_num, int last_bit = 0) {
1890
0
  if (input_num & uint64_t(0xffffffff00000000)) {
1891
0
    input_num >>= 32;
1892
0
    last_bit |= 32;
1893
0
  }
1894
0
  if (input_num & uint64_t(0xffff0000)) {
1895
0
    input_num >>= 16;
1896
0
    last_bit |= 16;
1897
0
  }
1898
0
  if (input_num & uint64_t(0xff00)) {
1899
0
    input_num >>= 8;
1900
0
    last_bit |= 8;
1901
0
  }
1902
0
  if (input_num & uint64_t(0xf0)) {
1903
0
    input_num >>= 4;
1904
0
    last_bit |= 4;
1905
0
  }
1906
0
  if (input_num & uint64_t(0xc)) {
1907
0
    input_num >>= 2;
1908
0
    last_bit |= 2;
1909
0
  }
1910
0
  if (input_num & uint64_t(0x2)) { /* input_num >>=  1; */
1911
0
    last_bit |= 1;
1912
0
  }
1913
0
  return 63 - last_bit;
1914
0
}
1915
1916
/* result might be undefined when input_num is zero */
1917
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 int
1918
196k
leading_zeroes(uint64_t input_num) {
1919
196k
  assert(input_num > 0);
1920
196k
  if (cpp20_and_in_constexpr()) {
1921
0
    return leading_zeroes_generic(input_num);
1922
0
  }
1923
#ifdef FASTFLOAT_VISUAL_STUDIO
1924
#if defined(_M_X64) || defined(_M_ARM64)
1925
  unsigned long leading_zero = 0;
1926
  // Search the mask data from most significant bit (MSB)
1927
  // to least significant bit (LSB) for a set bit (1).
1928
  _BitScanReverse64(&leading_zero, input_num);
1929
  return (int)(63 - leading_zero);
1930
#else
1931
  return leading_zeroes_generic(input_num);
1932
#endif
1933
#else
1934
196k
  return __builtin_clzll(input_num);
1935
196k
#endif
1936
196k
}
1937
1938
// slow emulation routine for 32-bit
1939
0
fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) {
1940
0
  return x * (uint64_t)y;
1941
0
}
1942
1943
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
1944
0
umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
1945
0
  uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
1946
0
  uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
1947
0
  uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
1948
0
  uint64_t adbc_carry = (uint64_t)(adbc < ad);
1949
0
  uint64_t lo = bd + (adbc << 32);
1950
0
  *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
1951
0
        (adbc_carry << 32) + (uint64_t)(lo < bd);
1952
0
  return lo;
1953
0
}
1954
1955
#ifdef FASTFLOAT_32BIT
1956
1957
// slow emulation routine for 32-bit
1958
#if !defined(__MINGW64__)
1959
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t _umul128(uint64_t ab,
1960
                                                                uint64_t cd,
1961
                                                                uint64_t *hi) {
1962
  return umul128_generic(ab, cd, hi);
1963
}
1964
#endif // !__MINGW64__
1965
1966
#endif // FASTFLOAT_32BIT
1967
1968
// compute 64-bit a*b
1969
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
1970
209k
full_multiplication(uint64_t a, uint64_t b) {
1971
209k
  if (cpp20_and_in_constexpr()) {
1972
0
    value128 answer;
1973
0
    answer.low = umul128_generic(a, b, &answer.high);
1974
0
    return answer;
1975
0
  }
1976
209k
  value128 answer;
1977
#if defined(_M_ARM64) && !defined(__MINGW32__)
1978
  // ARM64 has native support for 64-bit multiplications, no need to emulate
1979
  // But MinGW on ARM64 doesn't have native support for 64-bit multiplications
1980
  answer.high = __umulh(a, b);
1981
  answer.low = a * b;
1982
#elif defined(FASTFLOAT_32BIT) ||                                              \
1983
    (defined(_WIN64) && !defined(__clang__) && !defined(_M_ARM64))
1984
  answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64
1985
#elif defined(FASTFLOAT_64BIT) && defined(__SIZEOF_INT128__)
1986
  __uint128_t r = ((__uint128_t)a) * b;
1987
209k
  answer.low = uint64_t(r);
1988
209k
  answer.high = uint64_t(r >> 64);
1989
#else
1990
  answer.low = umul128_generic(a, b, &answer.high);
1991
#endif
1992
209k
  return answer;
1993
209k
}
1994
1995
struct adjusted_mantissa {
1996
  uint64_t mantissa{0};
1997
  int32_t power2{0}; // a negative value indicates an invalid result
1998
213k
  adjusted_mantissa() = default;
1999
2000
0
  constexpr bool operator==(adjusted_mantissa const &o) const {
2001
0
    return mantissa == o.mantissa && power2 == o.power2;
2002
0
  }
2003
2004
68.9k
  constexpr bool operator!=(adjusted_mantissa const &o) const {
2005
68.9k
    return mantissa != o.mantissa || power2 != o.power2;
2006
68.9k
  }
2007
};
2008
2009
// Bias so we can get the real exponent with an invalid adjusted_mantissa.
2010
constexpr static int32_t invalid_am_bias = -0x8000;
2011
2012
// used for binary_format_lookup_tables<T>::max_mantissa
2013
constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5;
2014
2015
template <typename T, typename U = void> struct binary_format_lookup_tables;
2016
2017
template <typename T> struct binary_format : binary_format_lookup_tables<T> {
2018
  using equiv_uint = equiv_uint_t<T>;
2019
2020
  static constexpr int mantissa_explicit_bits();
2021
  static constexpr int minimum_exponent();
2022
  static constexpr int infinite_power();
2023
  static constexpr int sign_index();
2024
  static constexpr int
2025
  min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
2026
  static constexpr int max_exponent_fast_path();
2027
  static constexpr int max_exponent_round_to_even();
2028
  static constexpr int min_exponent_round_to_even();
2029
  static constexpr uint64_t max_mantissa_fast_path(int64_t power);
2030
  static constexpr uint64_t
2031
  max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
2032
  static constexpr int largest_power_of_ten();
2033
  static constexpr int smallest_power_of_ten();
2034
  static constexpr T exact_power_of_ten(int64_t power);
2035
  static constexpr size_t max_digits();
2036
  static constexpr equiv_uint exponent_mask();
2037
  static constexpr equiv_uint mantissa_mask();
2038
  static constexpr equiv_uint hidden_bit_mask();
2039
};
2040
2041
template <typename U> struct binary_format_lookup_tables<double, U> {
2042
  static constexpr double powers_of_ten[] = {
2043
      1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,  1e8,  1e9,  1e10, 1e11,
2044
      1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
2045
2046
  // Largest integer value v so that (5**index * v) <= 1<<53.
2047
  // 0x20000000000000 == 1 << 53
2048
  static constexpr uint64_t max_mantissa[] = {
2049
      0x20000000000000,
2050
      0x20000000000000 / 5,
2051
      0x20000000000000 / (5 * 5),
2052
      0x20000000000000 / (5 * 5 * 5),
2053
      0x20000000000000 / (5 * 5 * 5 * 5),
2054
      0x20000000000000 / (constant_55555),
2055
      0x20000000000000 / (constant_55555 * 5),
2056
      0x20000000000000 / (constant_55555 * 5 * 5),
2057
      0x20000000000000 / (constant_55555 * 5 * 5 * 5),
2058
      0x20000000000000 / (constant_55555 * 5 * 5 * 5 * 5),
2059
      0x20000000000000 / (constant_55555 * constant_55555),
2060
      0x20000000000000 / (constant_55555 * constant_55555 * 5),
2061
      0x20000000000000 / (constant_55555 * constant_55555 * 5 * 5),
2062
      0x20000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5),
2063
      0x20000000000000 / (constant_55555 * constant_55555 * constant_55555),
2064
      0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5),
2065
      0x20000000000000 /
2066
          (constant_55555 * constant_55555 * constant_55555 * 5 * 5),
2067
      0x20000000000000 /
2068
          (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
2069
      0x20000000000000 /
2070
          (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5),
2071
      0x20000000000000 /
2072
          (constant_55555 * constant_55555 * constant_55555 * constant_55555),
2073
      0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
2074
                          constant_55555 * 5),
2075
      0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
2076
                          constant_55555 * 5 * 5),
2077
      0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
2078
                          constant_55555 * 5 * 5 * 5),
2079
      0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
2080
                          constant_55555 * 5 * 5 * 5 * 5)};
2081
};
2082
2083
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
2084
2085
template <typename U>
2086
constexpr double binary_format_lookup_tables<double, U>::powers_of_ten[];
2087
2088
template <typename U>
2089
constexpr uint64_t binary_format_lookup_tables<double, U>::max_mantissa[];
2090
2091
#endif
2092
2093
template <typename U> struct binary_format_lookup_tables<float, U> {
2094
  static constexpr float powers_of_ten[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
2095
                                            1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
2096
2097
  // Largest integer value v so that (5**index * v) <= 1<<24.
2098
  // 0x1000000 == 1<<24
2099
  static constexpr uint64_t max_mantissa[] = {
2100
      0x1000000,
2101
      0x1000000 / 5,
2102
      0x1000000 / (5 * 5),
2103
      0x1000000 / (5 * 5 * 5),
2104
      0x1000000 / (5 * 5 * 5 * 5),
2105
      0x1000000 / (constant_55555),
2106
      0x1000000 / (constant_55555 * 5),
2107
      0x1000000 / (constant_55555 * 5 * 5),
2108
      0x1000000 / (constant_55555 * 5 * 5 * 5),
2109
      0x1000000 / (constant_55555 * 5 * 5 * 5 * 5),
2110
      0x1000000 / (constant_55555 * constant_55555),
2111
      0x1000000 / (constant_55555 * constant_55555 * 5)};
2112
};
2113
2114
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
2115
2116
template <typename U>
2117
constexpr float binary_format_lookup_tables<float, U>::powers_of_ten[];
2118
2119
template <typename U>
2120
constexpr uint64_t binary_format_lookup_tables<float, U>::max_mantissa[];
2121
2122
#endif
2123
2124
template <>
2125
1.73M
inline constexpr int binary_format<double>::min_exponent_fast_path() {
2126
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
2127
  return 0;
2128
#else
2129
1.73M
  return -22;
2130
1.73M
#endif
2131
1.73M
}
2132
2133
template <>
2134
0
inline constexpr int binary_format<float>::min_exponent_fast_path() {
2135
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
2136
  return 0;
2137
#else
2138
0
  return -10;
2139
0
#endif
2140
0
}
2141
2142
template <>
2143
2.42M
inline constexpr int binary_format<double>::mantissa_explicit_bits() {
2144
2.42M
  return 52;
2145
2.42M
}
2146
2147
template <>
2148
0
inline constexpr int binary_format<float>::mantissa_explicit_bits() {
2149
0
  return 23;
2150
0
}
2151
2152
template <>
2153
17.5k
inline constexpr int binary_format<double>::max_exponent_round_to_even() {
2154
17.5k
  return 23;
2155
17.5k
}
2156
2157
template <>
2158
0
inline constexpr int binary_format<float>::max_exponent_round_to_even() {
2159
0
  return 10;
2160
0
}
2161
2162
template <>
2163
25.0k
inline constexpr int binary_format<double>::min_exponent_round_to_even() {
2164
25.0k
  return -4;
2165
25.0k
}
2166
2167
template <>
2168
0
inline constexpr int binary_format<float>::min_exponent_round_to_even() {
2169
0
  return -17;
2170
0
}
2171
2172
206k
template <> inline constexpr int binary_format<double>::minimum_exponent() {
2173
206k
  return -1023;
2174
206k
}
2175
2176
0
template <> inline constexpr int binary_format<float>::minimum_exponent() {
2177
0
  return -127;
2178
0
}
2179
2180
263k
template <> inline constexpr int binary_format<double>::infinite_power() {
2181
263k
  return 0x7FF;
2182
263k
}
2183
2184
0
template <> inline constexpr int binary_format<float>::infinite_power() {
2185
0
  return 0xFF;
2186
0
}
2187
2188
97.3k
template <> inline constexpr int binary_format<double>::sign_index() {
2189
97.3k
  return 63;
2190
97.3k
}
2191
2192
0
template <> inline constexpr int binary_format<float>::sign_index() {
2193
0
  return 31;
2194
0
}
2195
2196
template <>
2197
1.71M
inline constexpr int binary_format<double>::max_exponent_fast_path() {
2198
1.71M
  return 22;
2199
1.71M
}
2200
2201
template <>
2202
0
inline constexpr int binary_format<float>::max_exponent_fast_path() {
2203
0
  return 10;
2204
0
}
2205
2206
template <>
2207
1.66M
inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
2208
1.66M
  return uint64_t(2) << mantissa_explicit_bits();
2209
1.66M
}
2210
2211
template <>
2212
0
inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
2213
0
  return uint64_t(2) << mantissa_explicit_bits();
2214
0
}
2215
2216
// credit: Jakub Jelínek
2217
#ifdef __STDCPP_FLOAT16_T__
2218
template <typename U> struct binary_format_lookup_tables<std::float16_t, U> {
2219
  static constexpr std::float16_t powers_of_ten[] = {1e0f16, 1e1f16, 1e2f16,
2220
                                                     1e3f16, 1e4f16};
2221
2222
  // Largest integer value v so that (5**index * v) <= 1<<11.
2223
  // 0x800 == 1<<11
2224
  static constexpr uint64_t max_mantissa[] = {0x800,
2225
                                              0x800 / 5,
2226
                                              0x800 / (5 * 5),
2227
                                              0x800 / (5 * 5 * 5),
2228
                                              0x800 / (5 * 5 * 5 * 5),
2229
                                              0x800 / (constant_55555)};
2230
};
2231
2232
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
2233
2234
template <typename U>
2235
constexpr std::float16_t
2236
    binary_format_lookup_tables<std::float16_t, U>::powers_of_ten[];
2237
2238
template <typename U>
2239
constexpr uint64_t
2240
    binary_format_lookup_tables<std::float16_t, U>::max_mantissa[];
2241
2242
#endif
2243
2244
template <>
2245
inline constexpr std::float16_t
2246
binary_format<std::float16_t>::exact_power_of_ten(int64_t power) {
2247
  // Work around clang bug https://godbolt.org/z/zedh7rrhc
2248
  return (void)powers_of_ten[0], powers_of_ten[power];
2249
}
2250
2251
template <>
2252
inline constexpr binary_format<std::float16_t>::equiv_uint
2253
binary_format<std::float16_t>::exponent_mask() {
2254
  return 0x7C00;
2255
}
2256
2257
template <>
2258
inline constexpr binary_format<std::float16_t>::equiv_uint
2259
binary_format<std::float16_t>::mantissa_mask() {
2260
  return 0x03FF;
2261
}
2262
2263
template <>
2264
inline constexpr binary_format<std::float16_t>::equiv_uint
2265
binary_format<std::float16_t>::hidden_bit_mask() {
2266
  return 0x0400;
2267
}
2268
2269
template <>
2270
inline constexpr int binary_format<std::float16_t>::max_exponent_fast_path() {
2271
  return 4;
2272
}
2273
2274
template <>
2275
inline constexpr int binary_format<std::float16_t>::mantissa_explicit_bits() {
2276
  return 10;
2277
}
2278
2279
template <>
2280
inline constexpr uint64_t
2281
binary_format<std::float16_t>::max_mantissa_fast_path() {
2282
  return uint64_t(2) << mantissa_explicit_bits();
2283
}
2284
2285
template <>
2286
inline constexpr uint64_t
2287
binary_format<std::float16_t>::max_mantissa_fast_path(int64_t power) {
2288
  // caller is responsible to ensure that
2289
  // power >= 0 && power <= 4
2290
  //
2291
  // Work around clang bug https://godbolt.org/z/zedh7rrhc
2292
  return (void)max_mantissa[0], max_mantissa[power];
2293
}
2294
2295
template <>
2296
inline constexpr int binary_format<std::float16_t>::min_exponent_fast_path() {
2297
  return 0;
2298
}
2299
2300
template <>
2301
inline constexpr int
2302
binary_format<std::float16_t>::max_exponent_round_to_even() {
2303
  return 5;
2304
}
2305
2306
template <>
2307
inline constexpr int
2308
binary_format<std::float16_t>::min_exponent_round_to_even() {
2309
  return -22;
2310
}
2311
2312
template <>
2313
inline constexpr int binary_format<std::float16_t>::minimum_exponent() {
2314
  return -15;
2315
}
2316
2317
template <>
2318
inline constexpr int binary_format<std::float16_t>::infinite_power() {
2319
  return 0x1F;
2320
}
2321
2322
template <> inline constexpr int binary_format<std::float16_t>::sign_index() {
2323
  return 15;
2324
}
2325
2326
template <>
2327
inline constexpr int binary_format<std::float16_t>::largest_power_of_ten() {
2328
  return 4;
2329
}
2330
2331
template <>
2332
inline constexpr int binary_format<std::float16_t>::smallest_power_of_ten() {
2333
  return -27;
2334
}
2335
2336
template <>
2337
inline constexpr size_t binary_format<std::float16_t>::max_digits() {
2338
  return 22;
2339
}
2340
#endif // __STDCPP_FLOAT16_T__
2341
2342
// credit: Jakub Jelínek
2343
#ifdef __STDCPP_BFLOAT16_T__
2344
template <typename U> struct binary_format_lookup_tables<std::bfloat16_t, U> {
2345
  static constexpr std::bfloat16_t powers_of_ten[] = {1e0bf16, 1e1bf16, 1e2bf16,
2346
                                                      1e3bf16};
2347
2348
  // Largest integer value v so that (5**index * v) <= 1<<8.
2349
  // 0x100 == 1<<8
2350
  static constexpr uint64_t max_mantissa[] = {0x100, 0x100 / 5, 0x100 / (5 * 5),
2351
                                              0x100 / (5 * 5 * 5),
2352
                                              0x100 / (5 * 5 * 5 * 5)};
2353
};
2354
2355
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
2356
2357
template <typename U>
2358
constexpr std::bfloat16_t
2359
    binary_format_lookup_tables<std::bfloat16_t, U>::powers_of_ten[];
2360
2361
template <typename U>
2362
constexpr uint64_t
2363
    binary_format_lookup_tables<std::bfloat16_t, U>::max_mantissa[];
2364
2365
#endif
2366
2367
template <>
2368
inline constexpr std::bfloat16_t
2369
binary_format<std::bfloat16_t>::exact_power_of_ten(int64_t power) {
2370
  // Work around clang bug https://godbolt.org/z/zedh7rrhc
2371
  return (void)powers_of_ten[0], powers_of_ten[power];
2372
}
2373
2374
template <>
2375
inline constexpr int binary_format<std::bfloat16_t>::max_exponent_fast_path() {
2376
  return 3;
2377
}
2378
2379
template <>
2380
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
2381
binary_format<std::bfloat16_t>::exponent_mask() {
2382
  return 0x7F80;
2383
}
2384
2385
template <>
2386
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
2387
binary_format<std::bfloat16_t>::mantissa_mask() {
2388
  return 0x007F;
2389
}
2390
2391
template <>
2392
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
2393
binary_format<std::bfloat16_t>::hidden_bit_mask() {
2394
  return 0x0080;
2395
}
2396
2397
template <>
2398
inline constexpr int binary_format<std::bfloat16_t>::mantissa_explicit_bits() {
2399
  return 7;
2400
}
2401
2402
template <>
2403
inline constexpr uint64_t
2404
binary_format<std::bfloat16_t>::max_mantissa_fast_path() {
2405
  return uint64_t(2) << mantissa_explicit_bits();
2406
}
2407
2408
template <>
2409
inline constexpr uint64_t
2410
binary_format<std::bfloat16_t>::max_mantissa_fast_path(int64_t power) {
2411
  // caller is responsible to ensure that
2412
  // power >= 0 && power <= 3
2413
  //
2414
  // Work around clang bug https://godbolt.org/z/zedh7rrhc
2415
  return (void)max_mantissa[0], max_mantissa[power];
2416
}
2417
2418
template <>
2419
inline constexpr int binary_format<std::bfloat16_t>::min_exponent_fast_path() {
2420
  return 0;
2421
}
2422
2423
template <>
2424
inline constexpr int
2425
binary_format<std::bfloat16_t>::max_exponent_round_to_even() {
2426
  return 3;
2427
}
2428
2429
template <>
2430
inline constexpr int
2431
binary_format<std::bfloat16_t>::min_exponent_round_to_even() {
2432
  return -24;
2433
}
2434
2435
template <>
2436
inline constexpr int binary_format<std::bfloat16_t>::minimum_exponent() {
2437
  return -127;
2438
}
2439
2440
template <>
2441
inline constexpr int binary_format<std::bfloat16_t>::infinite_power() {
2442
  return 0xFF;
2443
}
2444
2445
template <> inline constexpr int binary_format<std::bfloat16_t>::sign_index() {
2446
  return 15;
2447
}
2448
2449
template <>
2450
inline constexpr int binary_format<std::bfloat16_t>::largest_power_of_ten() {
2451
  return 38;
2452
}
2453
2454
template <>
2455
inline constexpr int binary_format<std::bfloat16_t>::smallest_power_of_ten() {
2456
  return -60;
2457
}
2458
2459
template <>
2460
inline constexpr size_t binary_format<std::bfloat16_t>::max_digits() {
2461
  return 98;
2462
}
2463
#endif // __STDCPP_BFLOAT16_T__
2464
2465
template <>
2466
inline constexpr uint64_t
2467
0
binary_format<double>::max_mantissa_fast_path(int64_t power) {
2468
  // caller is responsible to ensure that
2469
  // power >= 0 && power <= 22
2470
  //
2471
  // Work around clang bug https://godbolt.org/z/zedh7rrhc
2472
0
  return (void)max_mantissa[0], max_mantissa[power];
2473
0
}
2474
2475
template <>
2476
inline constexpr uint64_t
2477
0
binary_format<float>::max_mantissa_fast_path(int64_t power) {
2478
  // caller is responsible to ensure that
2479
  // power >= 0 && power <= 10
2480
  //
2481
  // Work around clang bug https://godbolt.org/z/zedh7rrhc
2482
0
  return (void)max_mantissa[0], max_mantissa[power];
2483
0
}
2484
2485
template <>
2486
inline constexpr double
2487
1.65M
binary_format<double>::exact_power_of_ten(int64_t power) {
2488
  // Work around clang bug https://godbolt.org/z/zedh7rrhc
2489
1.65M
  return (void)powers_of_ten[0], powers_of_ten[power];
2490
1.65M
}
2491
2492
template <>
2493
0
inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
2494
  // Work around clang bug https://godbolt.org/z/zedh7rrhc
2495
0
  return (void)powers_of_ten[0], powers_of_ten[power];
2496
0
}
2497
2498
141k
template <> inline constexpr int binary_format<double>::largest_power_of_ten() {
2499
141k
  return 308;
2500
141k
}
2501
2502
0
template <> inline constexpr int binary_format<float>::largest_power_of_ten() {
2503
0
  return 38;
2504
0
}
2505
2506
template <>
2507
143k
inline constexpr int binary_format<double>::smallest_power_of_ten() {
2508
143k
  return -342;
2509
143k
}
2510
2511
0
template <> inline constexpr int binary_format<float>::smallest_power_of_ten() {
2512
0
  return -64;
2513
0
}
2514
2515
35.1k
template <> inline constexpr size_t binary_format<double>::max_digits() {
2516
35.1k
  return 769;
2517
35.1k
}
2518
2519
0
template <> inline constexpr size_t binary_format<float>::max_digits() {
2520
0
  return 114;
2521
0
}
2522
2523
template <>
2524
inline constexpr binary_format<float>::equiv_uint
2525
0
binary_format<float>::exponent_mask() {
2526
0
  return 0x7F800000;
2527
0
}
2528
2529
template <>
2530
inline constexpr binary_format<double>::equiv_uint
2531
0
binary_format<double>::exponent_mask() {
2532
0
  return 0x7FF0000000000000;
2533
0
}
2534
2535
template <>
2536
inline constexpr binary_format<float>::equiv_uint
2537
0
binary_format<float>::mantissa_mask() {
2538
0
  return 0x007FFFFF;
2539
0
}
2540
2541
template <>
2542
inline constexpr binary_format<double>::equiv_uint
2543
0
binary_format<double>::mantissa_mask() {
2544
0
  return 0x000FFFFFFFFFFFFF;
2545
0
}
2546
2547
template <>
2548
inline constexpr binary_format<float>::equiv_uint
2549
0
binary_format<float>::hidden_bit_mask() {
2550
0
  return 0x00800000;
2551
0
}
2552
2553
template <>
2554
inline constexpr binary_format<double>::equiv_uint
2555
0
binary_format<double>::hidden_bit_mask() {
2556
0
  return 0x0010000000000000;
2557
0
}
2558
2559
template <typename T>
2560
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
2561
97.3k
to_float(bool negative, adjusted_mantissa am, T &value) {
2562
97.3k
  using equiv_uint = equiv_uint_t<T>;
2563
97.3k
  equiv_uint word = equiv_uint(am.mantissa);
2564
97.3k
  word = equiv_uint(word | equiv_uint(am.power2)
2565
97.3k
                               << binary_format<T>::mantissa_explicit_bits());
2566
97.3k
  word =
2567
97.3k
      equiv_uint(word | equiv_uint(negative) << binary_format<T>::sign_index());
2568
#if FASTFLOAT_HAS_BIT_CAST
2569
  value = std::bit_cast<T>(word);
2570
#else
2571
97.3k
  ::memcpy(&value, &word, sizeof(T));
2572
97.3k
#endif
2573
97.3k
}
void fast_float::to_float<double>(bool, fast_float::adjusted_mantissa, double&)
Line
Count
Source
2561
97.3k
to_float(bool negative, adjusted_mantissa am, T &value) {
2562
97.3k
  using equiv_uint = equiv_uint_t<T>;
2563
97.3k
  equiv_uint word = equiv_uint(am.mantissa);
2564
97.3k
  word = equiv_uint(word | equiv_uint(am.power2)
2565
97.3k
                               << binary_format<T>::mantissa_explicit_bits());
2566
97.3k
  word =
2567
97.3k
      equiv_uint(word | equiv_uint(negative) << binary_format<T>::sign_index());
2568
#if FASTFLOAT_HAS_BIT_CAST
2569
  value = std::bit_cast<T>(word);
2570
#else
2571
97.3k
  ::memcpy(&value, &word, sizeof(T));
2572
97.3k
#endif
2573
97.3k
}
Unexecuted instantiation: void fast_float::to_float<float>(bool, fast_float::adjusted_mantissa, float&)
2574
2575
template <typename = void> struct space_lut {
2576
  static constexpr bool value[] = {
2577
      0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2578
      0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2579
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2580
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2581
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2582
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2583
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2584
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2585
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2586
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2587
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2588
};
2589
2590
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
2591
2592
template <typename T> constexpr bool space_lut<T>::value[];
2593
2594
#endif
2595
2596
0
template <typename UC> constexpr bool is_space(UC c) {
2597
0
  return c < 256 && space_lut<>::value[uint8_t(c)];
2598
0
}
2599
2600
224k
template <typename UC> static constexpr uint64_t int_cmp_zeros() {
2601
224k
  static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4),
2602
224k
                "Unsupported character size");
2603
224k
  return (sizeof(UC) == 1) ? 0x3030303030303030
2604
224k
         : (sizeof(UC) == 2)
2605
0
             ? (uint64_t(UC('0')) << 48 | uint64_t(UC('0')) << 32 |
2606
0
                uint64_t(UC('0')) << 16 | UC('0'))
2607
0
             : (uint64_t(UC('0')) << 32 | UC('0'));
2608
224k
}
2609
2610
436k
template <typename UC> static constexpr int int_cmp_len() {
2611
436k
  return sizeof(uint64_t) / sizeof(UC);
2612
436k
}
2613
2614
template <typename UC> constexpr UC const *str_const_nan();
2615
2616
65.1k
template <> constexpr char const *str_const_nan<char>() { return "nan"; }
2617
2618
0
template <> constexpr wchar_t const *str_const_nan<wchar_t>() { return L"nan"; }
2619
2620
0
template <> constexpr char16_t const *str_const_nan<char16_t>() {
2621
0
  return u"nan";
2622
0
}
2623
2624
0
template <> constexpr char32_t const *str_const_nan<char32_t>() {
2625
0
  return U"nan";
2626
0
}
2627
2628
#ifdef __cpp_char8_t
2629
template <> constexpr char8_t const *str_const_nan<char8_t>() {
2630
  return u8"nan";
2631
}
2632
#endif
2633
2634
template <typename UC> constexpr UC const *str_const_inf();
2635
2636
65.1k
template <> constexpr char const *str_const_inf<char>() { return "infinity"; }
2637
2638
0
template <> constexpr wchar_t const *str_const_inf<wchar_t>() {
2639
0
  return L"infinity";
2640
0
}
2641
2642
0
template <> constexpr char16_t const *str_const_inf<char16_t>() {
2643
0
  return u"infinity";
2644
0
}
2645
2646
0
template <> constexpr char32_t const *str_const_inf<char32_t>() {
2647
0
  return U"infinity";
2648
0
}
2649
2650
#ifdef __cpp_char8_t
2651
template <> constexpr char8_t const *str_const_inf<char8_t>() {
2652
  return u8"infinity";
2653
}
2654
#endif
2655
2656
template <typename = void> struct int_luts {
2657
  static constexpr uint8_t chdigit[] = {
2658
      255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2659
      255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2660
      255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2661
      255, 255, 255, 0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   255, 255,
2662
      255, 255, 255, 255, 255, 10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
2663
      20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,  32,  33,  34,
2664
      35,  255, 255, 255, 255, 255, 255, 10,  11,  12,  13,  14,  15,  16,  17,
2665
      18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,  32,
2666
      33,  34,  35,  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2667
      255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2668
      255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2669
      255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2670
      255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2671
      255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2672
      255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2673
      255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2674
      255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2675
      255};
2676
2677
  static constexpr size_t maxdigits_u64[] = {
2678
      64, 41, 32, 28, 25, 23, 22, 21, 20, 19, 18, 18, 17, 17, 16, 16, 16, 16,
2679
      15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13};
2680
2681
  static constexpr uint64_t min_safe_u64[] = {
2682
      9223372036854775808ull,  12157665459056928801ull, 4611686018427387904,
2683
      7450580596923828125,     4738381338321616896,     3909821048582988049,
2684
      9223372036854775808ull,  12157665459056928801ull, 10000000000000000000ull,
2685
      5559917313492231481,     2218611106740436992,     8650415919381337933,
2686
      2177953337809371136,     6568408355712890625,     1152921504606846976,
2687
      2862423051509815793,     6746640616477458432,     15181127029874798299ull,
2688
      1638400000000000000,     3243919932521508681,     6221821273427820544,
2689
      11592836324538749809ull, 876488338465357824,      1490116119384765625,
2690
      2481152873203736576,     4052555153018976267,     6502111422497947648,
2691
      10260628712958602189ull, 15943230000000000000ull, 787662783788549761,
2692
      1152921504606846976,     1667889514952984961,     2386420683693101056,
2693
      3379220508056640625,     4738381338321616896};
2694
};
2695
2696
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
2697
2698
template <typename T> constexpr uint8_t int_luts<T>::chdigit[];
2699
2700
template <typename T> constexpr size_t int_luts<T>::maxdigits_u64[];
2701
2702
template <typename T> constexpr uint64_t int_luts<T>::min_safe_u64[];
2703
2704
#endif
2705
2706
template <typename UC>
2707
fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) {
2708
  return int_luts<>::chdigit[static_cast<unsigned char>(c)];
2709
}
2710
2711
0
fastfloat_really_inline constexpr size_t max_digits_u64(int base) {
2712
0
  return int_luts<>::maxdigits_u64[base - 2];
2713
0
}
2714
2715
// If a u64 is exactly max_digits_u64() in length, this is
2716
// the value below which it has definitely overflowed.
2717
0
fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) {
2718
0
  return int_luts<>::min_safe_u64[base - 2];
2719
0
}
2720
2721
static_assert(tinyobj_ff::is_same<equiv_uint_t<double>, uint64_t>::value,
2722
              "equiv_uint should be uint64_t for double");
2723
static_assert(std::numeric_limits<double>::is_iec559,
2724
              "double must fulfill the requirements of IEC 559 (IEEE 754)");
2725
2726
static_assert(tinyobj_ff::is_same<equiv_uint_t<float>, uint32_t>::value,
2727
              "equiv_uint should be uint32_t for float");
2728
static_assert(std::numeric_limits<float>::is_iec559,
2729
              "float must fulfill the requirements of IEC 559 (IEEE 754)");
2730
2731
#ifdef __STDCPP_FLOAT64_T__
2732
static_assert(tinyobj_ff::is_same<equiv_uint_t<std::float64_t>, uint64_t>::value,
2733
              "equiv_uint should be uint64_t for std::float64_t");
2734
static_assert(
2735
    std::numeric_limits<std::float64_t>::is_iec559,
2736
    "std::float64_t must fulfill the requirements of IEC 559 (IEEE 754)");
2737
#endif // __STDCPP_FLOAT64_T__
2738
2739
#ifdef __STDCPP_FLOAT32_T__
2740
static_assert(tinyobj_ff::is_same<equiv_uint_t<std::float32_t>, uint32_t>::value,
2741
              "equiv_uint should be uint32_t for std::float32_t");
2742
static_assert(
2743
    std::numeric_limits<std::float32_t>::is_iec559,
2744
    "std::float32_t must fulfill the requirements of IEC 559 (IEEE 754)");
2745
#endif // __STDCPP_FLOAT32_T__
2746
2747
#ifdef __STDCPP_FLOAT16_T__
2748
static_assert(
2749
    tinyobj_ff::is_same<binary_format<std::float16_t>::equiv_uint, uint16_t>::value,
2750
    "equiv_uint should be uint16_t for std::float16_t");
2751
static_assert(
2752
    std::numeric_limits<std::float16_t>::is_iec559,
2753
    "std::float16_t must fulfill the requirements of IEC 559 (IEEE 754)");
2754
#endif // __STDCPP_FLOAT16_T__
2755
2756
#ifdef __STDCPP_BFLOAT16_T__
2757
static_assert(
2758
    tinyobj_ff::is_same<binary_format<std::bfloat16_t>::equiv_uint, uint16_t>::value,
2759
    "equiv_uint should be uint16_t for std::bfloat16_t");
2760
static_assert(
2761
    std::numeric_limits<std::bfloat16_t>::is_iec559,
2762
    "std::bfloat16_t must fulfill the requirements of IEC 559 (IEEE 754)");
2763
#endif // __STDCPP_BFLOAT16_T__
2764
2765
0
constexpr chars_format operator~(chars_format rhs) noexcept {
2766
0
  using int_type = tinyobj_ff::underlying_type<chars_format>::type;
2767
0
  return static_cast<chars_format>(~static_cast<int_type>(rhs));
2768
0
}
2769
2770
12.7M
constexpr chars_format operator&(chars_format lhs, chars_format rhs) noexcept {
2771
12.7M
  using int_type = tinyobj_ff::underlying_type<chars_format>::type;
2772
12.7M
  return static_cast<chars_format>(static_cast<int_type>(lhs) &
2773
12.7M
                                   static_cast<int_type>(rhs));
2774
12.7M
}
2775
2776
1.87M
constexpr chars_format operator|(chars_format lhs, chars_format rhs) noexcept {
2777
1.87M
  using int_type = tinyobj_ff::underlying_type<chars_format>::type;
2778
1.87M
  return static_cast<chars_format>(static_cast<int_type>(lhs) |
2779
1.87M
                                   static_cast<int_type>(rhs));
2780
1.87M
}
2781
2782
0
constexpr chars_format operator^(chars_format lhs, chars_format rhs) noexcept {
2783
0
  using int_type = tinyobj_ff::underlying_type<chars_format>::type;
2784
0
  return static_cast<chars_format>(static_cast<int_type>(lhs) ^
2785
0
                                   static_cast<int_type>(rhs));
2786
0
}
2787
2788
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
2789
0
operator&=(chars_format &lhs, chars_format rhs) noexcept {
2790
0
  return lhs = (lhs & rhs);
2791
0
}
2792
2793
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
2794
0
operator|=(chars_format &lhs, chars_format rhs) noexcept {
2795
0
  return lhs = (lhs | rhs);
2796
0
}
2797
2798
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
2799
0
operator^=(chars_format &lhs, chars_format rhs) noexcept {
2800
0
  return lhs = (lhs ^ rhs);
2801
0
}
2802
2803
namespace detail {
2804
// adjust for deprecated feature macros
2805
3.75M
constexpr chars_format adjust_for_feature_macros(chars_format fmt) {
2806
3.75M
  return fmt
2807
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS
2808
         | chars_format::allow_leading_plus
2809
#endif
2810
#ifdef FASTFLOAT_SKIP_WHITE_SPACE
2811
         | chars_format::skip_white_space
2812
#endif
2813
3.75M
      ;
2814
3.75M
}
2815
} // namespace detail
2816
2817
} // namespace fast_float
2818
2819
#endif
2820
2821
2822
#ifndef FASTFLOAT_FAST_FLOAT_H
2823
#define FASTFLOAT_FAST_FLOAT_H
2824
2825
2826
namespace fast_float {
2827
/**
2828
 * This function parses the character sequence [first,last) for a number. It
2829
 * parses floating-point numbers expecting a locale-indepent format equivalent
2830
 * to what is used by std::strtod in the default ("C") locale. The resulting
2831
 * floating-point value is the closest floating-point values (using either float
2832
 * or double), using the "round to even" convention for values that would
2833
 * otherwise fall right in-between two values. That is, we provide exact parsing
2834
 * according to the IEEE standard.
2835
 *
2836
 * Given a successful parse, the pointer (`ptr`) in the returned value is set to
2837
 * point right after the parsed number, and the `value` referenced is set to the
2838
 * parsed value. In case of error, the returned `ec` contains a representative
2839
 * error, otherwise the default (`tinyobj_ff::ff_errc()`) value is stored.
2840
 *
2841
 * The implementation does not throw and does not allocate memory (e.g., with
2842
 * `new` or `malloc`).
2843
 *
2844
 * Like the C++17 standard, the `fast_float::from_chars` functions take an
2845
 * optional last argument of the type `fast_float::chars_format`. It is a bitset
2846
 * value: we check whether `fmt & fast_float::chars_format::fixed` and `fmt &
2847
 * fast_float::chars_format::scientific` are set to determine whether we allow
2848
 * the fixed point and scientific notation respectively. The default is
2849
 * `fast_float::chars_format::general` which allows both `fixed` and
2850
 * `scientific`.
2851
 */
2852
template <typename T, typename UC = char,
2853
          typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>::value)>
2854
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
2855
from_chars(UC const *first, UC const *last, T &value,
2856
           chars_format fmt = chars_format::general) noexcept;
2857
2858
/**
2859
 * Like from_chars, but accepts an `options` argument to govern number parsing.
2860
 * Both for floating-point types and integer types.
2861
 */
2862
template <typename T, typename UC = char>
2863
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
2864
from_chars_advanced(UC const *first, UC const *last, T &value,
2865
                    parse_options_t<UC> options) noexcept;
2866
2867
/**
2868
 * from_chars for integer types.
2869
 */
2870
template <typename T, typename UC = char,
2871
          typename = FASTFLOAT_ENABLE_IF(is_supported_integer_type<T>::value)>
2872
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
2873
from_chars(UC const *first, UC const *last, T &value, int base = 10) noexcept;
2874
2875
} // namespace fast_float
2876
2877
#endif // FASTFLOAT_FAST_FLOAT_H
2878
2879
#ifndef FASTFLOAT_ASCII_NUMBER_H
2880
#define FASTFLOAT_ASCII_NUMBER_H
2881
2882
#include <cctype>
2883
#include <cstring>
2884
#include <limits>
2885
2886
2887
#ifdef FASTFLOAT_SSE2
2888
#include <emmintrin.h>
2889
#endif
2890
2891
#ifdef FASTFLOAT_NEON
2892
#include <arm_neon.h>
2893
#endif
2894
2895
namespace fast_float {
2896
2897
0
template <typename UC> fastfloat_really_inline constexpr bool has_simd_opt() {
2898
0
#ifdef FASTFLOAT_HAS_SIMD
2899
0
  return tinyobj_ff::is_same<UC, char16_t>::value;
2900
0
#else
2901
0
  return false;
2902
0
#endif
2903
0
}
2904
2905
// Next function can be micro-optimized, but compilers are entirely
2906
// able to optimize it well.
2907
template <typename UC>
2908
10.3M
fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
2909
10.3M
  return !(c > UC('9') || c < UC('0'));
2910
10.3M
}
2911
2912
0
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
2913
0
  return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 |
2914
0
         (val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 |
2915
0
         (val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 |
2916
0
         (val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
2917
0
}
2918
2919
// Read 8 UC into a u64. Truncates UC if not char.
2920
template <typename UC>
2921
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
2922
2.60M
read8_to_u64(UC const *chars) {
2923
2.60M
  if (cpp20_and_in_constexpr() || !tinyobj_ff::is_same<UC, char>::value) {
2924
0
    uint64_t val = 0;
2925
0
    for (int i = 0; i < 8; ++i) {
2926
0
      val |= uint64_t(uint8_t(*chars)) << (i * 8);
2927
0
      ++chars;
2928
0
    }
2929
0
    return val;
2930
0
  }
2931
2.60M
  uint64_t val;
2932
2.60M
  ::memcpy(&val, chars, sizeof(uint64_t));
2933
#if FASTFLOAT_IS_BIG_ENDIAN == 1
2934
  // Need to read as-if the number was in little-endian order.
2935
  val = byteswap(val);
2936
#endif
2937
2.60M
  return val;
2938
2.60M
}
2939
2940
#ifdef FASTFLOAT_SSE2
2941
2942
0
fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) {
2943
0
  FASTFLOAT_SIMD_DISABLE_WARNINGS
2944
0
  __m128i const packed = _mm_packus_epi16(data, data);
2945
0
#ifdef FASTFLOAT_64BIT
2946
0
  return uint64_t(_mm_cvtsi128_si64(packed));
2947
0
#else
2948
0
  uint64_t value;
2949
0
  // Visual Studio + older versions of GCC don't support _mm_storeu_si64
2950
0
  _mm_storel_epi64(reinterpret_cast<__m128i *>(&value), packed);
2951
0
  return value;
2952
0
#endif
2953
0
  FASTFLOAT_SIMD_RESTORE_WARNINGS
2954
0
}
2955
2956
0
fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) {
2957
0
  FASTFLOAT_SIMD_DISABLE_WARNINGS
2958
0
  return simd_read8_to_u64(
2959
0
      _mm_loadu_si128(reinterpret_cast<__m128i const *>(chars)));
2960
0
  FASTFLOAT_SIMD_RESTORE_WARNINGS
2961
0
}
2962
2963
#elif defined(FASTFLOAT_NEON)
2964
2965
fastfloat_really_inline uint64_t simd_read8_to_u64(uint16x8_t const data) {
2966
  FASTFLOAT_SIMD_DISABLE_WARNINGS
2967
  uint8x8_t utf8_packed = vmovn_u16(data);
2968
  return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
2969
  FASTFLOAT_SIMD_RESTORE_WARNINGS
2970
}
2971
2972
fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) {
2973
  FASTFLOAT_SIMD_DISABLE_WARNINGS
2974
  return simd_read8_to_u64(
2975
      vld1q_u16(reinterpret_cast<uint16_t const *>(chars)));
2976
  FASTFLOAT_SIMD_RESTORE_WARNINGS
2977
}
2978
2979
#endif // FASTFLOAT_SSE2
2980
2981
// MSVC SFINAE is broken pre-VS2017
2982
#if defined(_MSC_VER) && _MSC_VER <= 1900
2983
template <typename UC>
2984
#else
2985
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
2986
#endif
2987
// dummy for compile
2988
0
uint64_t simd_read8_to_u64(UC const *) {
2989
0
  return 0;
2990
0
}
2991
2992
// credit  @aqrit
2993
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
2994
1.66M
parse_eight_digits_unrolled(uint64_t val) {
2995
1.66M
  uint64_t const mask = 0x000000FF000000FF;
2996
1.66M
  uint64_t const mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
2997
1.66M
  uint64_t const mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
2998
1.66M
  val -= 0x3030303030303030;
2999
1.66M
  val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
3000
1.66M
  val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
3001
1.66M
  return uint32_t(val);
3002
1.66M
}
3003
3004
// Call this if chars are definitely 8 digits.
3005
template <typename UC>
3006
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t
3007
763k
parse_eight_digits_unrolled(UC const *chars) noexcept {
3008
763k
  if (cpp20_and_in_constexpr() || !has_simd_opt<UC>()) {
3009
763k
    return parse_eight_digits_unrolled(read8_to_u64(chars)); // truncation okay
3010
763k
  }
3011
0
  return parse_eight_digits_unrolled(simd_read8_to_u64(chars));
3012
763k
}
3013
3014
// credit @aqrit
3015
fastfloat_really_inline constexpr bool
3016
938k
is_made_of_eight_digits_fast(uint64_t val) noexcept {
3017
938k
  return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
3018
938k
            0x8080808080808080));
3019
938k
}
3020
3021
#ifdef FASTFLOAT_HAS_SIMD
3022
3023
// Call this if chars might not be 8 digits.
3024
// Using this style (instead of is_made_of_eight_digits_fast() then
3025
// parse_eight_digits_unrolled()) ensures we don't load SIMD registers twice.
3026
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
3027
simd_parse_if_eight_digits_unrolled(char16_t const *chars,
3028
0
                                    uint64_t &i) noexcept {
3029
0
  if (cpp20_and_in_constexpr()) {
3030
0
    return false;
3031
0
  }
3032
0
#ifdef FASTFLOAT_SSE2
3033
0
  FASTFLOAT_SIMD_DISABLE_WARNINGS
3034
0
  __m128i const data =
3035
0
      _mm_loadu_si128(reinterpret_cast<__m128i const *>(chars));
3036
0
3037
0
  // (x - '0') <= 9
3038
0
  // http://0x80.pl/articles/simd-parsing-int-sequences.html
3039
0
  __m128i const t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
3040
0
  __m128i const t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
3041
0
3042
0
  if (_mm_movemask_epi8(t1) == 0) {
3043
0
    i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
3044
0
    return true;
3045
0
  } else
3046
0
    return false;
3047
0
  FASTFLOAT_SIMD_RESTORE_WARNINGS
3048
0
#elif defined(FASTFLOAT_NEON)
3049
0
  FASTFLOAT_SIMD_DISABLE_WARNINGS
3050
0
  uint16x8_t const data = vld1q_u16(reinterpret_cast<uint16_t const *>(chars));
3051
0
3052
0
  // (x - '0') <= 9
3053
0
  // http://0x80.pl/articles/simd-parsing-int-sequences.html
3054
0
  uint16x8_t const t0 = vsubq_u16(data, vmovq_n_u16('0'));
3055
0
  uint16x8_t const mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1));
3056
0
3057
0
  if (vminvq_u16(mask) == 0xFFFF) {
3058
0
    i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
3059
0
    return true;
3060
0
  } else
3061
0
    return false;
3062
0
  FASTFLOAT_SIMD_RESTORE_WARNINGS
3063
0
#else
3064
0
  (void)chars;
3065
0
  (void)i;
3066
0
  return false;
3067
0
#endif // FASTFLOAT_SSE2
3068
0
}
3069
3070
#endif // FASTFLOAT_HAS_SIMD
3071
3072
// MSVC SFINAE is broken pre-VS2017
3073
#if defined(_MSC_VER) && _MSC_VER <= 1900
3074
template <typename UC>
3075
#else
3076
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
3077
#endif
3078
// dummy for compile
3079
bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) {
3080
  return 0;
3081
}
3082
3083
template <typename UC, FASTFLOAT_ENABLE_IF(!tinyobj_ff::is_same<UC, char>::value) = 0>
3084
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
3085
loop_parse_if_eight_digits(UC const *&p, UC const *const pend, uint64_t &i) {
3086
  if (!has_simd_opt<UC>()) {
3087
    return;
3088
  }
3089
  while ((tinyobj_ff::distance(p, pend) >= 8) &&
3090
         simd_parse_if_eight_digits_unrolled(
3091
             p, i)) { // in rare cases, this will overflow, but that's ok
3092
    p += 8;
3093
  }
3094
}
3095
3096
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
3097
loop_parse_if_eight_digits(char const *&p, char const *const pend,
3098
81.4k
                           uint64_t &i) {
3099
  // optimizes better than parse_if_eight_digits_unrolled() for UC = char.
3100
981k
  while ((tinyobj_ff::distance(p, pend) >= 8) &&
3101
938k
         is_made_of_eight_digits_fast(read8_to_u64(p))) {
3102
899k
    i = i * 100000000 +
3103
899k
        parse_eight_digits_unrolled(read8_to_u64(
3104
899k
            p)); // in rare cases, this will overflow, but that's ok
3105
899k
    p += 8;
3106
899k
  }
3107
81.4k
}
3108
3109
enum class parse_error {
3110
  no_error,
3111
  // [JSON-only] The minus sign must be followed by an integer.
3112
  missing_integer_after_sign,
3113
  // A sign must be followed by an integer or dot.
3114
  missing_integer_or_dot_after_sign,
3115
  // [JSON-only] The integer part must not have leading zeros.
3116
  leading_zeros_in_integer_part,
3117
  // [JSON-only] The integer part must have at least one digit.
3118
  no_digits_in_integer_part,
3119
  // [JSON-only] If there is a decimal point, there must be digits in the
3120
  // fractional part.
3121
  no_digits_in_fractional_part,
3122
  // The mantissa must have at least one digit.
3123
  no_digits_in_mantissa,
3124
  // Scientific notation requires an exponential part.
3125
  missing_exponential_part,
3126
};
3127
3128
template <typename UC> struct parsed_number_string_t {
3129
  int64_t exponent{0};
3130
  uint64_t mantissa{0};
3131
  UC const *lastmatch{nullptr};
3132
  bool negative{false};
3133
  bool valid{false};
3134
  bool too_many_digits{false};
3135
  // contains the range of the significant digits
3136
  span<UC const> integer{};  // non-nullable
3137
  span<UC const> fraction{}; // nullable
3138
  parse_error error{parse_error::no_error};
3139
};
3140
3141
using byte_span = span<char const>;
3142
using parsed_number_string = parsed_number_string_t<char>;
3143
3144
template <typename UC>
3145
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
3146
142k
report_parse_error(UC const *p, parse_error error) {
3147
142k
  parsed_number_string_t<UC> answer;
3148
142k
  answer.valid = false;
3149
142k
  answer.lastmatch = p;
3150
142k
  answer.error = error;
3151
142k
  return answer;
3152
142k
}
3153
3154
// Assuming that you use no more than 19 digits, this will
3155
// parse an ASCII string.
3156
template <bool basic_json_fmt, typename UC>
3157
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
3158
parse_number_string(UC const *p, UC const *pend,
3159
1.87M
                    parse_options_t<UC> options) noexcept {
3160
1.87M
  chars_format const fmt = detail::adjust_for_feature_macros(options.format);
3161
1.87M
  UC const decimal_point = options.decimal_point;
3162
3163
1.87M
  parsed_number_string_t<UC> answer;
3164
1.87M
  answer.valid = false;
3165
1.87M
  answer.too_many_digits = false;
3166
  // assume p < pend, so dereference without checks;
3167
1.87M
  answer.negative = (*p == UC('-'));
3168
  // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
3169
1.87M
  if ((*p == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) &&
3170
1.83M
                          !basic_json_fmt && *p == UC('+'))) {
3171
55.9k
    ++p;
3172
55.9k
    if (p == pend) {
3173
9.80k
      return report_parse_error<UC>(
3174
9.80k
          p, parse_error::missing_integer_or_dot_after_sign);
3175
9.80k
    }
3176
46.1k
    FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
3177
0
      if (!is_integer(*p)) { // a sign must be followed by an integer
3178
0
        return report_parse_error<UC>(p,
3179
0
                                      parse_error::missing_integer_after_sign);
3180
0
      }
3181
0
    }
3182
46.1k
    else {
3183
46.1k
      if (!is_integer(*p) &&
3184
30.2k
          (*p !=
3185
30.2k
           decimal_point)) { // a sign must be followed by an integer or the dot
3186
30.0k
        return report_parse_error<UC>(
3187
30.0k
            p, parse_error::missing_integer_or_dot_after_sign);
3188
30.0k
      }
3189
46.1k
    }
3190
46.1k
  }
3191
1.83M
  UC const *const start_digits = p;
3192
3193
1.83M
  uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
3194
3195
11.2M
  while ((p != pend) && is_integer(*p)) {
3196
    // a multiplication by 10 is cheaper than an arbitrary integer
3197
    // multiplication
3198
9.38M
    i = 10 * i +
3199
9.38M
        uint64_t(*p -
3200
9.38M
                 UC('0')); // might overflow, we will handle the overflow later
3201
9.38M
    ++p;
3202
9.38M
  }
3203
1.83M
  UC const *const end_of_integer_part = p;
3204
1.83M
  int64_t digit_count = int64_t(end_of_integer_part - start_digits);
3205
1.83M
  answer.integer = span<UC const>(start_digits, size_t(digit_count));
3206
1.83M
  FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
3207
    // at least 1 digit in integer part, without leading zeros
3208
0
    if (digit_count == 0) {
3209
0
      return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part);
3210
0
    }
3211
0
    if ((start_digits[0] == UC('0') && digit_count > 1)) {
3212
0
      return report_parse_error<UC>(start_digits,
3213
0
                                    parse_error::leading_zeros_in_integer_part);
3214
0
    }
3215
0
  }
3216
3217
1.83M
  int64_t exponent = 0;
3218
1.83M
  bool const has_decimal_point = (p != pend) && (*p == decimal_point);
3219
1.83M
  if (has_decimal_point) {
3220
81.4k
    ++p;
3221
81.4k
    UC const *before = p;
3222
    // can occur at most twice without overflowing, but let it occur more, since
3223
    // for integers with many digits, digit parsing is the primary bottleneck.
3224
81.4k
    loop_parse_if_eight_digits(p, pend, i);
3225
3226
302k
    while ((p != pend) && is_integer(*p)) {
3227
220k
      uint8_t digit = uint8_t(*p - UC('0'));
3228
220k
      ++p;
3229
220k
      i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
3230
220k
    }
3231
81.4k
    exponent = before - p;
3232
81.4k
    answer.fraction = span<UC const>(before, size_t(p - before));
3233
81.4k
    digit_count -= exponent;
3234
81.4k
  }
3235
1.83M
  FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
3236
    // at least 1 digit in fractional part
3237
0
    if (has_decimal_point && exponent == 0) {
3238
0
      return report_parse_error<UC>(p,
3239
0
                                    parse_error::no_digits_in_fractional_part);
3240
0
    }
3241
0
  }
3242
1.83M
  else if (digit_count == 0) { // we must have encountered at least one integer!
3243
102k
    return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
3244
102k
  }
3245
1.73M
  int64_t exp_number = 0; // explicit exponential part
3246
1.73M
  if ((uint64_t(fmt & chars_format::scientific) && (p != pend) &&
3247
348k
       ((UC('e') == *p) || (UC('E') == *p))) ||
3248
1.70M
      (uint64_t(fmt & detail::basic_fortran_fmt) && (p != pend) &&
3249
0
       ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) ||
3250
26.1k
        (UC('D') == *p)))) {
3251
26.1k
    UC const *location_of_e = p;
3252
26.1k
    if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) ||
3253
26.1k
        (UC('D') == *p)) {
3254
26.1k
      ++p;
3255
26.1k
    }
3256
26.1k
    bool neg_exp = false;
3257
26.1k
    if ((p != pend) && (UC('-') == *p)) {
3258
8.83k
      neg_exp = true;
3259
8.83k
      ++p;
3260
17.3k
    } else if ((p != pend) &&
3261
13.5k
               (UC('+') ==
3262
13.5k
                *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
3263
3.45k
      ++p;
3264
3.45k
    }
3265
26.1k
    if ((p == pend) || !is_integer(*p)) {
3266
9.43k
      if (!uint64_t(fmt & chars_format::fixed)) {
3267
        // The exponential part is invalid for scientific notation, so it must
3268
        // be a trailing token for fixed notation. However, fixed notation is
3269
        // disabled, so report a scientific notation error.
3270
0
        return report_parse_error<UC>(p, parse_error::missing_exponential_part);
3271
0
      }
3272
      // Otherwise, we will be ignoring the 'e'.
3273
9.43k
      p = location_of_e;
3274
16.7k
    } else {
3275
159k
      while ((p != pend) && is_integer(*p)) {
3276
142k
        uint8_t digit = uint8_t(*p - UC('0'));
3277
142k
        if (exp_number < 0x10000000) {
3278
130k
          exp_number = 10 * exp_number + digit;
3279
130k
        }
3280
142k
        ++p;
3281
142k
      }
3282
16.7k
      if (neg_exp) {
3283
8.73k
        exp_number = -exp_number;
3284
8.73k
      }
3285
16.7k
      exponent += exp_number;
3286
16.7k
    }
3287
1.70M
  } else {
3288
    // If it scientific and not fixed, we have to bail out.
3289
1.70M
    if (uint64_t(fmt & chars_format::scientific) &&
3290
1.70M
        !uint64_t(fmt & chars_format::fixed)) {
3291
0
      return report_parse_error<UC>(p, parse_error::missing_exponential_part);
3292
0
    }
3293
1.70M
  }
3294
1.73M
  answer.lastmatch = p;
3295
1.73M
  answer.valid = true;
3296
3297
  // If we frequently had to deal with long strings of digits,
3298
  // we could extend our code by using a 128-bit integer instead
3299
  // of a 64-bit integer. However, this is uncommon.
3300
  //
3301
  // We can deal with up to 19 digits.
3302
1.73M
  if (digit_count > 19) { // this is uncommon
3303
    // It is possible that the integer had an overflow.
3304
    // We have to handle the case where we have 0.0000somenumber.
3305
    // We need to be mindful of the case where we only have zeroes...
3306
    // E.g., 0.000000000...000.
3307
75.5k
    UC const *start = start_digits;
3308
801k
    while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
3309
726k
      if (*start == UC('0')) {
3310
714k
        digit_count--;
3311
714k
      }
3312
726k
      start++;
3313
726k
    }
3314
3315
75.5k
    if (digit_count > 19) {
3316
68.9k
      answer.too_many_digits = true;
3317
      // Let us start again, this time, avoiding overflows.
3318
      // We don't need to check if is_integer, since we use the
3319
      // pre-tokenized spans from above.
3320
68.9k
      i = 0;
3321
68.9k
      p = answer.integer.ptr;
3322
68.9k
      UC const *int_end = p + answer.integer.len();
3323
68.9k
      uint64_t const minimal_nineteen_digit_integer{1000000000000000000};
3324
1.18M
      while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
3325
1.11M
        i = i * 10 + uint64_t(*p - UC('0'));
3326
1.11M
        ++p;
3327
1.11M
      }
3328
68.9k
      if (i >= minimal_nineteen_digit_integer) { // We have a big integers
3329
38.0k
        exponent = end_of_integer_part - p + exp_number;
3330
38.0k
      } else { // We have a value with a fractional component.
3331
30.8k
        p = answer.fraction.ptr;
3332
30.8k
        UC const *frac_end = p + answer.fraction.len();
3333
554k
        while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
3334
523k
          i = i * 10 + uint64_t(*p - UC('0'));
3335
523k
          ++p;
3336
523k
        }
3337
30.8k
        exponent = answer.fraction.ptr - p + exp_number;
3338
30.8k
      }
3339
      // We have now corrected both exponent and i, to a truncated value
3340
68.9k
    }
3341
75.5k
  }
3342
1.73M
  answer.exponent = exponent;
3343
1.73M
  answer.mantissa = i;
3344
1.73M
  return answer;
3345
1.73M
}
Unexecuted instantiation: fast_float::parsed_number_string_t<char> fast_float::parse_number_string<true, char>(char const*, char const*, fast_float::parse_options_t<char>)
fast_float::parsed_number_string_t<char> fast_float::parse_number_string<false, char>(char const*, char const*, fast_float::parse_options_t<char>)
Line
Count
Source
3159
1.87M
                    parse_options_t<UC> options) noexcept {
3160
1.87M
  chars_format const fmt = detail::adjust_for_feature_macros(options.format);
3161
1.87M
  UC const decimal_point = options.decimal_point;
3162
3163
1.87M
  parsed_number_string_t<UC> answer;
3164
1.87M
  answer.valid = false;
3165
1.87M
  answer.too_many_digits = false;
3166
  // assume p < pend, so dereference without checks;
3167
1.87M
  answer.negative = (*p == UC('-'));
3168
  // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
3169
1.87M
  if ((*p == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) &&
3170
1.83M
                          !basic_json_fmt && *p == UC('+'))) {
3171
55.9k
    ++p;
3172
55.9k
    if (p == pend) {
3173
9.80k
      return report_parse_error<UC>(
3174
9.80k
          p, parse_error::missing_integer_or_dot_after_sign);
3175
9.80k
    }
3176
46.1k
    FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
3177
0
      if (!is_integer(*p)) { // a sign must be followed by an integer
3178
0
        return report_parse_error<UC>(p,
3179
0
                                      parse_error::missing_integer_after_sign);
3180
0
      }
3181
0
    }
3182
46.1k
    else {
3183
46.1k
      if (!is_integer(*p) &&
3184
30.2k
          (*p !=
3185
30.2k
           decimal_point)) { // a sign must be followed by an integer or the dot
3186
30.0k
        return report_parse_error<UC>(
3187
30.0k
            p, parse_error::missing_integer_or_dot_after_sign);
3188
30.0k
      }
3189
46.1k
    }
3190
46.1k
  }
3191
1.83M
  UC const *const start_digits = p;
3192
3193
1.83M
  uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
3194
3195
11.2M
  while ((p != pend) && is_integer(*p)) {
3196
    // a multiplication by 10 is cheaper than an arbitrary integer
3197
    // multiplication
3198
9.38M
    i = 10 * i +
3199
9.38M
        uint64_t(*p -
3200
9.38M
                 UC('0')); // might overflow, we will handle the overflow later
3201
9.38M
    ++p;
3202
9.38M
  }
3203
1.83M
  UC const *const end_of_integer_part = p;
3204
1.83M
  int64_t digit_count = int64_t(end_of_integer_part - start_digits);
3205
1.83M
  answer.integer = span<UC const>(start_digits, size_t(digit_count));
3206
1.83M
  FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
3207
    // at least 1 digit in integer part, without leading zeros
3208
0
    if (digit_count == 0) {
3209
0
      return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part);
3210
0
    }
3211
0
    if ((start_digits[0] == UC('0') && digit_count > 1)) {
3212
0
      return report_parse_error<UC>(start_digits,
3213
0
                                    parse_error::leading_zeros_in_integer_part);
3214
0
    }
3215
0
  }
3216
3217
1.83M
  int64_t exponent = 0;
3218
1.83M
  bool const has_decimal_point = (p != pend) && (*p == decimal_point);
3219
1.83M
  if (has_decimal_point) {
3220
81.4k
    ++p;
3221
81.4k
    UC const *before = p;
3222
    // can occur at most twice without overflowing, but let it occur more, since
3223
    // for integers with many digits, digit parsing is the primary bottleneck.
3224
81.4k
    loop_parse_if_eight_digits(p, pend, i);
3225
3226
302k
    while ((p != pend) && is_integer(*p)) {
3227
220k
      uint8_t digit = uint8_t(*p - UC('0'));
3228
220k
      ++p;
3229
220k
      i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
3230
220k
    }
3231
81.4k
    exponent = before - p;
3232
81.4k
    answer.fraction = span<UC const>(before, size_t(p - before));
3233
81.4k
    digit_count -= exponent;
3234
81.4k
  }
3235
1.83M
  FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
3236
    // at least 1 digit in fractional part
3237
0
    if (has_decimal_point && exponent == 0) {
3238
0
      return report_parse_error<UC>(p,
3239
0
                                    parse_error::no_digits_in_fractional_part);
3240
0
    }
3241
0
  }
3242
1.83M
  else if (digit_count == 0) { // we must have encountered at least one integer!
3243
102k
    return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
3244
102k
  }
3245
1.73M
  int64_t exp_number = 0; // explicit exponential part
3246
1.73M
  if ((uint64_t(fmt & chars_format::scientific) && (p != pend) &&
3247
348k
       ((UC('e') == *p) || (UC('E') == *p))) ||
3248
1.70M
      (uint64_t(fmt & detail::basic_fortran_fmt) && (p != pend) &&
3249
0
       ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) ||
3250
26.1k
        (UC('D') == *p)))) {
3251
26.1k
    UC const *location_of_e = p;
3252
26.1k
    if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) ||
3253
26.1k
        (UC('D') == *p)) {
3254
26.1k
      ++p;
3255
26.1k
    }
3256
26.1k
    bool neg_exp = false;
3257
26.1k
    if ((p != pend) && (UC('-') == *p)) {
3258
8.83k
      neg_exp = true;
3259
8.83k
      ++p;
3260
17.3k
    } else if ((p != pend) &&
3261
13.5k
               (UC('+') ==
3262
13.5k
                *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
3263
3.45k
      ++p;
3264
3.45k
    }
3265
26.1k
    if ((p == pend) || !is_integer(*p)) {
3266
9.43k
      if (!uint64_t(fmt & chars_format::fixed)) {
3267
        // The exponential part is invalid for scientific notation, so it must
3268
        // be a trailing token for fixed notation. However, fixed notation is
3269
        // disabled, so report a scientific notation error.
3270
0
        return report_parse_error<UC>(p, parse_error::missing_exponential_part);
3271
0
      }
3272
      // Otherwise, we will be ignoring the 'e'.
3273
9.43k
      p = location_of_e;
3274
16.7k
    } else {
3275
159k
      while ((p != pend) && is_integer(*p)) {
3276
142k
        uint8_t digit = uint8_t(*p - UC('0'));
3277
142k
        if (exp_number < 0x10000000) {
3278
130k
          exp_number = 10 * exp_number + digit;
3279
130k
        }
3280
142k
        ++p;
3281
142k
      }
3282
16.7k
      if (neg_exp) {
3283
8.73k
        exp_number = -exp_number;
3284
8.73k
      }
3285
16.7k
      exponent += exp_number;
3286
16.7k
    }
3287
1.70M
  } else {
3288
    // If it scientific and not fixed, we have to bail out.
3289
1.70M
    if (uint64_t(fmt & chars_format::scientific) &&
3290
1.70M
        !uint64_t(fmt & chars_format::fixed)) {
3291
0
      return report_parse_error<UC>(p, parse_error::missing_exponential_part);
3292
0
    }
3293
1.70M
  }
3294
1.73M
  answer.lastmatch = p;
3295
1.73M
  answer.valid = true;
3296
3297
  // If we frequently had to deal with long strings of digits,
3298
  // we could extend our code by using a 128-bit integer instead
3299
  // of a 64-bit integer. However, this is uncommon.
3300
  //
3301
  // We can deal with up to 19 digits.
3302
1.73M
  if (digit_count > 19) { // this is uncommon
3303
    // It is possible that the integer had an overflow.
3304
    // We have to handle the case where we have 0.0000somenumber.
3305
    // We need to be mindful of the case where we only have zeroes...
3306
    // E.g., 0.000000000...000.
3307
75.5k
    UC const *start = start_digits;
3308
801k
    while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
3309
726k
      if (*start == UC('0')) {
3310
714k
        digit_count--;
3311
714k
      }
3312
726k
      start++;
3313
726k
    }
3314
3315
75.5k
    if (digit_count > 19) {
3316
68.9k
      answer.too_many_digits = true;
3317
      // Let us start again, this time, avoiding overflows.
3318
      // We don't need to check if is_integer, since we use the
3319
      // pre-tokenized spans from above.
3320
68.9k
      i = 0;
3321
68.9k
      p = answer.integer.ptr;
3322
68.9k
      UC const *int_end = p + answer.integer.len();
3323
68.9k
      uint64_t const minimal_nineteen_digit_integer{1000000000000000000};
3324
1.18M
      while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
3325
1.11M
        i = i * 10 + uint64_t(*p - UC('0'));
3326
1.11M
        ++p;
3327
1.11M
      }
3328
68.9k
      if (i >= minimal_nineteen_digit_integer) { // We have a big integers
3329
38.0k
        exponent = end_of_integer_part - p + exp_number;
3330
38.0k
      } else { // We have a value with a fractional component.
3331
30.8k
        p = answer.fraction.ptr;
3332
30.8k
        UC const *frac_end = p + answer.fraction.len();
3333
554k
        while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
3334
523k
          i = i * 10 + uint64_t(*p - UC('0'));
3335
523k
          ++p;
3336
523k
        }
3337
30.8k
        exponent = answer.fraction.ptr - p + exp_number;
3338
30.8k
      }
3339
      // We have now corrected both exponent and i, to a truncated value
3340
68.9k
    }
3341
75.5k
  }
3342
1.73M
  answer.exponent = exponent;
3343
1.73M
  answer.mantissa = i;
3344
1.73M
  return answer;
3345
1.73M
}
3346
3347
template <typename T, typename UC>
3348
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
3349
parse_int_string(UC const *p, UC const *pend, T &value,
3350
                 parse_options_t<UC> options) {
3351
  chars_format const fmt = detail::adjust_for_feature_macros(options.format);
3352
  int const base = options.base;
3353
3354
  from_chars_result_t<UC> answer;
3355
3356
  UC const *const first = p;
3357
3358
  bool const negative = (*p == UC('-'));
3359
#ifdef FASTFLOAT_VISUAL_STUDIO
3360
#pragma warning(push)
3361
#pragma warning(disable : 4127)
3362
#endif
3363
  if (!tinyobj_ff::is_signed<T>::value && negative) {
3364
#ifdef FASTFLOAT_VISUAL_STUDIO
3365
#pragma warning(pop)
3366
#endif
3367
    answer.ec = tinyobj_ff::ff_errc::invalid_argument;
3368
    answer.ptr = first;
3369
    return answer;
3370
  }
3371
  if ((*p == UC('-')) ||
3372
      (uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) {
3373
    ++p;
3374
  }
3375
3376
  UC const *const start_num = p;
3377
3378
  while (p != pend && *p == UC('0')) {
3379
    ++p;
3380
  }
3381
3382
  bool const has_leading_zeros = p > start_num;
3383
3384
  UC const *const start_digits = p;
3385
3386
  uint64_t i = 0;
3387
  if (base == 10) {
3388
    loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible
3389
  }
3390
  while (p != pend) {
3391
    uint8_t digit = ch_to_digit(*p);
3392
    if (digit >= base) {
3393
      break;
3394
    }
3395
    i = uint64_t(base) * i + digit; // might overflow, check this later
3396
    p++;
3397
  }
3398
3399
  size_t digit_count = size_t(p - start_digits);
3400
3401
  if (digit_count == 0) {
3402
    if (has_leading_zeros) {
3403
      value = 0;
3404
      answer.ec = tinyobj_ff::ff_errc();
3405
      answer.ptr = p;
3406
    } else {
3407
      answer.ec = tinyobj_ff::ff_errc::invalid_argument;
3408
      answer.ptr = first;
3409
    }
3410
    return answer;
3411
  }
3412
3413
  answer.ptr = p;
3414
3415
  // check u64 overflow
3416
  size_t max_digits = max_digits_u64(base);
3417
  if (digit_count > max_digits) {
3418
    answer.ec = tinyobj_ff::ff_errc::result_out_of_range;
3419
    return answer;
3420
  }
3421
  // this check can be eliminated for all other types, but they will all require
3422
  // a max_digits(base) equivalent
3423
  if (digit_count == max_digits && i < min_safe_u64(base)) {
3424
    answer.ec = tinyobj_ff::ff_errc::result_out_of_range;
3425
    return answer;
3426
  }
3427
3428
  // check other types overflow
3429
  if (!tinyobj_ff::is_same<T, uint64_t>::value) {
3430
    if (i > uint64_t(std::numeric_limits<T>::max()) + uint64_t(negative)) {
3431
      answer.ec = tinyobj_ff::ff_errc::result_out_of_range;
3432
      return answer;
3433
    }
3434
  }
3435
3436
  if (negative) {
3437
#ifdef FASTFLOAT_VISUAL_STUDIO
3438
#pragma warning(push)
3439
#pragma warning(disable : 4146)
3440
#endif
3441
    // this weird workaround is required because:
3442
    // - converting unsigned to signed when its value is greater than signed max
3443
    // is UB pre-C++23.
3444
    // - reinterpret_casting (~i + 1) would work, but it is not constexpr
3445
    // this is always optimized into a neg instruction (note: T is an integer
3446
    // type)
3447
    value = T(-std::numeric_limits<T>::max() -
3448
              T(i - uint64_t(std::numeric_limits<T>::max())));
3449
#ifdef FASTFLOAT_VISUAL_STUDIO
3450
#pragma warning(pop)
3451
#endif
3452
  } else {
3453
    value = T(i);
3454
  }
3455
3456
  answer.ec = tinyobj_ff::ff_errc();
3457
  return answer;
3458
}
3459
3460
} // namespace fast_float
3461
3462
#endif
3463
3464
#ifndef FASTFLOAT_FAST_TABLE_H
3465
#define FASTFLOAT_FAST_TABLE_H
3466
3467
namespace fast_float {
3468
3469
/**
3470
 * When mapping numbers from decimal to binary,
3471
 * we go from w * 10^q to m * 2^p but we have
3472
 * 10^q = 5^q * 2^q, so effectively
3473
 * we are trying to match
3474
 * w * 2^q * 5^q to m * 2^p. Thus the powers of two
3475
 * are not a concern since they can be represented
3476
 * exactly using the binary notation, only the powers of five
3477
 * affect the binary significand.
3478
 */
3479
3480
/**
3481
 * The smallest non-zero float (binary64) is 2^-1074.
3482
 * We take as input numbers of the form w x 10^q where w < 2^64.
3483
 * We have that w * 10^-343  <  2^(64-344) 5^-343 < 2^-1076.
3484
 * However, we have that
3485
 * (2^64-1) * 10^-342 =  (2^64-1) * 2^-342 * 5^-342 > 2^-1074.
3486
 * Thus it is possible for a number of the form w * 10^-342 where
3487
 * w is a 64-bit value to be a non-zero floating-point number.
3488
 *********
3489
 * Any number of form w * 10^309 where w>= 1 is going to be
3490
 * infinite in binary64 so we never need to worry about powers
3491
 * of 5 greater than 308.
3492
 */
3493
template <class unused = void> struct powers_template {
3494
3495
  constexpr static int smallest_power_of_five =
3496
      binary_format<double>::smallest_power_of_ten();
3497
  constexpr static int largest_power_of_five =
3498
      binary_format<double>::largest_power_of_ten();
3499
  constexpr static int number_of_entries =
3500
      2 * (largest_power_of_five - smallest_power_of_five + 1);
3501
  // Powers of five from 5^-342 all the way to 5^308 rounded toward one.
3502
  constexpr static uint64_t power_of_five_128[number_of_entries] = {
3503
      0xeef453d6923bd65a, 0x113faa2906a13b3f,
3504
      0x9558b4661b6565f8, 0x4ac7ca59a424c507,
3505
      0xbaaee17fa23ebf76, 0x5d79bcf00d2df649,
3506
      0xe95a99df8ace6f53, 0xf4d82c2c107973dc,
3507
      0x91d8a02bb6c10594, 0x79071b9b8a4be869,
3508
      0xb64ec836a47146f9, 0x9748e2826cdee284,
3509
      0xe3e27a444d8d98b7, 0xfd1b1b2308169b25,
3510
      0x8e6d8c6ab0787f72, 0xfe30f0f5e50e20f7,
3511
      0xb208ef855c969f4f, 0xbdbd2d335e51a935,
3512
      0xde8b2b66b3bc4723, 0xad2c788035e61382,
3513
      0x8b16fb203055ac76, 0x4c3bcb5021afcc31,
3514
      0xaddcb9e83c6b1793, 0xdf4abe242a1bbf3d,
3515
      0xd953e8624b85dd78, 0xd71d6dad34a2af0d,
3516
      0x87d4713d6f33aa6b, 0x8672648c40e5ad68,
3517
      0xa9c98d8ccb009506, 0x680efdaf511f18c2,
3518
      0xd43bf0effdc0ba48, 0x212bd1b2566def2,
3519
      0x84a57695fe98746d, 0x14bb630f7604b57,
3520
      0xa5ced43b7e3e9188, 0x419ea3bd35385e2d,
3521
      0xcf42894a5dce35ea, 0x52064cac828675b9,
3522
      0x818995ce7aa0e1b2, 0x7343efebd1940993,
3523
      0xa1ebfb4219491a1f, 0x1014ebe6c5f90bf8,
3524
      0xca66fa129f9b60a6, 0xd41a26e077774ef6,
3525
      0xfd00b897478238d0, 0x8920b098955522b4,
3526
      0x9e20735e8cb16382, 0x55b46e5f5d5535b0,
3527
      0xc5a890362fddbc62, 0xeb2189f734aa831d,
3528
      0xf712b443bbd52b7b, 0xa5e9ec7501d523e4,
3529
      0x9a6bb0aa55653b2d, 0x47b233c92125366e,
3530
      0xc1069cd4eabe89f8, 0x999ec0bb696e840a,
3531
      0xf148440a256e2c76, 0xc00670ea43ca250d,
3532
      0x96cd2a865764dbca, 0x380406926a5e5728,
3533
      0xbc807527ed3e12bc, 0xc605083704f5ecf2,
3534
      0xeba09271e88d976b, 0xf7864a44c633682e,
3535
      0x93445b8731587ea3, 0x7ab3ee6afbe0211d,
3536
      0xb8157268fdae9e4c, 0x5960ea05bad82964,
3537
      0xe61acf033d1a45df, 0x6fb92487298e33bd,
3538
      0x8fd0c16206306bab, 0xa5d3b6d479f8e056,
3539
      0xb3c4f1ba87bc8696, 0x8f48a4899877186c,
3540
      0xe0b62e2929aba83c, 0x331acdabfe94de87,
3541
      0x8c71dcd9ba0b4925, 0x9ff0c08b7f1d0b14,
3542
      0xaf8e5410288e1b6f, 0x7ecf0ae5ee44dd9,
3543
      0xdb71e91432b1a24a, 0xc9e82cd9f69d6150,
3544
      0x892731ac9faf056e, 0xbe311c083a225cd2,
3545
      0xab70fe17c79ac6ca, 0x6dbd630a48aaf406,
3546
      0xd64d3d9db981787d, 0x92cbbccdad5b108,
3547
      0x85f0468293f0eb4e, 0x25bbf56008c58ea5,
3548
      0xa76c582338ed2621, 0xaf2af2b80af6f24e,
3549
      0xd1476e2c07286faa, 0x1af5af660db4aee1,
3550
      0x82cca4db847945ca, 0x50d98d9fc890ed4d,
3551
      0xa37fce126597973c, 0xe50ff107bab528a0,
3552
      0xcc5fc196fefd7d0c, 0x1e53ed49a96272c8,
3553
      0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7a,
3554
      0x9faacf3df73609b1, 0x77b191618c54e9ac,
3555
      0xc795830d75038c1d, 0xd59df5b9ef6a2417,
3556
      0xf97ae3d0d2446f25, 0x4b0573286b44ad1d,
3557
      0x9becce62836ac577, 0x4ee367f9430aec32,
3558
      0xc2e801fb244576d5, 0x229c41f793cda73f,
3559
      0xf3a20279ed56d48a, 0x6b43527578c1110f,
3560
      0x9845418c345644d6, 0x830a13896b78aaa9,
3561
      0xbe5691ef416bd60c, 0x23cc986bc656d553,
3562
      0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa8,
3563
      0x94b3a202eb1c3f39, 0x7bf7d71432f3d6a9,
3564
      0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc53,
3565
      0xe858ad248f5c22c9, 0xd1b3400f8f9cff68,
3566
      0x91376c36d99995be, 0x23100809b9c21fa1,
3567
      0xb58547448ffffb2d, 0xabd40a0c2832a78a,
3568
      0xe2e69915b3fff9f9, 0x16c90c8f323f516c,
3569
      0x8dd01fad907ffc3b, 0xae3da7d97f6792e3,
3570
      0xb1442798f49ffb4a, 0x99cd11cfdf41779c,
3571
      0xdd95317f31c7fa1d, 0x40405643d711d583,
3572
      0x8a7d3eef7f1cfc52, 0x482835ea666b2572,
3573
      0xad1c8eab5ee43b66, 0xda3243650005eecf,
3574
      0xd863b256369d4a40, 0x90bed43e40076a82,
3575
      0x873e4f75e2224e68, 0x5a7744a6e804a291,
3576
      0xa90de3535aaae202, 0x711515d0a205cb36,
3577
      0xd3515c2831559a83, 0xd5a5b44ca873e03,
3578
      0x8412d9991ed58091, 0xe858790afe9486c2,
3579
      0xa5178fff668ae0b6, 0x626e974dbe39a872,
3580
      0xce5d73ff402d98e3, 0xfb0a3d212dc8128f,
3581
      0x80fa687f881c7f8e, 0x7ce66634bc9d0b99,
3582
      0xa139029f6a239f72, 0x1c1fffc1ebc44e80,
3583
      0xc987434744ac874e, 0xa327ffb266b56220,
3584
      0xfbe9141915d7a922, 0x4bf1ff9f0062baa8,
3585
      0x9d71ac8fada6c9b5, 0x6f773fc3603db4a9,
3586
      0xc4ce17b399107c22, 0xcb550fb4384d21d3,
3587
      0xf6019da07f549b2b, 0x7e2a53a146606a48,
3588
      0x99c102844f94e0fb, 0x2eda7444cbfc426d,
3589
      0xc0314325637a1939, 0xfa911155fefb5308,
3590
      0xf03d93eebc589f88, 0x793555ab7eba27ca,
3591
      0x96267c7535b763b5, 0x4bc1558b2f3458de,
3592
      0xbbb01b9283253ca2, 0x9eb1aaedfb016f16,
3593
      0xea9c227723ee8bcb, 0x465e15a979c1cadc,
3594
      0x92a1958a7675175f, 0xbfacd89ec191ec9,
3595
      0xb749faed14125d36, 0xcef980ec671f667b,
3596
      0xe51c79a85916f484, 0x82b7e12780e7401a,
3597
      0x8f31cc0937ae58d2, 0xd1b2ecb8b0908810,
3598
      0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa15,
3599
      0xdfbdcece67006ac9, 0x67a791e093e1d49a,
3600
      0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e0,
3601
      0xaecc49914078536d, 0x58fae9f773886e18,
3602
      0xda7f5bf590966848, 0xaf39a475506a899e,
3603
      0x888f99797a5e012d, 0x6d8406c952429603,
3604
      0xaab37fd7d8f58178, 0xc8e5087ba6d33b83,
3605
      0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a64,
3606
      0x855c3be0a17fcd26, 0x5cf2eea09a55067f,
3607
      0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481e,
3608
      0xd0601d8efc57b08b, 0xf13b94daf124da26,
3609
      0x823c12795db6ce57, 0x76c53d08d6b70858,
3610
      0xa2cb1717b52481ed, 0x54768c4b0c64ca6e,
3611
      0xcb7ddcdda26da268, 0xa9942f5dcf7dfd09,
3612
      0xfe5d54150b090b02, 0xd3f93b35435d7c4c,
3613
      0x9efa548d26e5a6e1, 0xc47bc5014a1a6daf,
3614
      0xc6b8e9b0709f109a, 0x359ab6419ca1091b,
3615
      0xf867241c8cc6d4c0, 0xc30163d203c94b62,
3616
      0x9b407691d7fc44f8, 0x79e0de63425dcf1d,
3617
      0xc21094364dfb5636, 0x985915fc12f542e4,
3618
      0xf294b943e17a2bc4, 0x3e6f5b7b17b2939d,
3619
      0x979cf3ca6cec5b5a, 0xa705992ceecf9c42,
3620
      0xbd8430bd08277231, 0x50c6ff782a838353,
3621
      0xece53cec4a314ebd, 0xa4f8bf5635246428,
3622
      0x940f4613ae5ed136, 0x871b7795e136be99,
3623
      0xb913179899f68584, 0x28e2557b59846e3f,
3624
      0xe757dd7ec07426e5, 0x331aeada2fe589cf,
3625
      0x9096ea6f3848984f, 0x3ff0d2c85def7621,
3626
      0xb4bca50b065abe63, 0xfed077a756b53a9,
3627
      0xe1ebce4dc7f16dfb, 0xd3e8495912c62894,
3628
      0x8d3360f09cf6e4bd, 0x64712dd7abbbd95c,
3629
      0xb080392cc4349dec, 0xbd8d794d96aacfb3,
3630
      0xdca04777f541c567, 0xecf0d7a0fc5583a0,
3631
      0x89e42caaf9491b60, 0xf41686c49db57244,
3632
      0xac5d37d5b79b6239, 0x311c2875c522ced5,
3633
      0xd77485cb25823ac7, 0x7d633293366b828b,
3634
      0x86a8d39ef77164bc, 0xae5dff9c02033197,
3635
      0xa8530886b54dbdeb, 0xd9f57f830283fdfc,
3636
      0xd267caa862a12d66, 0xd072df63c324fd7b,
3637
      0x8380dea93da4bc60, 0x4247cb9e59f71e6d,
3638
      0xa46116538d0deb78, 0x52d9be85f074e608,
3639
      0xcd795be870516656, 0x67902e276c921f8b,
3640
      0x806bd9714632dff6, 0xba1cd8a3db53b6,
3641
      0xa086cfcd97bf97f3, 0x80e8a40eccd228a4,
3642
      0xc8a883c0fdaf7df0, 0x6122cd128006b2cd,
3643
      0xfad2a4b13d1b5d6c, 0x796b805720085f81,
3644
      0x9cc3a6eec6311a63, 0xcbe3303674053bb0,
3645
      0xc3f490aa77bd60fc, 0xbedbfc4411068a9c,
3646
      0xf4f1b4d515acb93b, 0xee92fb5515482d44,
3647
      0x991711052d8bf3c5, 0x751bdd152d4d1c4a,
3648
      0xbf5cd54678eef0b6, 0xd262d45a78a0635d,
3649
      0xef340a98172aace4, 0x86fb897116c87c34,
3650
      0x9580869f0e7aac0e, 0xd45d35e6ae3d4da0,
3651
      0xbae0a846d2195712, 0x8974836059cca109,
3652
      0xe998d258869facd7, 0x2bd1a438703fc94b,
3653
      0x91ff83775423cc06, 0x7b6306a34627ddcf,
3654
      0xb67f6455292cbf08, 0x1a3bc84c17b1d542,
3655
      0xe41f3d6a7377eeca, 0x20caba5f1d9e4a93,
3656
      0x8e938662882af53e, 0x547eb47b7282ee9c,
3657
      0xb23867fb2a35b28d, 0xe99e619a4f23aa43,
3658
      0xdec681f9f4c31f31, 0x6405fa00e2ec94d4,
3659
      0x8b3c113c38f9f37e, 0xde83bc408dd3dd04,
3660
      0xae0b158b4738705e, 0x9624ab50b148d445,
3661
      0xd98ddaee19068c76, 0x3badd624dd9b0957,
3662
      0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d6,
3663
      0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4c,
3664
      0xd47487cc8470652b, 0x7647c3200069671f,
3665
      0x84c8d4dfd2c63f3b, 0x29ecd9f40041e073,
3666
      0xa5fb0a17c777cf09, 0xf468107100525890,
3667
      0xcf79cc9db955c2cc, 0x7182148d4066eeb4,
3668
      0x81ac1fe293d599bf, 0xc6f14cd848405530,
3669
      0xa21727db38cb002f, 0xb8ada00e5a506a7c,
3670
      0xca9cf1d206fdc03b, 0xa6d90811f0e4851c,
3671
      0xfd442e4688bd304a, 0x908f4a166d1da663,
3672
      0x9e4a9cec15763e2e, 0x9a598e4e043287fe,
3673
      0xc5dd44271ad3cdba, 0x40eff1e1853f29fd,
3674
      0xf7549530e188c128, 0xd12bee59e68ef47c,
3675
      0x9a94dd3e8cf578b9, 0x82bb74f8301958ce,
3676
      0xc13a148e3032d6e7, 0xe36a52363c1faf01,
3677
      0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac1,
3678
      0x96f5600f15a7b7e5, 0x29ab103a5ef8c0b9,
3679
      0xbcb2b812db11a5de, 0x7415d448f6b6f0e7,
3680
      0xebdf661791d60f56, 0x111b495b3464ad21,
3681
      0x936b9fcebb25c995, 0xcab10dd900beec34,
3682
      0xb84687c269ef3bfb, 0x3d5d514f40eea742,
3683
      0xe65829b3046b0afa, 0xcb4a5a3112a5112,
3684
      0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ab,
3685
      0xb3f4e093db73a093, 0x59ed216765690f56,
3686
      0xe0f218b8d25088b8, 0x306869c13ec3532c,
3687
      0x8c974f7383725573, 0x1e414218c73a13fb,
3688
      0xafbd2350644eeacf, 0xe5d1929ef90898fa,
3689
      0xdbac6c247d62a583, 0xdf45f746b74abf39,
3690
      0x894bc396ce5da772, 0x6b8bba8c328eb783,
3691
      0xab9eb47c81f5114f, 0x66ea92f3f326564,
3692
      0xd686619ba27255a2, 0xc80a537b0efefebd,
3693
      0x8613fd0145877585, 0xbd06742ce95f5f36,
3694
      0xa798fc4196e952e7, 0x2c48113823b73704,
3695
      0xd17f3b51fca3a7a0, 0xf75a15862ca504c5,
3696
      0x82ef85133de648c4, 0x9a984d73dbe722fb,
3697
      0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebba,
3698
      0xcc963fee10b7d1b3, 0x318df905079926a8,
3699
      0xffbbcfe994e5c61f, 0xfdf17746497f7052,
3700
      0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa633,
3701
      0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc0,
3702
      0xf9bd690a1b68637b, 0x3dfdce7aa3c673b0,
3703
      0x9c1661a651213e2d, 0x6bea10ca65c084e,
3704
      0xc31bfa0fe5698db8, 0x486e494fcff30a62,
3705
      0xf3e2f893dec3f126, 0x5a89dba3c3efccfa,
3706
      0x986ddb5c6b3a76b7, 0xf89629465a75e01c,
3707
      0xbe89523386091465, 0xf6bbb397f1135823,
3708
      0xee2ba6c0678b597f, 0x746aa07ded582e2c,
3709
      0x94db483840b717ef, 0xa8c2a44eb4571cdc,
3710
      0xba121a4650e4ddeb, 0x92f34d62616ce413,
3711
      0xe896a0d7e51e1566, 0x77b020baf9c81d17,
3712
      0x915e2486ef32cd60, 0xace1474dc1d122e,
3713
      0xb5b5ada8aaff80b8, 0xd819992132456ba,
3714
      0xe3231912d5bf60e6, 0x10e1fff697ed6c69,
3715
      0x8df5efabc5979c8f, 0xca8d3ffa1ef463c1,
3716
      0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb2,
3717
      0xddd0467c64bce4a0, 0xac7cb3f6d05ddbde,
3718
      0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96b,
3719
      0xad4ab7112eb3929d, 0x86c16c98d2c953c6,
3720
      0xd89d64d57a607744, 0xe871c7bf077ba8b7,
3721
      0x87625f056c7c4a8b, 0x11471cd764ad4972,
3722
      0xa93af6c6c79b5d2d, 0xd598e40d3dd89bcf,
3723
      0xd389b47879823479, 0x4aff1d108d4ec2c3,
3724
      0x843610cb4bf160cb, 0xcedf722a585139ba,
3725
      0xa54394fe1eedb8fe, 0xc2974eb4ee658828,
3726
      0xce947a3da6a9273e, 0x733d226229feea32,
3727
      0x811ccc668829b887, 0x806357d5a3f525f,
3728
      0xa163ff802a3426a8, 0xca07c2dcb0cf26f7,
3729
      0xc9bcff6034c13052, 0xfc89b393dd02f0b5,
3730
      0xfc2c3f3841f17c67, 0xbbac2078d443ace2,
3731
      0x9d9ba7832936edc0, 0xd54b944b84aa4c0d,
3732
      0xc5029163f384a931, 0xa9e795e65d4df11,
3733
      0xf64335bcf065d37d, 0x4d4617b5ff4a16d5,
3734
      0x99ea0196163fa42e, 0x504bced1bf8e4e45,
3735
      0xc06481fb9bcf8d39, 0xe45ec2862f71e1d6,
3736
      0xf07da27a82c37088, 0x5d767327bb4e5a4c,
3737
      0x964e858c91ba2655, 0x3a6a07f8d510f86f,
3738
      0xbbe226efb628afea, 0x890489f70a55368b,
3739
      0xeadab0aba3b2dbe5, 0x2b45ac74ccea842e,
3740
      0x92c8ae6b464fc96f, 0x3b0b8bc90012929d,
3741
      0xb77ada0617e3bbcb, 0x9ce6ebb40173744,
3742
      0xe55990879ddcaabd, 0xcc420a6a101d0515,
3743
      0x8f57fa54c2a9eab6, 0x9fa946824a12232d,
3744
      0xb32df8e9f3546564, 0x47939822dc96abf9,
3745
      0xdff9772470297ebd, 0x59787e2b93bc56f7,
3746
      0x8bfbea76c619ef36, 0x57eb4edb3c55b65a,
3747
      0xaefae51477a06b03, 0xede622920b6b23f1,
3748
      0xdab99e59958885c4, 0xe95fab368e45eced,
3749
      0x88b402f7fd75539b, 0x11dbcb0218ebb414,
3750
      0xaae103b5fcd2a881, 0xd652bdc29f26a119,
3751
      0xd59944a37c0752a2, 0x4be76d3346f0495f,
3752
      0x857fcae62d8493a5, 0x6f70a4400c562ddb,
3753
      0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb952,
3754
      0xd097ad07a71f26b2, 0x7e2000a41346a7a7,
3755
      0x825ecc24c873782f, 0x8ed400668c0c28c8,
3756
      0xa2f67f2dfa90563b, 0x728900802f0f32fa,
3757
      0xcbb41ef979346bca, 0x4f2b40a03ad2ffb9,
3758
      0xfea126b7d78186bc, 0xe2f610c84987bfa8,
3759
      0x9f24b832e6b0f436, 0xdd9ca7d2df4d7c9,
3760
      0xc6ede63fa05d3143, 0x91503d1c79720dbb,
3761
      0xf8a95fcf88747d94, 0x75a44c6397ce912a,
3762
      0x9b69dbe1b548ce7c, 0xc986afbe3ee11aba,
3763
      0xc24452da229b021b, 0xfbe85badce996168,
3764
      0xf2d56790ab41c2a2, 0xfae27299423fb9c3,
3765
      0x97c560ba6b0919a5, 0xdccd879fc967d41a,
3766
      0xbdb6b8e905cb600f, 0x5400e987bbc1c920,
3767
      0xed246723473e3813, 0x290123e9aab23b68,
3768
      0x9436c0760c86e30b, 0xf9a0b6720aaf6521,
3769
      0xb94470938fa89bce, 0xf808e40e8d5b3e69,
3770
      0xe7958cb87392c2c2, 0xb60b1d1230b20e04,
3771
      0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c2,
3772
      0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af3,
3773
      0xe2280b6c20dd5232, 0x25c6da63c38de1b0,
3774
      0x8d590723948a535f, 0x579c487e5a38ad0e,
3775
      0xb0af48ec79ace837, 0x2d835a9df0c6d851,
3776
      0xdcdb1b2798182244, 0xf8e431456cf88e65,
3777
      0x8a08f0f8bf0f156b, 0x1b8e9ecb641b58ff,
3778
      0xac8b2d36eed2dac5, 0xe272467e3d222f3f,
3779
      0xd7adf884aa879177, 0x5b0ed81dcc6abb0f,
3780
      0x86ccbb52ea94baea, 0x98e947129fc2b4e9,
3781
      0xa87fea27a539e9a5, 0x3f2398d747b36224,
3782
      0xd29fe4b18e88640e, 0x8eec7f0d19a03aad,
3783
      0x83a3eeeef9153e89, 0x1953cf68300424ac,
3784
      0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd7,
3785
      0xcdb02555653131b6, 0x3792f412cb06794d,
3786
      0x808e17555f3ebf11, 0xe2bbd88bbee40bd0,
3787
      0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec4,
3788
      0xc8de047564d20a8b, 0xf245825a5a445275,
3789
      0xfb158592be068d2e, 0xeed6e2f0f0d56712,
3790
      0x9ced737bb6c4183d, 0x55464dd69685606b,
3791
      0xc428d05aa4751e4c, 0xaa97e14c3c26b886,
3792
      0xf53304714d9265df, 0xd53dd99f4b3066a8,
3793
      0x993fe2c6d07b7fab, 0xe546a8038efe4029,
3794
      0xbf8fdb78849a5f96, 0xde98520472bdd033,
3795
      0xef73d256a5c0f77c, 0x963e66858f6d4440,
3796
      0x95a8637627989aad, 0xdde7001379a44aa8,
3797
      0xbb127c53b17ec159, 0x5560c018580d5d52,
3798
      0xe9d71b689dde71af, 0xaab8f01e6e10b4a6,
3799
      0x9226712162ab070d, 0xcab3961304ca70e8,
3800
      0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d22,
3801
      0xe45c10c42a2b3b05, 0x8cb89a7db77c506a,
3802
      0x8eb98a7a9a5b04e3, 0x77f3608e92adb242,
3803
      0xb267ed1940f1c61c, 0x55f038b237591ed3,
3804
      0xdf01e85f912e37a3, 0x6b6c46dec52f6688,
3805
      0x8b61313bbabce2c6, 0x2323ac4b3b3da015,
3806
      0xae397d8aa96c1b77, 0xabec975e0a0d081a,
3807
      0xd9c7dced53c72255, 0x96e7bd358c904a21,
3808
      0x881cea14545c7575, 0x7e50d64177da2e54,
3809
      0xaa242499697392d2, 0xdde50bd1d5d0b9e9,
3810
      0xd4ad2dbfc3d07787, 0x955e4ec64b44e864,
3811
      0x84ec3c97da624ab4, 0xbd5af13bef0b113e,
3812
      0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58e,
3813
      0xcfb11ead453994ba, 0x67de18eda5814af2,
3814
      0x81ceb32c4b43fcf4, 0x80eacf948770ced7,
3815
      0xa2425ff75e14fc31, 0xa1258379a94d028d,
3816
      0xcad2f7f5359a3b3e, 0x96ee45813a04330,
3817
      0xfd87b5f28300ca0d, 0x8bca9d6e188853fc,
3818
      0x9e74d1b791e07e48, 0x775ea264cf55347e,
3819
      0xc612062576589dda, 0x95364afe032a819e,
3820
      0xf79687aed3eec551, 0x3a83ddbd83f52205,
3821
      0x9abe14cd44753b52, 0xc4926a9672793543,
3822
      0xc16d9a0095928a27, 0x75b7053c0f178294,
3823
      0xf1c90080baf72cb1, 0x5324c68b12dd6339,
3824
      0x971da05074da7bee, 0xd3f6fc16ebca5e04,
3825
      0xbce5086492111aea, 0x88f4bb1ca6bcf585,
3826
      0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6,
3827
      0x9392ee8e921d5d07, 0x3aff322e62439fd0,
3828
      0xb877aa3236a4b449, 0x9befeb9fad487c3,
3829
      0xe69594bec44de15b, 0x4c2ebe687989a9b4,
3830
      0x901d7cf73ab0acd9, 0xf9d37014bf60a11,
3831
      0xb424dc35095cd80f, 0x538484c19ef38c95,
3832
      0xe12e13424bb40e13, 0x2865a5f206b06fba,
3833
      0x8cbccc096f5088cb, 0xf93f87b7442e45d4,
3834
      0xafebff0bcb24aafe, 0xf78f69a51539d749,
3835
      0xdbe6fecebdedd5be, 0xb573440e5a884d1c,
3836
      0x89705f4136b4a597, 0x31680a88f8953031,
3837
      0xabcc77118461cefc, 0xfdc20d2b36ba7c3e,
3838
      0xd6bf94d5e57a42bc, 0x3d32907604691b4d,
3839
      0x8637bd05af6c69b5, 0xa63f9a49c2c1b110,
3840
      0xa7c5ac471b478423, 0xfcf80dc33721d54,
3841
      0xd1b71758e219652b, 0xd3c36113404ea4a9,
3842
      0x83126e978d4fdf3b, 0x645a1cac083126ea,
3843
      0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4,
3844
      0xcccccccccccccccc, 0xcccccccccccccccd,
3845
      0x8000000000000000, 0x0,
3846
      0xa000000000000000, 0x0,
3847
      0xc800000000000000, 0x0,
3848
      0xfa00000000000000, 0x0,
3849
      0x9c40000000000000, 0x0,
3850
      0xc350000000000000, 0x0,
3851
      0xf424000000000000, 0x0,
3852
      0x9896800000000000, 0x0,
3853
      0xbebc200000000000, 0x0,
3854
      0xee6b280000000000, 0x0,
3855
      0x9502f90000000000, 0x0,
3856
      0xba43b74000000000, 0x0,
3857
      0xe8d4a51000000000, 0x0,
3858
      0x9184e72a00000000, 0x0,
3859
      0xb5e620f480000000, 0x0,
3860
      0xe35fa931a0000000, 0x0,
3861
      0x8e1bc9bf04000000, 0x0,
3862
      0xb1a2bc2ec5000000, 0x0,
3863
      0xde0b6b3a76400000, 0x0,
3864
      0x8ac7230489e80000, 0x0,
3865
      0xad78ebc5ac620000, 0x0,
3866
      0xd8d726b7177a8000, 0x0,
3867
      0x878678326eac9000, 0x0,
3868
      0xa968163f0a57b400, 0x0,
3869
      0xd3c21bcecceda100, 0x0,
3870
      0x84595161401484a0, 0x0,
3871
      0xa56fa5b99019a5c8, 0x0,
3872
      0xcecb8f27f4200f3a, 0x0,
3873
      0x813f3978f8940984, 0x4000000000000000,
3874
      0xa18f07d736b90be5, 0x5000000000000000,
3875
      0xc9f2c9cd04674ede, 0xa400000000000000,
3876
      0xfc6f7c4045812296, 0x4d00000000000000,
3877
      0x9dc5ada82b70b59d, 0xf020000000000000,
3878
      0xc5371912364ce305, 0x6c28000000000000,
3879
      0xf684df56c3e01bc6, 0xc732000000000000,
3880
      0x9a130b963a6c115c, 0x3c7f400000000000,
3881
      0xc097ce7bc90715b3, 0x4b9f100000000000,
3882
      0xf0bdc21abb48db20, 0x1e86d40000000000,
3883
      0x96769950b50d88f4, 0x1314448000000000,
3884
      0xbc143fa4e250eb31, 0x17d955a000000000,
3885
      0xeb194f8e1ae525fd, 0x5dcfab0800000000,
3886
      0x92efd1b8d0cf37be, 0x5aa1cae500000000,
3887
      0xb7abc627050305ad, 0xf14a3d9e40000000,
3888
      0xe596b7b0c643c719, 0x6d9ccd05d0000000,
3889
      0x8f7e32ce7bea5c6f, 0xe4820023a2000000,
3890
      0xb35dbf821ae4f38b, 0xdda2802c8a800000,
3891
      0xe0352f62a19e306e, 0xd50b2037ad200000,
3892
      0x8c213d9da502de45, 0x4526f422cc340000,
3893
      0xaf298d050e4395d6, 0x9670b12b7f410000,
3894
      0xdaf3f04651d47b4c, 0x3c0cdd765f114000,
3895
      0x88d8762bf324cd0f, 0xa5880a69fb6ac800,
3896
      0xab0e93b6efee0053, 0x8eea0d047a457a00,
3897
      0xd5d238a4abe98068, 0x72a4904598d6d880,
3898
      0x85a36366eb71f041, 0x47a6da2b7f864750,
3899
      0xa70c3c40a64e6c51, 0x999090b65f67d924,
3900
      0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d,
3901
      0x82818f1281ed449f, 0xbff8f10e7a8921a4,
3902
      0xa321f2d7226895c7, 0xaff72d52192b6a0d,
3903
      0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490,
3904
      0xfee50b7025c36a08, 0x2f236d04753d5b4,
3905
      0x9f4f2726179a2245, 0x1d762422c946590,
3906
      0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5,
3907
      0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2,
3908
      0x9b934c3b330c8577, 0x63cc55f49f88eb2f,
3909
      0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb,
3910
      0xf316271c7fc3908a, 0x8bef464e3945ef7a,
3911
      0x97edd871cfda3a56, 0x97758bf0e3cbb5ac,
3912
      0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317,
3913
      0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd,
3914
      0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a,
3915
      0xb975d6b6ee39e436, 0xb3e2fd538e122b44,
3916
      0xe7d34c64a9c85d44, 0x60dbbca87196b616,
3917
      0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd,
3918
      0xb51d13aea4a488dd, 0x6babab6398bdbe41,
3919
      0xe264589a4dcdab14, 0xc696963c7eed2dd1,
3920
      0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2,
3921
      0xb0de65388cc8ada8, 0x3b25a55f43294bcb,
3922
      0xdd15fe86affad912, 0x49ef0eb713f39ebe,
3923
      0x8a2dbf142dfcc7ab, 0x6e3569326c784337,
3924
      0xacb92ed9397bf996, 0x49c2c37f07965404,
3925
      0xd7e77a8f87daf7fb, 0xdc33745ec97be906,
3926
      0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3,
3927
      0xa8acd7c0222311bc, 0xc40832ea0d68ce0c,
3928
      0xd2d80db02aabd62b, 0xf50a3fa490c30190,
3929
      0x83c7088e1aab65db, 0x792667c6da79e0fa,
3930
      0xa4b8cab1a1563f52, 0x577001b891185938,
3931
      0xcde6fd5e09abcf26, 0xed4c0226b55e6f86,
3932
      0x80b05e5ac60b6178, 0x544f8158315b05b4,
3933
      0xa0dc75f1778e39d6, 0x696361ae3db1c721,
3934
      0xc913936dd571c84c, 0x3bc3a19cd1e38e9,
3935
      0xfb5878494ace3a5f, 0x4ab48a04065c723,
3936
      0x9d174b2dcec0e47b, 0x62eb0d64283f9c76,
3937
      0xc45d1df942711d9a, 0x3ba5d0bd324f8394,
3938
      0xf5746577930d6500, 0xca8f44ec7ee36479,
3939
      0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb,
3940
      0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e,
3941
      0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e,
3942
      0x95d04aee3b80ece5, 0xbba1f1d158724a12,
3943
      0xbb445da9ca61281f, 0x2a8a6e45ae8edc97,
3944
      0xea1575143cf97226, 0xf52d09d71a3293bd,
3945
      0x924d692ca61be758, 0x593c2626705f9c56,
3946
      0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c,
3947
      0xe498f455c38b997a, 0xb6dfb9c0f956447,
3948
      0x8edf98b59a373fec, 0x4724bd4189bd5eac,
3949
      0xb2977ee300c50fe7, 0x58edec91ec2cb657,
3950
      0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed,
3951
      0x8b865b215899f46c, 0xbd79e0d20082ee74,
3952
      0xae67f1e9aec07187, 0xecd8590680a3aa11,
3953
      0xda01ee641a708de9, 0xe80e6f4820cc9495,
3954
      0x884134fe908658b2, 0x3109058d147fdcdd,
3955
      0xaa51823e34a7eede, 0xbd4b46f0599fd415,
3956
      0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a,
3957
      0x850fadc09923329e, 0x3e2cf6bc604ddb0,
3958
      0xa6539930bf6bff45, 0x84db8346b786151c,
3959
      0xcfe87f7cef46ff16, 0xe612641865679a63,
3960
      0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e,
3961
      0xa26da3999aef7749, 0xe3be5e330f38f09d,
3962
      0xcb090c8001ab551c, 0x5cadf5bfd3072cc5,
3963
      0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6,
3964
      0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa,
3965
      0xc646d63501a1511d, 0xb281e1fd541501b8,
3966
      0xf7d88bc24209a565, 0x1f225a7ca91a4226,
3967
      0x9ae757596946075f, 0x3375788de9b06958,
3968
      0xc1a12d2fc3978937, 0x52d6b1641c83ae,
3969
      0xf209787bb47d6b84, 0xc0678c5dbd23a49a,
3970
      0x9745eb4d50ce6332, 0xf840b7ba963646e0,
3971
      0xbd176620a501fbff, 0xb650e5a93bc3d898,
3972
      0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe,
3973
      0x93ba47c980e98cdf, 0xc66f336c36b10137,
3974
      0xb8a8d9bbe123f017, 0xb80b0047445d4184,
3975
      0xe6d3102ad96cec1d, 0xa60dc059157491e5,
3976
      0x9043ea1ac7e41392, 0x87c89837ad68db2f,
3977
      0xb454e4a179dd1877, 0x29babe4598c311fb,
3978
      0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a,
3979
      0x8ce2529e2734bb1d, 0x1899e4a65f58660c,
3980
      0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f,
3981
      0xdc21a1171d42645d, 0x76707543f4fa1f73,
3982
      0x899504ae72497eba, 0x6a06494a791c53a8,
3983
      0xabfa45da0edbde69, 0x487db9d17636892,
3984
      0xd6f8d7509292d603, 0x45a9d2845d3c42b6,
3985
      0x865b86925b9bc5c2, 0xb8a2392ba45a9b2,
3986
      0xa7f26836f282b732, 0x8e6cac7768d7141e,
3987
      0xd1ef0244af2364ff, 0x3207d795430cd926,
3988
      0x8335616aed761f1f, 0x7f44e6bd49e807b8,
3989
      0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6,
3990
      0xcd036837130890a1, 0x36dba887c37a8c0f,
3991
      0x802221226be55a64, 0xc2494954da2c9789,
3992
      0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c,
3993
      0xc83553c5c8965d3d, 0x6f92829494e5acc7,
3994
      0xfa42a8b73abbf48c, 0xcb772339ba1f17f9,
3995
      0x9c69a97284b578d7, 0xff2a760414536efb,
3996
      0xc38413cf25e2d70d, 0xfef5138519684aba,
3997
      0xf46518c2ef5b8cd1, 0x7eb258665fc25d69,
3998
      0x98bf2f79d5993802, 0xef2f773ffbd97a61,
3999
      0xbeeefb584aff8603, 0xaafb550ffacfd8fa,
4000
      0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38,
4001
      0x952ab45cfa97a0b2, 0xdd945a747bf26183,
4002
      0xba756174393d88df, 0x94f971119aeef9e4,
4003
      0xe912b9d1478ceb17, 0x7a37cd5601aab85d,
4004
      0x91abb422ccb812ee, 0xac62e055c10ab33a,
4005
      0xb616a12b7fe617aa, 0x577b986b314d6009,
4006
      0xe39c49765fdf9d94, 0xed5a7e85fda0b80b,
4007
      0x8e41ade9fbebc27d, 0x14588f13be847307,
4008
      0xb1d219647ae6b31c, 0x596eb2d8ae258fc8,
4009
      0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb,
4010
      0x8aec23d680043bee, 0x25de7bb9480d5854,
4011
      0xada72ccc20054ae9, 0xaf561aa79a10ae6a,
4012
      0xd910f7ff28069da4, 0x1b2ba1518094da04,
4013
      0x87aa9aff79042286, 0x90fb44d2f05d0842,
4014
      0xa99541bf57452b28, 0x353a1607ac744a53,
4015
      0xd3fa922f2d1675f2, 0x42889b8997915ce8,
4016
      0x847c9b5d7c2e09b7, 0x69956135febada11,
4017
      0xa59bc234db398c25, 0x43fab9837e699095,
4018
      0xcf02b2c21207ef2e, 0x94f967e45e03f4bb,
4019
      0x8161afb94b44f57d, 0x1d1be0eebac278f5,
4020
      0xa1ba1ba79e1632dc, 0x6462d92a69731732,
4021
      0xca28a291859bbf93, 0x7d7b8f7503cfdcfe,
4022
      0xfcb2cb35e702af78, 0x5cda735244c3d43e,
4023
      0x9defbf01b061adab, 0x3a0888136afa64a7,
4024
      0xc56baec21c7a1916, 0x88aaa1845b8fdd0,
4025
      0xf6c69a72a3989f5b, 0x8aad549e57273d45,
4026
      0x9a3c2087a63f6399, 0x36ac54e2f678864b,
4027
      0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd,
4028
      0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5,
4029
      0x969eb7c47859e743, 0x9f644ae5a4b1b325,
4030
      0xbc4665b596706114, 0x873d5d9f0dde1fee,
4031
      0xeb57ff22fc0c7959, 0xa90cb506d155a7ea,
4032
      0x9316ff75dd87cbd8, 0x9a7f12442d588f2,
4033
      0xb7dcbf5354e9bece, 0xc11ed6d538aeb2f,
4034
      0xe5d3ef282a242e81, 0x8f1668c8a86da5fa,
4035
      0x8fa475791a569d10, 0xf96e017d694487bc,
4036
      0xb38d92d760ec4455, 0x37c981dcc395a9ac,
4037
      0xe070f78d3927556a, 0x85bbe253f47b1417,
4038
      0x8c469ab843b89562, 0x93956d7478ccec8e,
4039
      0xaf58416654a6babb, 0x387ac8d1970027b2,
4040
      0xdb2e51bfe9d0696a, 0x6997b05fcc0319e,
4041
      0x88fcf317f22241e2, 0x441fece3bdf81f03,
4042
      0xab3c2fddeeaad25a, 0xd527e81cad7626c3,
4043
      0xd60b3bd56a5586f1, 0x8a71e223d8d3b074,
4044
      0x85c7056562757456, 0xf6872d5667844e49,
4045
      0xa738c6bebb12d16c, 0xb428f8ac016561db,
4046
      0xd106f86e69d785c7, 0xe13336d701beba52,
4047
      0x82a45b450226b39c, 0xecc0024661173473,
4048
      0xa34d721642b06084, 0x27f002d7f95d0190,
4049
      0xcc20ce9bd35c78a5, 0x31ec038df7b441f4,
4050
      0xff290242c83396ce, 0x7e67047175a15271,
4051
      0x9f79a169bd203e41, 0xf0062c6e984d386,
4052
      0xc75809c42c684dd1, 0x52c07b78a3e60868,
4053
      0xf92e0c3537826145, 0xa7709a56ccdf8a82,
4054
      0x9bbcc7a142b17ccb, 0x88a66076400bb691,
4055
      0xc2abf989935ddbfe, 0x6acff893d00ea435,
4056
      0xf356f7ebf83552fe, 0x583f6b8c4124d43,
4057
      0x98165af37b2153de, 0xc3727a337a8b704a,
4058
      0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c,
4059
      0xeda2ee1c7064130c, 0x1162def06f79df73,
4060
      0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8,
4061
      0xb9a74a0637ce2ee1, 0x6d953e2bd7173692,
4062
      0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437,
4063
      0x910ab1d4db9914a0, 0x1d9c9892400a22a2,
4064
      0xb54d5e4a127f59c8, 0x2503beb6d00cab4b,
4065
      0xe2a0b5dc971f303a, 0x2e44ae64840fd61d,
4066
      0x8da471a9de737e24, 0x5ceaecfed289e5d2,
4067
      0xb10d8e1456105dad, 0x7425a83e872c5f47,
4068
      0xdd50f1996b947518, 0xd12f124e28f77719,
4069
      0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f,
4070
      0xace73cbfdc0bfb7b, 0x636cc64d1001550b,
4071
      0xd8210befd30efa5a, 0x3c47f7e05401aa4e,
4072
      0x8714a775e3e95c78, 0x65acfaec34810a71,
4073
      0xa8d9d1535ce3b396, 0x7f1839a741a14d0d,
4074
      0xd31045a8341ca07c, 0x1ede48111209a050,
4075
      0x83ea2b892091e44d, 0x934aed0aab460432,
4076
      0xa4e4b66b68b65d60, 0xf81da84d5617853f,
4077
      0xce1de40642e3f4b9, 0x36251260ab9d668e,
4078
      0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019,
4079
      0xa1075a24e4421730, 0xb24cf65b8612f81f,
4080
      0xc94930ae1d529cfc, 0xdee033f26797b627,
4081
      0xfb9b7cd9a4a7443c, 0x169840ef017da3b1,
4082
      0x9d412e0806e88aa5, 0x8e1f289560ee864e,
4083
      0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2,
4084
      0xf5b5d7ec8acb58a2, 0xae10af696774b1db,
4085
      0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29,
4086
      0xbff610b0cc6edd3f, 0x17fd090a58d32af3,
4087
      0xeff394dcff8a948e, 0xddfc4b4cef07f5b0,
4088
      0x95f83d0a1fb69cd9, 0x4abdaf101564f98e,
4089
      0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1,
4090
      0xea53df5fd18d5513, 0x84c86189216dc5ed,
4091
      0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4,
4092
      0xb7118682dbb66a77, 0x3fbc8c33221dc2a1,
4093
      0xe4d5e82392a40515, 0xfabaf3feaa5334a,
4094
      0x8f05b1163ba6832d, 0x29cb4d87f2a7400e,
4095
      0xb2c71d5bca9023f8, 0x743e20e9ef511012,
4096
      0xdf78e4b2bd342cf6, 0x914da9246b255416,
4097
      0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e,
4098
      0xae9672aba3d0c320, 0xa184ac2473b529b1,
4099
      0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e,
4100
      0x8865899617fb1871, 0x7e2fa67c7a658892,
4101
      0xaa7eebfb9df9de8d, 0xddbb901b98feeab7,
4102
      0xd51ea6fa85785631, 0x552a74227f3ea565,
4103
      0x8533285c936b35de, 0xd53a88958f87275f,
4104
      0xa67ff273b8460356, 0x8a892abaf368f137,
4105
      0xd01fef10a657842c, 0x2d2b7569b0432d85,
4106
      0x8213f56a67f6b29b, 0x9c3b29620e29fc73,
4107
      0xa298f2c501f45f42, 0x8349f3ba91b47b8f,
4108
      0xcb3f2f7642717713, 0x241c70a936219a73,
4109
      0xfe0efb53d30dd4d7, 0xed238cd383aa0110,
4110
      0x9ec95d1463e8a506, 0xf4363804324a40aa,
4111
      0xc67bb4597ce2ce48, 0xb143c6053edcd0d5,
4112
      0xf81aa16fdc1b81da, 0xdd94b7868e94050a,
4113
      0x9b10a4e5e9913128, 0xca7cf2b4191c8326,
4114
      0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0,
4115
      0xf24a01a73cf2dccf, 0xbc633b39673c8cec,
4116
      0x976e41088617ca01, 0xd5be0503e085d813,
4117
      0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18,
4118
      0xec9c459d51852ba2, 0xddf8e7d60ed1219e,
4119
      0x93e1ab8252f33b45, 0xcabb90e5c942b503,
4120
      0xb8da1662e7b00a17, 0x3d6a751f3b936243,
4121
      0xe7109bfba19c0c9d, 0xcc512670a783ad4,
4122
      0x906a617d450187e2, 0x27fb2b80668b24c5,
4123
      0xb484f9dc9641e9da, 0xb1f9f660802dedf6,
4124
      0xe1a63853bbd26451, 0x5e7873f8a0396973,
4125
      0x8d07e33455637eb2, 0xdb0b487b6423e1e8,
4126
      0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62,
4127
      0xdc5c5301c56b75f7, 0x7641a140cc7810fb,
4128
      0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d,
4129
      0xac2820d9623bf429, 0x546345fa9fbdcd44,
4130
      0xd732290fbacaf133, 0xa97c177947ad4095,
4131
      0x867f59a9d4bed6c0, 0x49ed8eabcccc485d,
4132
      0xa81f301449ee8c70, 0x5c68f256bfff5a74,
4133
      0xd226fc195c6a2f8c, 0x73832eec6fff3111,
4134
      0x83585d8fd9c25db7, 0xc831fd53c5ff7eab,
4135
      0xa42e74f3d032f525, 0xba3e7ca8b77f5e55,
4136
      0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb,
4137
      0x80444b5e7aa7cf85, 0x7980d163cf5b81b3,
4138
      0xa0555e361951c366, 0xd7e105bcc332621f,
4139
      0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7,
4140
      0xfa856334878fc150, 0xb14f98f6f0feb951,
4141
      0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3,
4142
      0xc3b8358109e84f07, 0xa862f80ec4700c8,
4143
      0xf4a642e14c6262c8, 0xcd27bb612758c0fa,
4144
      0x98e7e9cccfbd7dbd, 0x8038d51cb897789c,
4145
      0xbf21e44003acdd2c, 0xe0470a63e6bd56c3,
4146
      0xeeea5d5004981478, 0x1858ccfce06cac74,
4147
      0x95527a5202df0ccb, 0xf37801e0c43ebc8,
4148
      0xbaa718e68396cffd, 0xd30560258f54e6ba,
4149
      0xe950df20247c83fd, 0x47c6b82ef32a2069,
4150
      0x91d28b7416cdd27e, 0x4cdc331d57fa5441,
4151
      0xb6472e511c81471d, 0xe0133fe4adf8e952,
4152
      0xe3d8f9e563a198e5, 0x58180fddd97723a6,
4153
      0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648,
4154
  };
4155
};
4156
4157
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
4158
4159
template <class unused>
4160
constexpr uint64_t
4161
    powers_template<unused>::power_of_five_128[number_of_entries];
4162
4163
#endif
4164
4165
using powers = powers_template<>;
4166
4167
} // namespace fast_float
4168
4169
#endif
4170
4171
#ifndef FASTFLOAT_DECIMAL_TO_BINARY_H
4172
#define FASTFLOAT_DECIMAL_TO_BINARY_H
4173
4174
#include <cmath>
4175
#include <cstdlib>
4176
#include <cstring>
4177
4178
namespace fast_float {
4179
4180
// This will compute or rather approximate w * 5**q and return a pair of 64-bit
4181
// words approximating the result, with the "high" part corresponding to the
4182
// most significant bits and the low part corresponding to the least significant
4183
// bits.
4184
//
4185
template <int bit_precision>
4186
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
4187
171k
compute_product_approximation(int64_t q, uint64_t w) {
4188
171k
  int const index = 2 * int(q - powers::smallest_power_of_five);
4189
  // For small values of q, e.g., q in [0,27], the answer is always exact
4190
  // because The line value128 firstproduct = full_multiplication(w,
4191
  // power_of_five_128[index]); gives the exact answer.
4192
171k
  value128 firstproduct =
4193
171k
      full_multiplication(w, powers::power_of_five_128[index]);
4194
171k
  static_assert((bit_precision >= 0) && (bit_precision <= 64),
4195
171k
                " precision should  be in (0,64]");
4196
171k
  constexpr uint64_t precision_mask =
4197
171k
      (bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
4198
171k
                           : uint64_t(0xFFFFFFFFFFFFFFFF);
4199
171k
  if ((firstproduct.high & precision_mask) ==
4200
171k
      precision_mask) { // could further guard with  (lower + w < lower)
4201
    // regarding the second product, we only need secondproduct.high, but our
4202
    // expectation is that the compiler will optimize this extra work away if
4203
    // needed.
4204
38.0k
    value128 secondproduct =
4205
38.0k
        full_multiplication(w, powers::power_of_five_128[index + 1]);
4206
38.0k
    firstproduct.low += secondproduct.high;
4207
38.0k
    if (secondproduct.high > firstproduct.low) {
4208
23.0k
      firstproduct.high++;
4209
23.0k
    }
4210
38.0k
  }
4211
171k
  return firstproduct;
4212
171k
}
fast_float::value128 fast_float::compute_product_approximation<55>(long, unsigned long)
Line
Count
Source
4187
171k
compute_product_approximation(int64_t q, uint64_t w) {
4188
171k
  int const index = 2 * int(q - powers::smallest_power_of_five);
4189
  // For small values of q, e.g., q in [0,27], the answer is always exact
4190
  // because The line value128 firstproduct = full_multiplication(w,
4191
  // power_of_five_128[index]); gives the exact answer.
4192
171k
  value128 firstproduct =
4193
171k
      full_multiplication(w, powers::power_of_five_128[index]);
4194
171k
  static_assert((bit_precision >= 0) && (bit_precision <= 64),
4195
171k
                " precision should  be in (0,64]");
4196
171k
  constexpr uint64_t precision_mask =
4197
171k
      (bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
4198
171k
                           : uint64_t(0xFFFFFFFFFFFFFFFF);
4199
171k
  if ((firstproduct.high & precision_mask) ==
4200
171k
      precision_mask) { // could further guard with  (lower + w < lower)
4201
    // regarding the second product, we only need secondproduct.high, but our
4202
    // expectation is that the compiler will optimize this extra work away if
4203
    // needed.
4204
38.0k
    value128 secondproduct =
4205
38.0k
        full_multiplication(w, powers::power_of_five_128[index + 1]);
4206
38.0k
    firstproduct.low += secondproduct.high;
4207
38.0k
    if (secondproduct.high > firstproduct.low) {
4208
23.0k
      firstproduct.high++;
4209
23.0k
    }
4210
38.0k
  }
4211
171k
  return firstproduct;
4212
171k
}
Unexecuted instantiation: fast_float::value128 fast_float::compute_product_approximation<26>(long, unsigned long)
4213
4214
namespace detail {
4215
/**
4216
 * For q in (0,350), we have that
4217
 *  f = (((152170 + 65536) * q ) >> 16);
4218
 * is equal to
4219
 *   floor(p) + q
4220
 * where
4221
 *   p = log(5**q)/log(2) = q * log(5)/log(2)
4222
 *
4223
 * For negative values of q in (-400,0), we have that
4224
 *  f = (((152170 + 65536) * q ) >> 16);
4225
 * is equal to
4226
 *   -ceil(p) + q
4227
 * where
4228
 *   p = log(5**-q)/log(2) = -q * log(5)/log(2)
4229
 */
4230
171k
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
4231
171k
  return (((152170 + 65536) * q) >> 16) + 63;
4232
171k
}
4233
} // namespace detail
4234
4235
// create an adjusted mantissa, biased by the invalid power2
4236
// for significant digits already multiplied by 10 ** q.
4237
template <typename binary>
4238
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa
4239
35.1k
compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
4240
35.1k
  int hilz = int(w >> 63) ^ 1;
4241
35.1k
  adjusted_mantissa answer;
4242
35.1k
  answer.mantissa = w << hilz;
4243
35.1k
  int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent();
4244
35.1k
  answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 +
4245
35.1k
                          invalid_am_bias);
4246
35.1k
  return answer;
4247
35.1k
}
fast_float::adjusted_mantissa fast_float::compute_error_scaled<fast_float::binary_format<double> >(long, unsigned long, int)
Line
Count
Source
4239
35.1k
compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
4240
35.1k
  int hilz = int(w >> 63) ^ 1;
4241
35.1k
  adjusted_mantissa answer;
4242
35.1k
  answer.mantissa = w << hilz;
4243
35.1k
  int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent();
4244
35.1k
  answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 +
4245
35.1k
                          invalid_am_bias);
4246
35.1k
  return answer;
4247
35.1k
}
Unexecuted instantiation: fast_float::adjusted_mantissa fast_float::compute_error_scaled<fast_float::binary_format<float> >(long, unsigned long, int)
4248
4249
// w * 10 ** q, without rounding the representation up.
4250
// the power2 in the exponent will be adjusted by invalid_am_bias.
4251
template <typename binary>
4252
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
4253
35.1k
compute_error(int64_t q, uint64_t w) noexcept {
4254
35.1k
  int lz = leading_zeroes(w);
4255
35.1k
  w <<= lz;
4256
35.1k
  value128 product =
4257
35.1k
      compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
4258
35.1k
  return compute_error_scaled<binary>(q, product.high, lz);
4259
35.1k
}
fast_float::adjusted_mantissa fast_float::compute_error<fast_float::binary_format<double> >(long, unsigned long)
Line
Count
Source
4253
35.1k
compute_error(int64_t q, uint64_t w) noexcept {
4254
35.1k
  int lz = leading_zeroes(w);
4255
35.1k
  w <<= lz;
4256
35.1k
  value128 product =
4257
35.1k
      compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
4258
35.1k
  return compute_error_scaled<binary>(q, product.high, lz);
4259
35.1k
}
Unexecuted instantiation: fast_float::adjusted_mantissa fast_float::compute_error<fast_float::binary_format<float> >(long, unsigned long)
4260
4261
// Computers w * 10 ** q.
4262
// The returned value should be a valid number that simply needs to be
4263
// packed. However, in some very rare cases, the computation will fail. In such
4264
// cases, we return an adjusted_mantissa with a negative power of 2: the caller
4265
// should recompute in such cases.
4266
template <typename binary>
4267
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
4268
143k
compute_float(int64_t q, uint64_t w) noexcept {
4269
143k
  adjusted_mantissa answer;
4270
143k
  if ((w == 0) || (q < binary::smallest_power_of_ten())) {
4271
2.41k
    answer.power2 = 0;
4272
2.41k
    answer.mantissa = 0;
4273
    // result should be zero
4274
2.41k
    return answer;
4275
2.41k
  }
4276
141k
  if (q > binary::largest_power_of_ten()) {
4277
    // we want to get infinity:
4278
4.80k
    answer.power2 = binary::infinite_power();
4279
4.80k
    answer.mantissa = 0;
4280
4.80k
    return answer;
4281
4.80k
  }
4282
  // At this point in time q is in [powers::smallest_power_of_five,
4283
  // powers::largest_power_of_five].
4284
4285
  // We want the most significant bit of i to be 1. Shift if needed.
4286
136k
  int lz = leading_zeroes(w);
4287
136k
  w <<= lz;
4288
4289
  // The required precision is binary::mantissa_explicit_bits() + 3 because
4290
  // 1. We need the implicit bit
4291
  // 2. We need an extra bit for rounding purposes
4292
  // 3. We might lose a bit due to the "upperbit" routine (result too small,
4293
  // requiring a shift)
4294
4295
136k
  value128 product =
4296
136k
      compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
4297
  // The computed 'product' is always sufficient.
4298
  // Mathematical proof:
4299
  // Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to
4300
  // appear) See script/mushtak_lemire.py
4301
4302
  // The "compute_product_approximation" function can be slightly slower than a
4303
  // branchless approach: value128 product = compute_product(q, w); but in
4304
  // practice, we can win big with the compute_product_approximation if its
4305
  // additional branch is easily predicted. Which is best is data specific.
4306
136k
  int upperbit = int(product.high >> 63);
4307
136k
  int shift = upperbit + 64 - binary::mantissa_explicit_bits() - 3;
4308
4309
136k
  answer.mantissa = product.high >> shift;
4310
4311
136k
  answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz -
4312
136k
                          binary::minimum_exponent());
4313
136k
  if (answer.power2 <= 0) { // we have a subnormal?
4314
    // Here have that answer.power2 <= 0 so -answer.power2 >= 0
4315
6.75k
    if (-answer.power2 + 1 >=
4316
6.75k
        64) { // if we have more than 64 bits below the minimum exponent, you
4317
              // have a zero for sure.
4318
477
      answer.power2 = 0;
4319
477
      answer.mantissa = 0;
4320
      // result should be zero
4321
477
      return answer;
4322
477
    }
4323
    // next line is safe because -answer.power2 + 1 < 64
4324
6.28k
    answer.mantissa >>= -answer.power2 + 1;
4325
    // Thankfully, we can't have both "round-to-even" and subnormals because
4326
    // "round-to-even" only occurs for powers close to 0 in the 32-bit and
4327
    // and 64-bit case (with no more than 19 digits).
4328
6.28k
    answer.mantissa += (answer.mantissa & 1); // round up
4329
6.28k
    answer.mantissa >>= 1;
4330
    // There is a weird scenario where we don't have a subnormal but just.
4331
    // Suppose we start with 2.2250738585072013e-308, we end up
4332
    // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
4333
    // whereas 0x40000000000000 x 2^-1023-53  is normal. Now, we need to round
4334
    // up 0x3fffffffffffff x 2^-1023-53  and once we do, we are no longer
4335
    // subnormal, but we can only know this after rounding.
4336
    // So we only declare a subnormal if we are smaller than the threshold.
4337
6.28k
    answer.power2 =
4338
6.28k
        (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits()))
4339
6.28k
            ? 0
4340
6.28k
            : 1;
4341
6.28k
    return answer;
4342
6.75k
  }
4343
4344
  // usually, we round *up*, but if we fall right in between and and we have an
4345
  // even basis, we need to round down
4346
  // We are only concerned with the cases where 5**q fits in single 64-bit word.
4347
129k
  if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) &&
4348
17.5k
      (q <= binary::max_exponent_round_to_even()) &&
4349
17.2k
      ((answer.mantissa & 3) == 1)) { // we may fall between two floats!
4350
    // To be in-between two floats we need that in doing
4351
    //   answer.mantissa = product.high >> (upperbit + 64 -
4352
    //   binary::mantissa_explicit_bits() - 3);
4353
    // ... we dropped out only zeroes. But if this happened, then we can go
4354
    // back!!!
4355
12.2k
    if ((answer.mantissa << shift) == product.high) {
4356
7.08k
      answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up
4357
7.08k
    }
4358
12.2k
  }
4359
4360
129k
  answer.mantissa += (answer.mantissa & 1); // round up
4361
129k
  answer.mantissa >>= 1;
4362
129k
  if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) {
4363
4.05k
    answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits());
4364
4.05k
    answer.power2++; // undo previous addition
4365
4.05k
  }
4366
4367
129k
  answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits());
4368
129k
  if (answer.power2 >= binary::infinite_power()) { // infinity
4369
2.13k
    answer.power2 = binary::infinite_power();
4370
2.13k
    answer.mantissa = 0;
4371
2.13k
  }
4372
129k
  return answer;
4373
136k
}
fast_float::adjusted_mantissa fast_float::compute_float<fast_float::binary_format<double> >(long, unsigned long)
Line
Count
Source
4268
143k
compute_float(int64_t q, uint64_t w) noexcept {
4269
143k
  adjusted_mantissa answer;
4270
143k
  if ((w == 0) || (q < binary::smallest_power_of_ten())) {
4271
2.41k
    answer.power2 = 0;
4272
2.41k
    answer.mantissa = 0;
4273
    // result should be zero
4274
2.41k
    return answer;
4275
2.41k
  }
4276
141k
  if (q > binary::largest_power_of_ten()) {
4277
    // we want to get infinity:
4278
4.80k
    answer.power2 = binary::infinite_power();
4279
4.80k
    answer.mantissa = 0;
4280
4.80k
    return answer;
4281
4.80k
  }
4282
  // At this point in time q is in [powers::smallest_power_of_five,
4283
  // powers::largest_power_of_five].
4284
4285
  // We want the most significant bit of i to be 1. Shift if needed.
4286
136k
  int lz = leading_zeroes(w);
4287
136k
  w <<= lz;
4288
4289
  // The required precision is binary::mantissa_explicit_bits() + 3 because
4290
  // 1. We need the implicit bit
4291
  // 2. We need an extra bit for rounding purposes
4292
  // 3. We might lose a bit due to the "upperbit" routine (result too small,
4293
  // requiring a shift)
4294
4295
136k
  value128 product =
4296
136k
      compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
4297
  // The computed 'product' is always sufficient.
4298
  // Mathematical proof:
4299
  // Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to
4300
  // appear) See script/mushtak_lemire.py
4301
4302
  // The "compute_product_approximation" function can be slightly slower than a
4303
  // branchless approach: value128 product = compute_product(q, w); but in
4304
  // practice, we can win big with the compute_product_approximation if its
4305
  // additional branch is easily predicted. Which is best is data specific.
4306
136k
  int upperbit = int(product.high >> 63);
4307
136k
  int shift = upperbit + 64 - binary::mantissa_explicit_bits() - 3;
4308
4309
136k
  answer.mantissa = product.high >> shift;
4310
4311
136k
  answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz -
4312
136k
                          binary::minimum_exponent());
4313
136k
  if (answer.power2 <= 0) { // we have a subnormal?
4314
    // Here have that answer.power2 <= 0 so -answer.power2 >= 0
4315
6.75k
    if (-answer.power2 + 1 >=
4316
6.75k
        64) { // if we have more than 64 bits below the minimum exponent, you
4317
              // have a zero for sure.
4318
477
      answer.power2 = 0;
4319
477
      answer.mantissa = 0;
4320
      // result should be zero
4321
477
      return answer;
4322
477
    }
4323
    // next line is safe because -answer.power2 + 1 < 64
4324
6.28k
    answer.mantissa >>= -answer.power2 + 1;
4325
    // Thankfully, we can't have both "round-to-even" and subnormals because
4326
    // "round-to-even" only occurs for powers close to 0 in the 32-bit and
4327
    // and 64-bit case (with no more than 19 digits).
4328
6.28k
    answer.mantissa += (answer.mantissa & 1); // round up
4329
6.28k
    answer.mantissa >>= 1;
4330
    // There is a weird scenario where we don't have a subnormal but just.
4331
    // Suppose we start with 2.2250738585072013e-308, we end up
4332
    // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
4333
    // whereas 0x40000000000000 x 2^-1023-53  is normal. Now, we need to round
4334
    // up 0x3fffffffffffff x 2^-1023-53  and once we do, we are no longer
4335
    // subnormal, but we can only know this after rounding.
4336
    // So we only declare a subnormal if we are smaller than the threshold.
4337
6.28k
    answer.power2 =
4338
6.28k
        (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits()))
4339
6.28k
            ? 0
4340
6.28k
            : 1;
4341
6.28k
    return answer;
4342
6.75k
  }
4343
4344
  // usually, we round *up*, but if we fall right in between and and we have an
4345
  // even basis, we need to round down
4346
  // We are only concerned with the cases where 5**q fits in single 64-bit word.
4347
129k
  if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) &&
4348
17.5k
      (q <= binary::max_exponent_round_to_even()) &&
4349
17.2k
      ((answer.mantissa & 3) == 1)) { // we may fall between two floats!
4350
    // To be in-between two floats we need that in doing
4351
    //   answer.mantissa = product.high >> (upperbit + 64 -
4352
    //   binary::mantissa_explicit_bits() - 3);
4353
    // ... we dropped out only zeroes. But if this happened, then we can go
4354
    // back!!!
4355
12.2k
    if ((answer.mantissa << shift) == product.high) {
4356
7.08k
      answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up
4357
7.08k
    }
4358
12.2k
  }
4359
4360
129k
  answer.mantissa += (answer.mantissa & 1); // round up
4361
129k
  answer.mantissa >>= 1;
4362
129k
  if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) {
4363
4.05k
    answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits());
4364
4.05k
    answer.power2++; // undo previous addition
4365
4.05k
  }
4366
4367
129k
  answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits());
4368
129k
  if (answer.power2 >= binary::infinite_power()) { // infinity
4369
2.13k
    answer.power2 = binary::infinite_power();
4370
2.13k
    answer.mantissa = 0;
4371
2.13k
  }
4372
129k
  return answer;
4373
136k
}
Unexecuted instantiation: fast_float::adjusted_mantissa fast_float::compute_float<fast_float::binary_format<float> >(long, unsigned long)
4374
4375
} // namespace fast_float
4376
4377
#endif
4378
4379
#ifndef FASTFLOAT_BIGINT_H
4380
#define FASTFLOAT_BIGINT_H
4381
4382
#include <cstring>
4383
4384
4385
namespace fast_float {
4386
4387
// the limb width: we want efficient multiplication of double the bits in
4388
// limb, or for 64-bit limbs, at least 64-bit multiplication where we can
4389
// extract the high and low parts efficiently. this is every 64-bit
4390
// architecture except for sparc, which emulates 128-bit multiplication.
4391
// we might have platforms where `CHAR_BIT` is not 8, so let's avoid
4392
// doing `8 * sizeof(limb)`.
4393
#if defined(FASTFLOAT_64BIT) && !defined(__sparc)
4394
#define FASTFLOAT_64BIT_LIMB 1
4395
typedef uint64_t limb;
4396
constexpr size_t limb_bits = 64;
4397
#else
4398
#define FASTFLOAT_32BIT_LIMB
4399
typedef uint32_t limb;
4400
constexpr size_t limb_bits = 32;
4401
#endif
4402
4403
typedef span<limb> limb_span;
4404
4405
// number of bits in a bigint. this needs to be at least the number
4406
// of bits required to store the largest bigint, which is
4407
// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
4408
// ~3600 bits, so we round to 4000.
4409
constexpr size_t bigint_bits = 4000;
4410
constexpr size_t bigint_limbs = bigint_bits / limb_bits;
4411
4412
// vector-like type that is allocated on the stack. the entire
4413
// buffer is pre-allocated, and only the length changes.
4414
template <uint16_t size> struct stackvec {
4415
  limb data[size];
4416
  // we never need more than 150 limbs
4417
  uint16_t length{0};
4418
4419
252k
  stackvec() = default;
4420
  stackvec(stackvec const &) = delete;
4421
  stackvec &operator=(stackvec const &) = delete;
4422
  stackvec(stackvec &&) = delete;
4423
  stackvec &operator=(stackvec &&other) = delete;
4424
4425
  // create stack vector from existing limb span.
4426
48.7k
  FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) {
4427
48.7k
    FASTFLOAT_ASSERT(try_extend(s));
4428
48.7k
  }
4429
4430
25.1M
  FASTFLOAT_CONSTEXPR14 limb &operator[](size_t index) noexcept {
4431
25.1M
    FASTFLOAT_DEBUG_ASSERT(index < length);
4432
25.1M
    return data[index];
4433
25.1M
  }
4434
4435
154k
  FASTFLOAT_CONSTEXPR14 const limb &operator[](size_t index) const noexcept {
4436
154k
    FASTFLOAT_DEBUG_ASSERT(index < length);
4437
154k
    return data[index];
4438
154k
  }
4439
4440
  // index from the end of the container
4441
110k
  FASTFLOAT_CONSTEXPR14 const limb &rindex(size_t index) const noexcept {
4442
110k
    FASTFLOAT_DEBUG_ASSERT(index < length);
4443
110k
    size_t rindex = length - index - 1;
4444
110k
    return data[rindex];
4445
110k
  }
4446
4447
  // set the length, without bounds checking.
4448
652k
  FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
4449
652k
    length = uint16_t(len);
4450
652k
  }
4451
4452
13.9M
  constexpr size_t len() const noexcept { return length; }
4453
4454
32.9k
  constexpr bool is_empty() const noexcept { return length == 0; }
4455
4456
1.15M
  constexpr size_t capacity() const noexcept { return size; }
4457
4458
  // append item to vector, without bounds checking
4459
723k
  FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
4460
723k
    data[length] = value;
4461
723k
    length++;
4462
723k
  }
4463
4464
  // append item to vector, returning if item was added
4465
701k
  FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept {
4466
701k
    if (len() < capacity()) {
4467
701k
      push_unchecked(value);
4468
701k
      return true;
4469
701k
    } else {
4470
0
      return false;
4471
0
    }
4472
701k
  }
4473
4474
  // add items to the vector, from a span, without bounds checking
4475
243k
  FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
4476
243k
    limb *ptr = data + length;
4477
243k
    tinyobj_ff::copy_n(s.ptr, s.len(), ptr);
4478
243k
    set_len(len() + s.len());
4479
243k
  }
4480
4481
  // try to add items to the vector, returning if items were added
4482
243k
  FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept {
4483
243k
    if (len() + s.len() <= capacity()) {
4484
243k
      extend_unchecked(s);
4485
243k
      return true;
4486
243k
    } else {
4487
0
      return false;
4488
0
    }
4489
243k
  }
4490
4491
  // resize the vector, without bounds checking
4492
  // if the new size is longer than the vector, assign value to each
4493
  // appended item.
4494
  FASTFLOAT_CONSTEXPR20
4495
193k
  void resize_unchecked(size_t new_len, limb value) noexcept {
4496
193k
    if (new_len > len()) {
4497
193k
      size_t count = new_len - len();
4498
193k
      limb *first = data + len();
4499
193k
      limb *last = first + count;
4500
193k
      tinyobj_ff::fill(first, last, value);
4501
193k
      set_len(new_len);
4502
193k
    } else {
4503
0
      set_len(new_len);
4504
0
    }
4505
193k
  }
4506
4507
  // try to resize the vector, returning if the vector was resized.
4508
193k
  FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept {
4509
193k
    if (new_len > capacity()) {
4510
0
      return false;
4511
193k
    } else {
4512
193k
      resize_unchecked(new_len, value);
4513
193k
      return true;
4514
193k
    }
4515
193k
  }
4516
4517
  // check if any limbs are non-zero after the given index.
4518
  // this needs to be done in reverse order, since the index
4519
  // is relative to the most significant limbs.
4520
7.55k
  FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept {
4521
10.3k
    while (index < len()) {
4522
6.74k
      if (rindex(index) != 0) {
4523
3.94k
        return true;
4524
3.94k
      }
4525
2.79k
      index++;
4526
2.79k
    }
4527
3.60k
    return false;
4528
7.55k
  }
4529
4530
  // normalize the big integer, so most-significant zero limbs are removed.
4531
71.4k
  FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
4532
71.4k
    while (len() > 0 && rindex(0) == 0) {
4533
0
      length--;
4534
0
    }
4535
71.4k
  }
4536
};
4537
4538
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
4539
0
empty_hi64(bool &truncated) noexcept {
4540
0
  truncated = false;
4541
0
  return 0;
4542
0
}
4543
4544
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
4545
4.90k
uint64_hi64(uint64_t r0, bool &truncated) noexcept {
4546
4.90k
  truncated = false;
4547
4.90k
  int shl = leading_zeroes(r0);
4548
4.90k
  return r0 << shl;
4549
4.90k
}
4550
4551
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
4552
7.55k
uint64_hi64(uint64_t r0, uint64_t r1, bool &truncated) noexcept {
4553
7.55k
  int shl = leading_zeroes(r0);
4554
7.55k
  if (shl == 0) {
4555
439
    truncated = r1 != 0;
4556
439
    return r0;
4557
7.11k
  } else {
4558
7.11k
    int shr = 64 - shl;
4559
7.11k
    truncated = (r1 << shl) != 0;
4560
7.11k
    return (r0 << shl) | (r1 >> shr);
4561
7.11k
  }
4562
7.55k
}
4563
4564
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
4565
0
uint32_hi64(uint32_t r0, bool &truncated) noexcept {
4566
0
  return uint64_hi64(r0, truncated);
4567
0
}
4568
4569
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
4570
0
uint32_hi64(uint32_t r0, uint32_t r1, bool &truncated) noexcept {
4571
0
  uint64_t x0 = r0;
4572
0
  uint64_t x1 = r1;
4573
0
  return uint64_hi64((x0 << 32) | x1, truncated);
4574
0
}
4575
4576
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
4577
0
uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool &truncated) noexcept {
4578
0
  uint64_t x0 = r0;
4579
0
  uint64_t x1 = r1;
4580
0
  uint64_t x2 = r2;
4581
0
  return uint64_hi64(x0, (x1 << 32) | x2, truncated);
4582
0
}
4583
4584
// add two small integers, checking for overflow.
4585
// we want an efficient operation. for msvc, where
4586
// we don't have built-in intrinsics, this is still
4587
// pretty fast.
4588
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb
4589
3.20M
scalar_add(limb x, limb y, bool &overflow) noexcept {
4590
3.20M
  limb z;
4591
// gcc and clang
4592
3.20M
#if defined(__has_builtin)
4593
3.20M
#if __has_builtin(__builtin_add_overflow)
4594
3.20M
  if (!cpp20_and_in_constexpr()) {
4595
3.20M
    overflow = __builtin_add_overflow(x, y, &z);
4596
3.20M
    return z;
4597
3.20M
  }
4598
0
#endif
4599
0
#endif
4600
4601
  // generic, this still optimizes correctly on MSVC.
4602
0
  z = x + y;
4603
0
  overflow = z < x;
4604
0
  return z;
4605
3.20M
}
4606
4607
// multiply two small integers, getting both the high and low bits.
4608
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb
4609
9.96M
scalar_mul(limb x, limb y, limb &carry) noexcept {
4610
9.96M
#ifdef FASTFLOAT_64BIT_LIMB
4611
9.96M
#if defined(__SIZEOF_INT128__)
4612
  // GCC and clang both define it as an extension.
4613
9.96M
  __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry);
4614
9.96M
  carry = limb(z >> limb_bits);
4615
9.96M
  return limb(z);
4616
#else
4617
  // fallback, no native 128-bit integer multiplication with carry.
4618
  // on msvc, this optimizes identically, somehow.
4619
  value128 z = full_multiplication(x, y);
4620
  bool overflow;
4621
  z.low = scalar_add(z.low, carry, overflow);
4622
  z.high += uint64_t(overflow); // cannot overflow
4623
  carry = z.high;
4624
  return z.low;
4625
#endif
4626
#else
4627
  uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry);
4628
  carry = limb(z >> limb_bits);
4629
  return limb(z);
4630
#endif
4631
9.96M
}
4632
4633
// add scalar value to bigint starting from offset.
4634
// used in grade school multiplication
4635
template <uint16_t size>
4636
inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
4637
420k
                                                 size_t start) noexcept {
4638
420k
  size_t index = start;
4639
420k
  limb carry = y;
4640
420k
  bool overflow;
4641
548k
  while (carry != 0 && index < vec.len()) {
4642
128k
    vec[index] = scalar_add(vec[index], carry, overflow);
4643
128k
    carry = limb(overflow);
4644
128k
    index += 1;
4645
128k
  }
4646
420k
  if (carry != 0) {
4647
35.1k
    FASTFLOAT_TRY(vec.try_push(carry));
4648
35.1k
  }
4649
420k
  return true;
4650
420k
}
4651
4652
// add scalar value to bigint.
4653
template <uint16_t size>
4654
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
4655
420k
small_add(stackvec<size> &vec, limb y) noexcept {
4656
420k
  return small_add_from(vec, y, 0);
4657
420k
}
4658
4659
// multiply bigint by scalar value.
4660
template <uint16_t size>
4661
inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
4662
733k
                                            limb y) noexcept {
4663
733k
  limb carry = 0;
4664
10.7M
  for (size_t index = 0; index < vec.len(); index++) {
4665
9.96M
    vec[index] = scalar_mul(vec[index], y, carry);
4666
9.96M
  }
4667
733k
  if (carry != 0) {
4668
659k
    FASTFLOAT_TRY(vec.try_push(carry));
4669
659k
  }
4670
733k
  return true;
4671
733k
}
4672
4673
// add bigint to bigint starting from index.
4674
// used in grade school multiplication
4675
template <uint16_t size>
4676
FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
4677
195k
                                          size_t start) noexcept {
4678
  // the effective x buffer is from `xstart..x.len()`, so exit early
4679
  // if we can't get that current range.
4680
195k
  if (x.len() < start || y.len() > x.len() - start) {
4681
193k
    FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
4682
193k
  }
4683
4684
195k
  bool carry = false;
4685
2.37M
  for (size_t index = 0; index < y.len(); index++) {
4686
2.17M
    limb xi = x[index + start];
4687
2.17M
    limb yi = y[index];
4688
2.17M
    bool c1 = false;
4689
2.17M
    bool c2 = false;
4690
2.17M
    xi = scalar_add(xi, yi, c1);
4691
2.17M
    if (carry) {
4692
900k
      xi = scalar_add(xi, 1, c2);
4693
900k
    }
4694
2.17M
    x[index + start] = xi;
4695
2.17M
    carry = c1 | c2;
4696
2.17M
  }
4697
4698
  // handle overflow
4699
195k
  if (carry) {
4700
0
    FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start));
4701
0
  }
4702
195k
  return true;
4703
195k
}
4704
4705
// add bigint to bigint.
4706
template <uint16_t size>
4707
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
4708
large_add_from(stackvec<size> &x, limb_span y) noexcept {
4709
  return large_add_from(x, y, 0);
4710
}
4711
4712
// grade-school multiplication algorithm
4713
template <uint16_t size>
4714
48.7k
FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
4715
48.7k
  limb_span xs = limb_span(x.data, x.len());
4716
48.7k
  stackvec<size> z(xs);
4717
48.7k
  limb_span zs = limb_span(z.data, z.len());
4718
4719
48.7k
  if (y.len() != 0) {
4720
48.7k
    limb y0 = y[0];
4721
48.7k
    FASTFLOAT_TRY(small_mul(x, y0));
4722
243k
    for (size_t index = 1; index < y.len(); index++) {
4723
195k
      limb yi = y[index];
4724
195k
      stackvec<size> zi;
4725
195k
      if (yi != 0) {
4726
        // re-use the same buffer throughout
4727
195k
        zi.set_len(0);
4728
195k
        FASTFLOAT_TRY(zi.try_extend(zs));
4729
195k
        FASTFLOAT_TRY(small_mul(zi, yi));
4730
195k
        limb_span zis = limb_span(zi.data, zi.len());
4731
195k
        FASTFLOAT_TRY(large_add_from(x, zis, index));
4732
195k
      }
4733
195k
    }
4734
48.7k
  }
4735
4736
48.7k
  x.normalize();
4737
48.7k
  return true;
4738
48.7k
}
4739
4740
// grade-school multiplication algorithm
4741
template <uint16_t size>
4742
48.7k
FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept {
4743
48.7k
  if (y.len() == 1) {
4744
0
    FASTFLOAT_TRY(small_mul(x, y[0]));
4745
48.7k
  } else {
4746
48.7k
    FASTFLOAT_TRY(long_mul(x, y));
4747
48.7k
  }
4748
48.7k
  return true;
4749
48.7k
}
4750
4751
template <typename = void> struct pow5_tables {
4752
  static constexpr uint32_t large_step = 135;
4753
  static constexpr uint64_t small_power_of_5[] = {
4754
      1UL,
4755
      5UL,
4756
      25UL,
4757
      125UL,
4758
      625UL,
4759
      3125UL,
4760
      15625UL,
4761
      78125UL,
4762
      390625UL,
4763
      1953125UL,
4764
      9765625UL,
4765
      48828125UL,
4766
      244140625UL,
4767
      1220703125UL,
4768
      6103515625UL,
4769
      30517578125UL,
4770
      152587890625UL,
4771
      762939453125UL,
4772
      3814697265625UL,
4773
      19073486328125UL,
4774
      95367431640625UL,
4775
      476837158203125UL,
4776
      2384185791015625UL,
4777
      11920928955078125UL,
4778
      59604644775390625UL,
4779
      298023223876953125UL,
4780
      1490116119384765625UL,
4781
      7450580596923828125UL,
4782
  };
4783
#ifdef FASTFLOAT_64BIT_LIMB
4784
  constexpr static limb large_power_of_5[] = {
4785
      1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
4786
      10482974169319127550UL, 198276706040285095UL};
4787
#else
4788
  constexpr static limb large_power_of_5[] = {
4789
      4279965485U, 329373468U,  4020270615U, 2137533757U, 4287402176U,
4790
      1057042919U, 1071430142U, 2440757623U, 381945767U,  46164893U};
4791
#endif
4792
};
4793
4794
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
4795
4796
template <typename T> constexpr uint32_t pow5_tables<T>::large_step;
4797
4798
template <typename T> constexpr uint64_t pow5_tables<T>::small_power_of_5[];
4799
4800
template <typename T> constexpr limb pow5_tables<T>::large_power_of_5[];
4801
4802
#endif
4803
4804
// big integer type. implements a small subset of big integer
4805
// arithmetic, using simple algorithms since asymptotically
4806
// faster algorithms are slower for a small number of limbs.
4807
// all operations assume the big-integer is normalized.
4808
struct bigint : pow5_tables<> {
4809
  // storage of the limbs, in little-endian order.
4810
  stackvec<bigint_limbs> vec;
4811
4812
35.1k
  FASTFLOAT_CONSTEXPR20 bigint() : vec() {}
4813
4814
  bigint(bigint const &) = delete;
4815
  bigint &operator=(bigint const &) = delete;
4816
  bigint(bigint &&) = delete;
4817
  bigint &operator=(bigint &&other) = delete;
4818
4819
22.7k
  FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) : vec() {
4820
22.7k
#ifdef FASTFLOAT_64BIT_LIMB
4821
22.7k
    vec.push_unchecked(value);
4822
#else
4823
    vec.push_unchecked(uint32_t(value));
4824
    vec.push_unchecked(uint32_t(value >> 32));
4825
#endif
4826
22.7k
    vec.normalize();
4827
22.7k
  }
4828
4829
  // get the high 64 bits from the vector, and if bits were truncated.
4830
  // this is to get the significant digits for the float.
4831
12.4k
  FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool &truncated) const noexcept {
4832
12.4k
#ifdef FASTFLOAT_64BIT_LIMB
4833
12.4k
    if (vec.len() == 0) {
4834
0
      return empty_hi64(truncated);
4835
12.4k
    } else if (vec.len() == 1) {
4836
4.90k
      return uint64_hi64(vec.rindex(0), truncated);
4837
7.55k
    } else {
4838
7.55k
      uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated);
4839
7.55k
      truncated |= vec.nonzero(2);
4840
7.55k
      return result;
4841
7.55k
    }
4842
#else
4843
    if (vec.len() == 0) {
4844
      return empty_hi64(truncated);
4845
    } else if (vec.len() == 1) {
4846
      return uint32_hi64(vec.rindex(0), truncated);
4847
    } else if (vec.len() == 2) {
4848
      return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated);
4849
    } else {
4850
      uint64_t result =
4851
          uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated);
4852
      truncated |= vec.nonzero(3);
4853
      return result;
4854
    }
4855
#endif
4856
12.4k
  }
4857
4858
  // compare two big integers, returning the large value.
4859
  // assumes both are normalized. if the return value is
4860
  // negative, other is larger, if the return value is
4861
  // positive, this is larger, otherwise they are equal.
4862
  // the limbs are stored in little-endian order, so we
4863
  // must compare the limbs in ever order.
4864
22.7k
  FASTFLOAT_CONSTEXPR20 int compare(bigint const &other) const noexcept {
4865
22.7k
    if (vec.len() > other.vec.len()) {
4866
0
      return 1;
4867
22.7k
    } else if (vec.len() < other.vec.len()) {
4868
0
      return -1;
4869
22.7k
    } else {
4870
78.3k
      for (size_t index = vec.len(); index > 0; index--) {
4871
77.2k
        limb xi = vec[index - 1];
4872
77.2k
        limb yi = other.vec[index - 1];
4873
77.2k
        if (xi > yi) {
4874
6.63k
          return 1;
4875
70.6k
        } else if (xi < yi) {
4876
14.9k
          return -1;
4877
14.9k
        }
4878
77.2k
      }
4879
1.08k
      return 0;
4880
22.7k
    }
4881
22.7k
  }
4882
4883
  // shift left each limb n bits, carrying over to the new limb
4884
  // returns true if we were able to shift all the digits.
4885
22.2k
  FASTFLOAT_CONSTEXPR20 bool shl_bits(size_t n) noexcept {
4886
    // Internally, for each item, we shift left by n, and add the previous
4887
    // right shifted limb-bits.
4888
    // For example, we transform (for u8) shifted left 2, to:
4889
    //      b10100100 b01000010
4890
    //      b10 b10010001 b00001000
4891
22.2k
    FASTFLOAT_DEBUG_ASSERT(n != 0);
4892
22.2k
    FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
4893
4894
22.2k
    size_t shl = n;
4895
22.2k
    size_t shr = limb_bits - shl;
4896
22.2k
    limb prev = 0;
4897
312k
    for (size_t index = 0; index < vec.len(); index++) {
4898
290k
      limb xi = vec[index];
4899
290k
      vec[index] = (xi << shl) | (prev >> shr);
4900
290k
      prev = xi;
4901
290k
    }
4902
4903
22.2k
    limb carry = prev >> shr;
4904
22.2k
    if (carry != 0) {
4905
6.77k
      return vec.try_push(carry);
4906
6.77k
    }
4907
15.4k
    return true;
4908
22.2k
  }
4909
4910
  // move the limbs left by `n` limbs.
4911
20.4k
  FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept {
4912
20.4k
    FASTFLOAT_DEBUG_ASSERT(n != 0);
4913
20.4k
    if (n + vec.len() > vec.capacity()) {
4914
0
      return false;
4915
20.4k
    } else if (!vec.is_empty()) {
4916
      // move limbs
4917
20.4k
      limb *dst = vec.data + n;
4918
20.4k
      limb const *src = vec.data;
4919
20.4k
      tinyobj_ff::copy_backward(src, src + vec.len(), dst + vec.len());
4920
      // fill in empty limbs
4921
20.4k
      limb *first = vec.data;
4922
20.4k
      limb *last = first + n;
4923
20.4k
      tinyobj_ff::fill(first, last, 0);
4924
20.4k
      vec.set_len(n + vec.len());
4925
20.4k
      return true;
4926
20.4k
    } else {
4927
0
      return true;
4928
0
    }
4929
20.4k
  }
4930
4931
  // move the limbs left by `n` bits.
4932
34.8k
  FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept {
4933
34.8k
    size_t rem = n % limb_bits;
4934
34.8k
    size_t div = n / limb_bits;
4935
34.8k
    if (rem != 0) {
4936
22.2k
      FASTFLOAT_TRY(shl_bits(rem));
4937
22.2k
    }
4938
34.8k
    if (div != 0) {
4939
20.4k
      FASTFLOAT_TRY(shl_limbs(div));
4940
20.4k
    }
4941
34.8k
    return true;
4942
34.8k
  }
4943
4944
  // get the number of leading zeros in the bigint.
4945
12.4k
  FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept {
4946
12.4k
    if (vec.is_empty()) {
4947
0
      return 0;
4948
12.4k
    } else {
4949
12.4k
#ifdef FASTFLOAT_64BIT_LIMB
4950
12.4k
      return leading_zeroes(vec.rindex(0));
4951
#else
4952
      // no use defining a specialized leading_zeroes for a 32-bit type.
4953
      uint64_t r0 = vec.rindex(0);
4954
      return leading_zeroes(r0 << 32);
4955
#endif
4956
12.4k
    }
4957
12.4k
  }
4958
4959
  // get the number of bits in the bigint.
4960
12.4k
  FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept {
4961
12.4k
    int lz = ctlz();
4962
12.4k
    return int(limb_bits * vec.len()) - lz;
4963
12.4k
  }
4964
4965
420k
  FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { return small_mul(vec, y); }
4966
4967
420k
  FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); }
4968
4969
  // multiply as if by 2 raised to a power.
4970
34.8k
  FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept { return shl(exp); }
4971
4972
  // multiply as if by 5 raised to a power.
4973
35.1k
  FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept {
4974
    // multiply by a power of 5
4975
35.1k
    size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
4976
35.1k
    limb_span large = limb_span(large_power_of_5, large_length);
4977
83.9k
    while (exp >= large_step) {
4978
48.7k
      FASTFLOAT_TRY(large_mul(vec, large));
4979
48.7k
      exp -= large_step;
4980
48.7k
    }
4981
35.1k
#ifdef FASTFLOAT_64BIT_LIMB
4982
35.1k
    uint32_t small_step = 27;
4983
35.1k
    limb max_native = 7450580596923828125UL;
4984
#else
4985
    uint32_t small_step = 13;
4986
    limb max_native = 1220703125U;
4987
#endif
4988
79.8k
    while (exp >= small_step) {
4989
44.6k
      FASTFLOAT_TRY(small_mul(vec, max_native));
4990
44.6k
      exp -= small_step;
4991
44.6k
    }
4992
35.1k
    if (exp != 0) {
4993
      // Work around clang bug https://godbolt.org/z/zedh7rrhc
4994
      // This is similar to https://github.com/llvm/llvm-project/issues/47746,
4995
      // except the workaround described there don't work here
4996
24.3k
      FASTFLOAT_TRY(small_mul(
4997
24.3k
          vec, limb(((void)small_power_of_5[0], small_power_of_5[exp]))));
4998
24.3k
    }
4999
5000
35.1k
    return true;
5001
35.1k
  }
5002
5003
  // multiply as if by 10 raised to a power.
5004
12.4k
  FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept {
5005
12.4k
    FASTFLOAT_TRY(pow5(exp));
5006
12.4k
    return pow2(exp);
5007
12.4k
  }
5008
};
5009
5010
} // namespace fast_float
5011
5012
#endif
5013
5014
#ifndef FASTFLOAT_DIGIT_COMPARISON_H
5015
#define FASTFLOAT_DIGIT_COMPARISON_H
5016
5017
#include <cstring>
5018
5019
5020
namespace fast_float {
5021
5022
// 1e0 to 1e19
5023
constexpr static uint64_t powers_of_ten_uint64[] = {1UL,
5024
                                                    10UL,
5025
                                                    100UL,
5026
                                                    1000UL,
5027
                                                    10000UL,
5028
                                                    100000UL,
5029
                                                    1000000UL,
5030
                                                    10000000UL,
5031
                                                    100000000UL,
5032
                                                    1000000000UL,
5033
                                                    10000000000UL,
5034
                                                    100000000000UL,
5035
                                                    1000000000000UL,
5036
                                                    10000000000000UL,
5037
                                                    100000000000000UL,
5038
                                                    1000000000000000UL,
5039
                                                    10000000000000000UL,
5040
                                                    100000000000000000UL,
5041
                                                    1000000000000000000UL,
5042
                                                    10000000000000000000UL};
5043
5044
// calculate the exponent, in scientific notation, of the number.
5045
// this algorithm is not even close to optimized, but it has no practical
5046
// effect on performance: in order to have a faster algorithm, we'd need
5047
// to slow down performance for faster algorithms, and this is still fast.
5048
template <typename UC>
5049
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t
5050
35.1k
scientific_exponent(parsed_number_string_t<UC> &num) noexcept {
5051
35.1k
  uint64_t mantissa = num.mantissa;
5052
35.1k
  int32_t exponent = int32_t(num.exponent);
5053
175k
  while (mantissa >= 10000) {
5054
140k
    mantissa /= 10000;
5055
140k
    exponent += 4;
5056
140k
  }
5057
70.3k
  while (mantissa >= 100) {
5058
35.1k
    mantissa /= 100;
5059
35.1k
    exponent += 2;
5060
35.1k
  }
5061
35.1k
  while (mantissa >= 10) {
5062
0
    mantissa /= 10;
5063
0
    exponent += 1;
5064
0
  }
5065
35.1k
  return exponent;
5066
35.1k
}
5067
5068
// this converts a native floating-point number to an extended-precision float.
5069
template <typename T>
5070
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
5071
22.7k
to_extended(T value) noexcept {
5072
22.7k
  using equiv_uint = equiv_uint_t<T>;
5073
22.7k
  constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
5074
22.7k
  constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
5075
22.7k
  constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
5076
5077
22.7k
  adjusted_mantissa am;
5078
22.7k
  int32_t bias = binary_format<T>::mantissa_explicit_bits() -
5079
22.7k
                 binary_format<T>::minimum_exponent();
5080
22.7k
  equiv_uint bits;
5081
#if FASTFLOAT_HAS_BIT_CAST
5082
  bits = std::bit_cast<equiv_uint>(value);
5083
#else
5084
22.7k
  ::memcpy(&bits, &value, sizeof(T));
5085
22.7k
#endif
5086
22.7k
  if ((bits & exponent_mask) == 0) {
5087
    // denormal
5088
2.09k
    am.power2 = 1 - bias;
5089
2.09k
    am.mantissa = bits & mantissa_mask;
5090
20.6k
  } else {
5091
    // normal
5092
20.6k
    am.power2 = int32_t((bits & exponent_mask) >>
5093
20.6k
                        binary_format<T>::mantissa_explicit_bits());
5094
20.6k
    am.power2 -= bias;
5095
20.6k
    am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
5096
20.6k
  }
5097
5098
22.7k
  return am;
5099
22.7k
}
fast_float::adjusted_mantissa fast_float::to_extended<double>(double)
Line
Count
Source
5071
22.7k
to_extended(T value) noexcept {
5072
22.7k
  using equiv_uint = equiv_uint_t<T>;
5073
22.7k
  constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
5074
22.7k
  constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
5075
22.7k
  constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
5076
5077
22.7k
  adjusted_mantissa am;
5078
22.7k
  int32_t bias = binary_format<T>::mantissa_explicit_bits() -
5079
22.7k
                 binary_format<T>::minimum_exponent();
5080
22.7k
  equiv_uint bits;
5081
#if FASTFLOAT_HAS_BIT_CAST
5082
  bits = std::bit_cast<equiv_uint>(value);
5083
#else
5084
22.7k
  ::memcpy(&bits, &value, sizeof(T));
5085
22.7k
#endif
5086
22.7k
  if ((bits & exponent_mask) == 0) {
5087
    // denormal
5088
2.09k
    am.power2 = 1 - bias;
5089
2.09k
    am.mantissa = bits & mantissa_mask;
5090
20.6k
  } else {
5091
    // normal
5092
20.6k
    am.power2 = int32_t((bits & exponent_mask) >>
5093
20.6k
                        binary_format<T>::mantissa_explicit_bits());
5094
20.6k
    am.power2 -= bias;
5095
20.6k
    am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
5096
20.6k
  }
5097
5098
22.7k
  return am;
5099
22.7k
}
Unexecuted instantiation: fast_float::adjusted_mantissa fast_float::to_extended<float>(float)
5100
5101
// get the extended precision value of the halfway point between b and b+u.
5102
// we are given a native float that represents b, so we need to adjust it
5103
// halfway between b and b+u.
5104
template <typename T>
5105
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
5106
22.7k
to_extended_halfway(T value) noexcept {
5107
22.7k
  adjusted_mantissa am = to_extended(value);
5108
22.7k
  am.mantissa <<= 1;
5109
22.7k
  am.mantissa += 1;
5110
22.7k
  am.power2 -= 1;
5111
22.7k
  return am;
5112
22.7k
}
fast_float::adjusted_mantissa fast_float::to_extended_halfway<double>(double)
Line
Count
Source
5106
22.7k
to_extended_halfway(T value) noexcept {
5107
22.7k
  adjusted_mantissa am = to_extended(value);
5108
22.7k
  am.mantissa <<= 1;
5109
22.7k
  am.mantissa += 1;
5110
22.7k
  am.power2 -= 1;
5111
22.7k
  return am;
5112
22.7k
}
Unexecuted instantiation: fast_float::adjusted_mantissa fast_float::to_extended_halfway<float>(float)
5113
5114
// round an extended-precision float to the nearest machine float.
5115
template <typename T, typename callback>
5116
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
5117
57.8k
                                                         callback cb) noexcept {
5118
57.8k
  int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
5119
57.8k
  if (-am.power2 >= mantissa_shift) {
5120
    // have a denormal float
5121
4.19k
    int32_t shift = -am.power2 + 1;
5122
4.19k
    cb(am, tinyobj_ff::min_val<int32_t>(shift, 64));
5123
    // check for round-up: if rounding-nearest carried us to the hidden bit.
5124
4.19k
    am.power2 = (am.mantissa <
5125
4.19k
                 (uint64_t(1) << binary_format<T>::mantissa_explicit_bits()))
5126
4.19k
                    ? 0
5127
4.19k
                    : 1;
5128
4.19k
    return;
5129
4.19k
  }
5130
5131
  // have a normal float, use the default shift.
5132
53.6k
  cb(am, mantissa_shift);
5133
5134
  // check for carry
5135
53.6k
  if (am.mantissa >=
5136
53.6k
      (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
5137
542
    am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
5138
542
    am.power2++;
5139
542
  }
5140
5141
  // check for infinite: we could have carried to an infinite power
5142
53.6k
  am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
5143
53.6k
  if (am.power2 >= binary_format<T>::infinite_power()) {
5144
0
    am.power2 = binary_format<T>::infinite_power();
5145
0
    am.mantissa = 0;
5146
0
  }
5147
53.6k
}
void fast_float::round<double, fast_float::positive_digit_comp<double>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}>(fast_float::adjusted_mantissa&, fast_float::positive_digit_comp<double>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1})
Line
Count
Source
5117
12.4k
                                                         callback cb) noexcept {
5118
12.4k
  int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
5119
12.4k
  if (-am.power2 >= mantissa_shift) {
5120
    // have a denormal float
5121
0
    int32_t shift = -am.power2 + 1;
5122
0
    cb(am, tinyobj_ff::min_val<int32_t>(shift, 64));
5123
    // check for round-up: if rounding-nearest carried us to the hidden bit.
5124
0
    am.power2 = (am.mantissa <
5125
0
                 (uint64_t(1) << binary_format<T>::mantissa_explicit_bits()))
5126
0
                    ? 0
5127
0
                    : 1;
5128
0
    return;
5129
0
  }
5130
5131
  // have a normal float, use the default shift.
5132
12.4k
  cb(am, mantissa_shift);
5133
5134
  // check for carry
5135
12.4k
  if (am.mantissa >=
5136
12.4k
      (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
5137
430
    am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
5138
430
    am.power2++;
5139
430
  }
5140
5141
  // check for infinite: we could have carried to an infinite power
5142
12.4k
  am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
5143
12.4k
  if (am.power2 >= binary_format<T>::infinite_power()) {
5144
0
    am.power2 = binary_format<T>::infinite_power();
5145
0
    am.mantissa = 0;
5146
0
  }
5147
12.4k
}
void fast_float::round<double, fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}>(fast_float::adjusted_mantissa&, fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#1})
Line
Count
Source
5117
22.7k
                                                         callback cb) noexcept {
5118
22.7k
  int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
5119
22.7k
  if (-am.power2 >= mantissa_shift) {
5120
    // have a denormal float
5121
2.09k
    int32_t shift = -am.power2 + 1;
5122
2.09k
    cb(am, tinyobj_ff::min_val<int32_t>(shift, 64));
5123
    // check for round-up: if rounding-nearest carried us to the hidden bit.
5124
2.09k
    am.power2 = (am.mantissa <
5125
2.09k
                 (uint64_t(1) << binary_format<T>::mantissa_explicit_bits()))
5126
2.09k
                    ? 0
5127
2.09k
                    : 1;
5128
2.09k
    return;
5129
2.09k
  }
5130
5131
  // have a normal float, use the default shift.
5132
20.6k
  cb(am, mantissa_shift);
5133
5134
  // check for carry
5135
20.6k
  if (am.mantissa >=
5136
20.6k
      (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
5137
0
    am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
5138
0
    am.power2++;
5139
0
  }
5140
5141
  // check for infinite: we could have carried to an infinite power
5142
20.6k
  am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
5143
20.6k
  if (am.power2 >= binary_format<T>::infinite_power()) {
5144
0
    am.power2 = binary_format<T>::infinite_power();
5145
0
    am.mantissa = 0;
5146
0
  }
5147
20.6k
}
void fast_float::round<double, fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2}>(fast_float::adjusted_mantissa&, fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2})
Line
Count
Source
5117
22.7k
                                                         callback cb) noexcept {
5118
22.7k
  int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
5119
22.7k
  if (-am.power2 >= mantissa_shift) {
5120
    // have a denormal float
5121
2.09k
    int32_t shift = -am.power2 + 1;
5122
2.09k
    cb(am, tinyobj_ff::min_val<int32_t>(shift, 64));
5123
    // check for round-up: if rounding-nearest carried us to the hidden bit.
5124
2.09k
    am.power2 = (am.mantissa <
5125
2.09k
                 (uint64_t(1) << binary_format<T>::mantissa_explicit_bits()))
5126
2.09k
                    ? 0
5127
2.09k
                    : 1;
5128
2.09k
    return;
5129
2.09k
  }
5130
5131
  // have a normal float, use the default shift.
5132
20.6k
  cb(am, mantissa_shift);
5133
5134
  // check for carry
5135
20.6k
  if (am.mantissa >=
5136
20.6k
      (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
5137
112
    am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
5138
112
    am.power2++;
5139
112
  }
5140
5141
  // check for infinite: we could have carried to an infinite power
5142
20.6k
  am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
5143
20.6k
  if (am.power2 >= binary_format<T>::infinite_power()) {
5144
0
    am.power2 = binary_format<T>::infinite_power();
5145
0
    am.mantissa = 0;
5146
0
  }
5147
20.6k
}
Unexecuted instantiation: void fast_float::round<float, fast_float::positive_digit_comp<float>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}>(fast_float::adjusted_mantissa&, fast_float::positive_digit_comp<float>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1})
Unexecuted instantiation: void fast_float::round<float, fast_float::negative_digit_comp<float>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}>(fast_float::adjusted_mantissa&, fast_float::negative_digit_comp<float>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#1})
Unexecuted instantiation: void fast_float::round<float, fast_float::negative_digit_comp<float>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2}>(fast_float::adjusted_mantissa&, fast_float::negative_digit_comp<float>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2})
5148
5149
template <typename callback>
5150
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
5151
round_nearest_tie_even(adjusted_mantissa &am, int32_t shift,
5152
35.1k
                       callback cb) noexcept {
5153
35.1k
  uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1;
5154
35.1k
  uint64_t const halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1);
5155
35.1k
  uint64_t truncated_bits = am.mantissa & mask;
5156
35.1k
  bool is_above = truncated_bits > halfway;
5157
35.1k
  bool is_halfway = truncated_bits == halfway;
5158
5159
  // shift digits into position
5160
35.1k
  if (shift == 64) {
5161
0
    am.mantissa = 0;
5162
35.1k
  } else {
5163
35.1k
    am.mantissa >>= shift;
5164
35.1k
  }
5165
35.1k
  am.power2 += shift;
5166
5167
35.1k
  bool is_odd = (am.mantissa & 1) == 1;
5168
35.1k
  am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
5169
35.1k
}
void fast_float::round_nearest_tie_even<fast_float::positive_digit_comp<double>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1}>(fast_float::adjusted_mantissa&, int, fast_float::positive_digit_comp<double>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1})
Line
Count
Source
5152
12.4k
                       callback cb) noexcept {
5153
12.4k
  uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1;
5154
12.4k
  uint64_t const halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1);
5155
12.4k
  uint64_t truncated_bits = am.mantissa & mask;
5156
12.4k
  bool is_above = truncated_bits > halfway;
5157
12.4k
  bool is_halfway = truncated_bits == halfway;
5158
5159
  // shift digits into position
5160
12.4k
  if (shift == 64) {
5161
0
    am.mantissa = 0;
5162
12.4k
  } else {
5163
12.4k
    am.mantissa >>= shift;
5164
12.4k
  }
5165
12.4k
  am.power2 += shift;
5166
5167
12.4k
  bool is_odd = (am.mantissa & 1) == 1;
5168
12.4k
  am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
5169
12.4k
}
void fast_float::round_nearest_tie_even<fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1}>(fast_float::adjusted_mantissa&, int, fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1})
Line
Count
Source
5152
22.7k
                       callback cb) noexcept {
5153
22.7k
  uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1;
5154
22.7k
  uint64_t const halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1);
5155
22.7k
  uint64_t truncated_bits = am.mantissa & mask;
5156
22.7k
  bool is_above = truncated_bits > halfway;
5157
22.7k
  bool is_halfway = truncated_bits == halfway;
5158
5159
  // shift digits into position
5160
22.7k
  if (shift == 64) {
5161
0
    am.mantissa = 0;
5162
22.7k
  } else {
5163
22.7k
    am.mantissa >>= shift;
5164
22.7k
  }
5165
22.7k
  am.power2 += shift;
5166
5167
22.7k
  bool is_odd = (am.mantissa & 1) == 1;
5168
22.7k
  am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
5169
22.7k
}
Unexecuted instantiation: void fast_float::round_nearest_tie_even<fast_float::positive_digit_comp<float>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1}>(fast_float::adjusted_mantissa&, int, fast_float::positive_digit_comp<float>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1})
Unexecuted instantiation: void fast_float::round_nearest_tie_even<fast_float::negative_digit_comp<float>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1}>(fast_float::adjusted_mantissa&, int, fast_float::negative_digit_comp<float>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1})
5170
5171
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
5172
22.7k
round_down(adjusted_mantissa &am, int32_t shift) noexcept {
5173
22.7k
  if (shift == 64) {
5174
0
    am.mantissa = 0;
5175
22.7k
  } else {
5176
22.7k
    am.mantissa >>= shift;
5177
22.7k
  }
5178
22.7k
  am.power2 += shift;
5179
22.7k
}
5180
5181
template <typename UC>
5182
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
5183
41.1k
skip_zeros(UC const *&first, UC const *last) noexcept {
5184
41.1k
  uint64_t val;
5185
70.8k
  while (!cpp20_and_in_constexpr() &&
5186
70.8k
         tinyobj_ff::distance(first, last) >= int_cmp_len<UC>()) {
5187
57.4k
    ::memcpy(&val, first, sizeof(uint64_t));
5188
57.4k
    if (val != int_cmp_zeros<UC>()) {
5189
27.8k
      break;
5190
27.8k
    }
5191
29.6k
    first += int_cmp_len<UC>();
5192
29.6k
  }
5193
66.2k
  while (first != last) {
5194
60.2k
    if (*first != UC('0')) {
5195
35.1k
      break;
5196
35.1k
    }
5197
25.0k
    first++;
5198
25.0k
  }
5199
41.1k
}
5200
5201
// determine if any non-zero digits were truncated.
5202
// all characters must be valid digits.
5203
template <typename UC>
5204
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
5205
8.93k
is_truncated(UC const *first, UC const *last) noexcept {
5206
  // do 8-bit optimizations, can just compare to 8 literal 0s.
5207
8.93k
  uint64_t val;
5208
172k
  while (!cpp20_and_in_constexpr() &&
5209
172k
         tinyobj_ff::distance(first, last) >= int_cmp_len<UC>()) {
5210
167k
    ::memcpy(&val, first, sizeof(uint64_t));
5211
167k
    if (val != int_cmp_zeros<UC>()) {
5212
3.47k
      return true;
5213
3.47k
    }
5214
163k
    first += int_cmp_len<UC>();
5215
163k
  }
5216
24.2k
  while (first != last) {
5217
20.4k
    if (*first != UC('0')) {
5218
1.59k
      return true;
5219
1.59k
    }
5220
18.8k
    ++first;
5221
18.8k
  }
5222
3.86k
  return false;
5223
5.46k
}
5224
5225
template <typename UC>
5226
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
5227
1.99k
is_truncated(span<UC const> s) noexcept {
5228
1.99k
  return is_truncated(s.ptr, s.ptr + s.len());
5229
1.99k
}
5230
5231
template <typename UC>
5232
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
5233
parse_eight_digits(UC const *&p, limb &value, size_t &counter,
5234
763k
                   size_t &count) noexcept {
5235
763k
  value = value * 100000000 + parse_eight_digits_unrolled(p);
5236
763k
  p += 8;
5237
763k
  counter += 8;
5238
763k
  count += 8;
5239
763k
}
5240
5241
template <typename UC>
5242
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
5243
parse_one_digit(UC const *&p, limb &value, size_t &counter,
5244
1.23M
                size_t &count) noexcept {
5245
1.23M
  value = value * 10 + limb(*p - UC('0'));
5246
1.23M
  p++;
5247
1.23M
  counter++;
5248
1.23M
  count++;
5249
1.23M
}
5250
5251
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
5252
420k
add_native(bigint &big, limb power, limb value) noexcept {
5253
420k
  big.mul(power);
5254
420k
  big.add(value);
5255
420k
}
5256
5257
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
5258
4.13k
round_up_bigint(bigint &big, size_t &count) noexcept {
5259
  // need to round-up the digits, but need to avoid rounding
5260
  // ....9999 to ...10000, which could cause a false halfway point.
5261
4.13k
  add_native(big, 10, 1);
5262
4.13k
  count++;
5263
4.13k
}
5264
5265
// parse the significant digits into a big integer
5266
template <typename UC>
5267
inline FASTFLOAT_CONSTEXPR20 void
5268
parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
5269
35.1k
               size_t max_digits, size_t &digits) noexcept {
5270
  // try to minimize the number of big integer and scalar multiplication.
5271
  // therefore, try to parse 8 digits at a time, and multiply by the largest
5272
  // scalar value (9 or 19 digits) for each step.
5273
35.1k
  size_t counter = 0;
5274
35.1k
  digits = 0;
5275
35.1k
  limb value = 0;
5276
35.1k
#ifdef FASTFLOAT_64BIT_LIMB
5277
35.1k
  size_t step = 19;
5278
#else
5279
  size_t step = 9;
5280
#endif
5281
5282
  // process all integer digits.
5283
35.1k
  UC const *p = num.integer.ptr;
5284
35.1k
  UC const *pend = p + num.integer.len();
5285
35.1k
  skip_zeros(p, pend);
5286
  // process all digits, in increments of step per loop
5287
187k
  while (p != pend) {
5288
423k
    while ((tinyobj_ff::distance(p, pend) >= 8) && (step - counter >= 8) &&
5289
270k
           (max_digits - digits >= 8)) {
5290
268k
      parse_eight_digits(p, value, counter, digits);
5291
268k
    }
5292
613k
    while (counter < step && p != pend && digits < max_digits) {
5293
458k
      parse_one_digit(p, value, counter, digits);
5294
458k
    }
5295
155k
    if (digits == max_digits) {
5296
      // add the temporary value, then check if we've truncated any digits
5297
2.63k
      add_native(result, limb(powers_of_ten_uint64[counter]), value);
5298
2.63k
      bool truncated = is_truncated(p, pend);
5299
2.63k
      if (num.fraction.ptr != nullptr) {
5300
1.99k
        truncated |= is_truncated(num.fraction);
5301
1.99k
      }
5302
2.63k
      if (truncated) {
5303
1.63k
        round_up_bigint(result, digits);
5304
1.63k
      }
5305
2.63k
      return;
5306
152k
    } else {
5307
152k
      add_native(result, limb(powers_of_ten_uint64[counter]), value);
5308
152k
      counter = 0;
5309
152k
      value = 0;
5310
152k
    }
5311
155k
  }
5312
5313
  // add our fraction digits, if they're available.
5314
32.5k
  if (num.fraction.ptr != nullptr) {
5315
24.3k
    p = num.fraction.ptr;
5316
24.3k
    pend = p + num.fraction.len();
5317
24.3k
    if (digits == 0) {
5318
5.98k
      skip_zeros(p, pend);
5319
5.98k
    }
5320
    // process all digits, in increments of step per loop
5321
281k
    while (p != pend) {
5322
756k
      while ((tinyobj_ff::distance(p, pend) >= 8) && (step - counter >= 8) &&
5323
498k
             (max_digits - digits >= 8)) {
5324
495k
        parse_eight_digits(p, value, counter, digits);
5325
495k
      }
5326
1.03M
      while (counter < step && p != pend && digits < max_digits) {
5327
773k
        parse_one_digit(p, value, counter, digits);
5328
773k
      }
5329
261k
      if (digits == max_digits) {
5330
        // add the temporary value, then check if we've truncated any digits
5331
4.30k
        add_native(result, limb(powers_of_ten_uint64[counter]), value);
5332
4.30k
        bool truncated = is_truncated(p, pend);
5333
4.30k
        if (truncated) {
5334
2.50k
          round_up_bigint(result, digits);
5335
2.50k
        }
5336
4.30k
        return;
5337
256k
      } else {
5338
256k
        add_native(result, limb(powers_of_ten_uint64[counter]), value);
5339
256k
        counter = 0;
5340
256k
        value = 0;
5341
256k
      }
5342
261k
    }
5343
24.3k
  }
5344
5345
28.2k
  if (counter != 0) {
5346
0
    add_native(result, limb(powers_of_ten_uint64[counter]), value);
5347
0
  }
5348
28.2k
}
5349
5350
template <typename T>
5351
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
5352
12.4k
positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
5353
12.4k
  FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
5354
12.4k
  adjusted_mantissa answer;
5355
12.4k
  bool truncated;
5356
12.4k
  answer.mantissa = bigmant.hi64(truncated);
5357
12.4k
  int bias = binary_format<T>::mantissa_explicit_bits() -
5358
12.4k
             binary_format<T>::minimum_exponent();
5359
12.4k
  answer.power2 = bigmant.bit_length() - 64 + bias;
5360
5361
12.4k
  round<T>(answer, [truncated](adjusted_mantissa &a, int32_t shift) {
5362
12.4k
    round_nearest_tie_even(
5363
12.4k
        a, shift,
5364
12.4k
        [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
5365
12.4k
          return is_above || (is_halfway && truncated) ||
5366
4.08k
                 (is_odd && is_halfway);
5367
12.4k
        });
fast_float::positive_digit_comp<double>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1}::operator()(bool, bool, bool) const
Line
Count
Source
5364
12.4k
        [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
5365
12.4k
          return is_above || (is_halfway && truncated) ||
5366
4.08k
                 (is_odd && is_halfway);
5367
12.4k
        });
Unexecuted instantiation: fast_float::positive_digit_comp<float>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1}::operator()(bool, bool, bool) const
5368
12.4k
  });
fast_float::positive_digit_comp<double>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}::operator()(fast_float::adjusted_mantissa&, int) const
Line
Count
Source
5361
12.4k
  round<T>(answer, [truncated](adjusted_mantissa &a, int32_t shift) {
5362
12.4k
    round_nearest_tie_even(
5363
12.4k
        a, shift,
5364
12.4k
        [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
5365
12.4k
          return is_above || (is_halfway && truncated) ||
5366
12.4k
                 (is_odd && is_halfway);
5367
12.4k
        });
5368
12.4k
  });
Unexecuted instantiation: fast_float::positive_digit_comp<float>(fast_float::bigint&, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}::operator()(fast_float::adjusted_mantissa&, int) const
5369
5370
12.4k
  return answer;
5371
12.4k
}
fast_float::adjusted_mantissa fast_float::positive_digit_comp<double>(fast_float::bigint&, int)
Line
Count
Source
5352
12.4k
positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
5353
12.4k
  FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
5354
12.4k
  adjusted_mantissa answer;
5355
12.4k
  bool truncated;
5356
12.4k
  answer.mantissa = bigmant.hi64(truncated);
5357
12.4k
  int bias = binary_format<T>::mantissa_explicit_bits() -
5358
12.4k
             binary_format<T>::minimum_exponent();
5359
12.4k
  answer.power2 = bigmant.bit_length() - 64 + bias;
5360
5361
12.4k
  round<T>(answer, [truncated](adjusted_mantissa &a, int32_t shift) {
5362
12.4k
    round_nearest_tie_even(
5363
12.4k
        a, shift,
5364
12.4k
        [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
5365
12.4k
          return is_above || (is_halfway && truncated) ||
5366
12.4k
                 (is_odd && is_halfway);
5367
12.4k
        });
5368
12.4k
  });
5369
5370
12.4k
  return answer;
5371
12.4k
}
Unexecuted instantiation: fast_float::adjusted_mantissa fast_float::positive_digit_comp<float>(fast_float::bigint&, int)
5372
5373
// the scaling here is quite simple: we have, for the real digits `m * 10^e`,
5374
// and for the theoretical digits `n * 2^f`. Since `e` is always negative,
5375
// to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`.
5376
// we then need to scale by `2^(f- e)`, and then the two significant digits
5377
// are of the same magnitude.
5378
template <typename T>
5379
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
5380
22.7k
    bigint &bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
5381
22.7k
  bigint &real_digits = bigmant;
5382
22.7k
  int32_t real_exp = exponent;
5383
5384
  // get the value of `b`, rounded down, and get a bigint representation of b+h
5385
22.7k
  adjusted_mantissa am_b = am;
5386
  // gcc7 buf: use a lambda to remove the noexcept qualifier bug with
5387
  // -Wnoexcept-type.
5388
22.7k
  round<T>(am_b,
5389
22.7k
           [](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); });
fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}::operator()(fast_float::adjusted_mantissa&, int) const
Line
Count
Source
5389
22.7k
           [](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); });
Unexecuted instantiation: fast_float::negative_digit_comp<float>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#1}::operator()(fast_float::adjusted_mantissa&, int) const
5390
22.7k
  T b;
5391
22.7k
  to_float(false, am_b, b);
5392
22.7k
  adjusted_mantissa theor = to_extended_halfway(b);
5393
22.7k
  bigint theor_digits(theor.mantissa);
5394
22.7k
  int32_t theor_exp = theor.power2;
5395
5396
  // scale real digits and theor digits to be same power.
5397
22.7k
  int32_t pow2_exp = theor_exp - real_exp;
5398
22.7k
  uint32_t pow5_exp = uint32_t(-real_exp);
5399
22.7k
  if (pow5_exp != 0) {
5400
22.7k
    FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
5401
22.7k
  }
5402
22.7k
  if (pow2_exp > 0) {
5403
13.2k
    FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp)));
5404
13.2k
  } else if (pow2_exp < 0) {
5405
9.17k
    FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp)));
5406
9.17k
  }
5407
5408
  // compare digits, and use it to director rounding
5409
22.7k
  int ord = real_digits.compare(theor_digits);
5410
22.7k
  adjusted_mantissa answer = am;
5411
22.7k
  round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
5412
22.7k
    round_nearest_tie_even(
5413
22.7k
        a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
5414
22.7k
          (void)_;  // not needed, since we've done our comparison
5415
22.7k
          (void)__; // not needed, since we've done our comparison
5416
22.7k
          if (ord > 0) {
5417
6.63k
            return true;
5418
16.0k
          } else if (ord < 0) {
5419
14.9k
            return false;
5420
14.9k
          } else {
5421
1.08k
            return is_odd;
5422
1.08k
          }
5423
22.7k
        });
fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1}::operator()(bool, bool, bool) const
Line
Count
Source
5413
22.7k
        a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
5414
22.7k
          (void)_;  // not needed, since we've done our comparison
5415
22.7k
          (void)__; // not needed, since we've done our comparison
5416
22.7k
          if (ord > 0) {
5417
6.63k
            return true;
5418
16.0k
          } else if (ord < 0) {
5419
14.9k
            return false;
5420
14.9k
          } else {
5421
1.08k
            return is_odd;
5422
1.08k
          }
5423
22.7k
        });
Unexecuted instantiation: fast_float::negative_digit_comp<float>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2}::operator()(fast_float::adjusted_mantissa&, int) const::{lambda(bool, bool, bool)#1}::operator()(bool, bool, bool) const
5424
22.7k
  });
fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2}::operator()(fast_float::adjusted_mantissa&, int) const
Line
Count
Source
5411
22.7k
  round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
5412
22.7k
    round_nearest_tie_even(
5413
22.7k
        a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
5414
22.7k
          (void)_;  // not needed, since we've done our comparison
5415
22.7k
          (void)__; // not needed, since we've done our comparison
5416
22.7k
          if (ord > 0) {
5417
22.7k
            return true;
5418
22.7k
          } else if (ord < 0) {
5419
22.7k
            return false;
5420
22.7k
          } else {
5421
22.7k
            return is_odd;
5422
22.7k
          }
5423
22.7k
        });
5424
22.7k
  });
Unexecuted instantiation: fast_float::negative_digit_comp<float>(fast_float::bigint&, fast_float::adjusted_mantissa, int)::{lambda(fast_float::adjusted_mantissa&, int)#2}::operator()(fast_float::adjusted_mantissa&, int) const
5425
5426
22.7k
  return answer;
5427
22.7k
}
fast_float::adjusted_mantissa fast_float::negative_digit_comp<double>(fast_float::bigint&, fast_float::adjusted_mantissa, int)
Line
Count
Source
5380
22.7k
    bigint &bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
5381
22.7k
  bigint &real_digits = bigmant;
5382
22.7k
  int32_t real_exp = exponent;
5383
5384
  // get the value of `b`, rounded down, and get a bigint representation of b+h
5385
22.7k
  adjusted_mantissa am_b = am;
5386
  // gcc7 buf: use a lambda to remove the noexcept qualifier bug with
5387
  // -Wnoexcept-type.
5388
22.7k
  round<T>(am_b,
5389
22.7k
           [](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); });
5390
22.7k
  T b;
5391
22.7k
  to_float(false, am_b, b);
5392
22.7k
  adjusted_mantissa theor = to_extended_halfway(b);
5393
22.7k
  bigint theor_digits(theor.mantissa);
5394
22.7k
  int32_t theor_exp = theor.power2;
5395
5396
  // scale real digits and theor digits to be same power.
5397
22.7k
  int32_t pow2_exp = theor_exp - real_exp;
5398
22.7k
  uint32_t pow5_exp = uint32_t(-real_exp);
5399
22.7k
  if (pow5_exp != 0) {
5400
22.7k
    FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
5401
22.7k
  }
5402
22.7k
  if (pow2_exp > 0) {
5403
13.2k
    FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp)));
5404
13.2k
  } else if (pow2_exp < 0) {
5405
9.17k
    FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp)));
5406
9.17k
  }
5407
5408
  // compare digits, and use it to director rounding
5409
22.7k
  int ord = real_digits.compare(theor_digits);
5410
22.7k
  adjusted_mantissa answer = am;
5411
22.7k
  round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
5412
22.7k
    round_nearest_tie_even(
5413
22.7k
        a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
5414
22.7k
          (void)_;  // not needed, since we've done our comparison
5415
22.7k
          (void)__; // not needed, since we've done our comparison
5416
22.7k
          if (ord > 0) {
5417
22.7k
            return true;
5418
22.7k
          } else if (ord < 0) {
5419
22.7k
            return false;
5420
22.7k
          } else {
5421
22.7k
            return is_odd;
5422
22.7k
          }
5423
22.7k
        });
5424
22.7k
  });
5425
5426
22.7k
  return answer;
5427
22.7k
}
Unexecuted instantiation: fast_float::adjusted_mantissa fast_float::negative_digit_comp<float>(fast_float::bigint&, fast_float::adjusted_mantissa, int)
5428
5429
// parse the significant digits as a big integer to unambiguously round the
5430
// the significant digits. here, we are trying to determine how to round
5431
// an extended float representation close to `b+h`, halfway between `b`
5432
// (the float rounded-down) and `b+u`, the next positive float. this
5433
// algorithm is always correct, and uses one of two approaches. when
5434
// the exponent is positive relative to the significant digits (such as
5435
// 1234), we create a big-integer representation, get the high 64-bits,
5436
// determine if any lower bits are truncated, and use that to direct
5437
// rounding. in case of a negative exponent relative to the significant
5438
// digits (such as 1.2345), we create a theoretical representation of
5439
// `b` as a big-integer type, scaled to the same binary exponent as
5440
// the actual digits. we then compare the big integer representations
5441
// of both, and use that to direct rounding.
5442
template <typename T, typename UC>
5443
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
5444
35.1k
digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept {
5445
  // remove the invalid exponent bias
5446
35.1k
  am.power2 -= invalid_am_bias;
5447
5448
35.1k
  int32_t sci_exp = scientific_exponent(num);
5449
35.1k
  size_t max_digits = binary_format<T>::max_digits();
5450
35.1k
  size_t digits = 0;
5451
35.1k
  bigint bigmant;
5452
35.1k
  parse_mantissa(bigmant, num, max_digits, digits);
5453
  // can't underflow, since digits is at most max_digits.
5454
35.1k
  int32_t exponent = sci_exp + 1 - int32_t(digits);
5455
35.1k
  if (exponent >= 0) {
5456
12.4k
    return positive_digit_comp<T>(bigmant, exponent);
5457
22.7k
  } else {
5458
22.7k
    return negative_digit_comp<T>(bigmant, am, exponent);
5459
22.7k
  }
5460
35.1k
}
fast_float::adjusted_mantissa fast_float::digit_comp<double, char>(fast_float::parsed_number_string_t<char>&, fast_float::adjusted_mantissa)
Line
Count
Source
5444
35.1k
digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept {
5445
  // remove the invalid exponent bias
5446
35.1k
  am.power2 -= invalid_am_bias;
5447
5448
35.1k
  int32_t sci_exp = scientific_exponent(num);
5449
35.1k
  size_t max_digits = binary_format<T>::max_digits();
5450
35.1k
  size_t digits = 0;
5451
35.1k
  bigint bigmant;
5452
35.1k
  parse_mantissa(bigmant, num, max_digits, digits);
5453
  // can't underflow, since digits is at most max_digits.
5454
35.1k
  int32_t exponent = sci_exp + 1 - int32_t(digits);
5455
35.1k
  if (exponent >= 0) {
5456
12.4k
    return positive_digit_comp<T>(bigmant, exponent);
5457
22.7k
  } else {
5458
22.7k
    return negative_digit_comp<T>(bigmant, am, exponent);
5459
22.7k
  }
5460
35.1k
}
Unexecuted instantiation: fast_float::adjusted_mantissa fast_float::digit_comp<float, char>(fast_float::parsed_number_string_t<char>&, fast_float::adjusted_mantissa)
5461
5462
} // namespace fast_float
5463
5464
#endif
5465
5466
#ifndef FASTFLOAT_PARSE_NUMBER_H
5467
#define FASTFLOAT_PARSE_NUMBER_H
5468
5469
5470
#include <cmath>
5471
#include <cstring>
5472
#include <limits>
5473
5474
namespace fast_float {
5475
5476
namespace detail {
5477
/**
5478
 * Special case +inf, -inf, nan, infinity, -infinity.
5479
 * The case comparisons could be made much faster given that we know that the
5480
 * strings a null-free and fixed.
5481
 **/
5482
template <typename T, typename UC>
5483
from_chars_result_t<UC>
5484
    FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last,
5485
142k
                                       T &value, chars_format fmt) noexcept {
5486
142k
  from_chars_result_t<UC> answer{};
5487
142k
  answer.ptr = first;
5488
142k
  answer.ec = tinyobj_ff::ff_errc(); // be optimistic
5489
  // assume first < last, so dereference without checks;
5490
142k
  bool const minusSign = (*first == UC('-'));
5491
  // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
5492
142k
  if ((*first == UC('-')) ||
5493
111k
      (uint64_t(fmt & chars_format::allow_leading_plus) &&
5494
111k
       (*first == UC('+')))) {
5495
39.9k
    ++first;
5496
39.9k
  }
5497
142k
  if (last - first >= 3) {
5498
65.1k
    if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
5499
0
      answer.ptr = (first += 3);
5500
0
      value = minusSign ? -std::numeric_limits<T>::quiet_NaN()
5501
0
                        : std::numeric_limits<T>::quiet_NaN();
5502
      // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7,
5503
      // C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
5504
0
      if (first != last && *first == UC('(')) {
5505
0
        for (UC const *ptr = first + 1; ptr != last; ++ptr) {
5506
0
          if (*ptr == UC(')')) {
5507
0
            answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
5508
0
            break;
5509
0
          } else if (!((UC('a') <= *ptr && *ptr <= UC('z')) ||
5510
0
                       (UC('A') <= *ptr && *ptr <= UC('Z')) ||
5511
0
                       (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
5512
0
            break; // forbidden char, not nan(n-char-seq-opt)
5513
0
        }
5514
0
      }
5515
0
      return answer;
5516
0
    }
5517
65.1k
    if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
5518
0
      if ((last - first >= 8) &&
5519
0
          fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
5520
0
        answer.ptr = first + 8;
5521
0
      } else {
5522
0
        answer.ptr = first + 3;
5523
0
      }
5524
0
      value = minusSign ? -std::numeric_limits<T>::infinity()
5525
0
                        : std::numeric_limits<T>::infinity();
5526
0
      return answer;
5527
0
    }
5528
65.1k
  }
5529
142k
  answer.ec = tinyobj_ff::ff_errc::invalid_argument;
5530
142k
  return answer;
5531
142k
}
fast_float::from_chars_result_t<char> fast_float::detail::parse_infnan<double, char>(char const*, char const*, double&, fast_float::chars_format)
Line
Count
Source
5485
142k
                                       T &value, chars_format fmt) noexcept {
5486
142k
  from_chars_result_t<UC> answer{};
5487
142k
  answer.ptr = first;
5488
142k
  answer.ec = tinyobj_ff::ff_errc(); // be optimistic
5489
  // assume first < last, so dereference without checks;
5490
142k
  bool const minusSign = (*first == UC('-'));
5491
  // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
5492
142k
  if ((*first == UC('-')) ||
5493
111k
      (uint64_t(fmt & chars_format::allow_leading_plus) &&
5494
111k
       (*first == UC('+')))) {
5495
39.9k
    ++first;
5496
39.9k
  }
5497
142k
  if (last - first >= 3) {
5498
65.1k
    if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
5499
0
      answer.ptr = (first += 3);
5500
0
      value = minusSign ? -std::numeric_limits<T>::quiet_NaN()
5501
0
                        : std::numeric_limits<T>::quiet_NaN();
5502
      // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7,
5503
      // C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
5504
0
      if (first != last && *first == UC('(')) {
5505
0
        for (UC const *ptr = first + 1; ptr != last; ++ptr) {
5506
0
          if (*ptr == UC(')')) {
5507
0
            answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
5508
0
            break;
5509
0
          } else if (!((UC('a') <= *ptr && *ptr <= UC('z')) ||
5510
0
                       (UC('A') <= *ptr && *ptr <= UC('Z')) ||
5511
0
                       (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
5512
0
            break; // forbidden char, not nan(n-char-seq-opt)
5513
0
        }
5514
0
      }
5515
0
      return answer;
5516
0
    }
5517
65.1k
    if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
5518
0
      if ((last - first >= 8) &&
5519
0
          fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
5520
0
        answer.ptr = first + 8;
5521
0
      } else {
5522
0
        answer.ptr = first + 3;
5523
0
      }
5524
0
      value = minusSign ? -std::numeric_limits<T>::infinity()
5525
0
                        : std::numeric_limits<T>::infinity();
5526
0
      return answer;
5527
0
    }
5528
65.1k
  }
5529
142k
  answer.ec = tinyobj_ff::ff_errc::invalid_argument;
5530
142k
  return answer;
5531
142k
}
Unexecuted instantiation: fast_float::from_chars_result_t<char> fast_float::detail::parse_infnan<float, char>(char const*, char const*, float&, fast_float::chars_format)
5532
5533
/**
5534
 * Returns true if the floating-pointing rounding mode is to 'nearest'.
5535
 * It is the default on most system. This function is meant to be inexpensive.
5536
 * Credit : @mwalcott3
5537
 */
5538
1.66M
fastfloat_really_inline bool rounds_to_nearest() noexcept {
5539
  // https://lemire.me/blog/2020/06/26/gcc-not-nearest/
5540
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
5541
  return false;
5542
#endif
5543
  // See
5544
  // A fast function to check your floating-point rounding mode
5545
  // https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/
5546
  //
5547
  // This function is meant to be equivalent to :
5548
  // prior: #include <cfenv>
5549
  //  return fegetround() == FE_TONEAREST;
5550
  // However, it is expected to be much faster than the fegetround()
5551
  // function call.
5552
  //
5553
  // The volatile keyword prevents the compiler from computing the function
5554
  // at compile-time.
5555
  // There might be other ways to prevent compile-time optimizations (e.g.,
5556
  // asm). The value does not need to be std::numeric_limits<float>::min(), any
5557
  // small value so that 1 + x should round to 1 would do (after accounting for
5558
  // excess precision, as in 387 instructions).
5559
1.66M
  static float volatile fmin = std::numeric_limits<float>::min();
5560
1.66M
  float fmini = fmin; // we copy it so that it gets loaded at most once.
5561
//
5562
// Explanation:
5563
// Only when fegetround() == FE_TONEAREST do we have that
5564
// fmin + 1.0f == 1.0f - fmin.
5565
//
5566
// FE_UPWARD:
5567
//  fmin + 1.0f > 1
5568
//  1.0f - fmin == 1
5569
//
5570
// FE_DOWNWARD or  FE_TOWARDZERO:
5571
//  fmin + 1.0f == 1
5572
//  1.0f - fmin < 1
5573
//
5574
// Note: This may fail to be accurate if fast-math has been
5575
// enabled, as rounding conventions may not apply.
5576
#ifdef FASTFLOAT_VISUAL_STUDIO
5577
#pragma warning(push)
5578
//  todo: is there a VS warning?
5579
//  see
5580
//  https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
5581
#elif defined(__clang__)
5582
#pragma clang diagnostic push
5583
1.66M
#pragma clang diagnostic ignored "-Wfloat-equal"
5584
#elif defined(__GNUC__)
5585
#pragma GCC diagnostic push
5586
#pragma GCC diagnostic ignored "-Wfloat-equal"
5587
#endif
5588
1.66M
  return (fmini + 1.0f == 1.0f - fmini);
5589
#ifdef FASTFLOAT_VISUAL_STUDIO
5590
#pragma warning(pop)
5591
#elif defined(__clang__)
5592
#pragma clang diagnostic pop
5593
#elif defined(__GNUC__)
5594
#pragma GCC diagnostic pop
5595
#endif
5596
1.66M
}
5597
5598
} // namespace detail
5599
5600
template <typename T> struct from_chars_caller {
5601
  template <typename UC>
5602
  FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
5603
  call(UC const *first, UC const *last, T &value,
5604
1.87M
       parse_options_t<UC> options) noexcept {
5605
1.87M
    return from_chars_advanced(first, last, value, options);
5606
1.87M
  }
fast_float::from_chars_result_t<char> fast_float::from_chars_caller<double>::call<char>(char const*, char const*, double&, fast_float::parse_options_t<char>)
Line
Count
Source
5604
1.87M
       parse_options_t<UC> options) noexcept {
5605
1.87M
    return from_chars_advanced(first, last, value, options);
5606
1.87M
  }
Unexecuted instantiation: fast_float::from_chars_result_t<char> fast_float::from_chars_caller<float>::call<char>(char const*, char const*, float&, fast_float::parse_options_t<char>)
5607
};
5608
5609
#ifdef __STDCPP_FLOAT32_T__
5610
template <> struct from_chars_caller<std::float32_t> {
5611
  template <typename UC>
5612
  FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
5613
  call(UC const *first, UC const *last, std::float32_t &value,
5614
       parse_options_t<UC> options) noexcept {
5615
    // if std::float32_t is defined, and we are in C++23 mode; macro set for
5616
    // float32; set value to float due to equivalence between float and
5617
    // float32_t
5618
    float val;
5619
    auto ret = from_chars_advanced(first, last, val, options);
5620
    value = val;
5621
    return ret;
5622
  }
5623
};
5624
#endif
5625
5626
#ifdef __STDCPP_FLOAT64_T__
5627
template <> struct from_chars_caller<std::float64_t> {
5628
  template <typename UC>
5629
  FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
5630
  call(UC const *first, UC const *last, std::float64_t &value,
5631
       parse_options_t<UC> options) noexcept {
5632
    // if std::float64_t is defined, and we are in C++23 mode; macro set for
5633
    // float64; set value as double due to equivalence between double and
5634
    // float64_t
5635
    double val;
5636
    auto ret = from_chars_advanced(first, last, val, options);
5637
    value = val;
5638
    return ret;
5639
  }
5640
};
5641
#endif
5642
5643
template <typename T, typename UC, typename>
5644
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
5645
from_chars(UC const *first, UC const *last, T &value,
5646
1.87M
           chars_format fmt /*= chars_format::general*/) noexcept {
5647
1.87M
  return from_chars_caller<T>::call(first, last, value,
5648
1.87M
                                    parse_options_t<UC>(fmt));
5649
1.87M
}
fast_float::from_chars_result_t<char> fast_float::from_chars<double, char, int>(char const*, char const*, double&, fast_float::chars_format)
Line
Count
Source
5646
1.87M
           chars_format fmt /*= chars_format::general*/) noexcept {
5647
1.87M
  return from_chars_caller<T>::call(first, last, value,
5648
1.87M
                                    parse_options_t<UC>(fmt));
5649
1.87M
}
Unexecuted instantiation: fast_float::from_chars_result_t<char> fast_float::from_chars<float, char, int>(char const*, char const*, float&, fast_float::chars_format)
5650
5651
/**
5652
 * This function overload takes parsed_number_string_t structure that is created
5653
 * and populated either by from_chars_advanced function taking chars range and
5654
 * parsing options or other parsing custom function implemented by user.
5655
 */
5656
template <typename T, typename UC>
5657
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
5658
1.73M
from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
5659
5660
1.73M
  static_assert(is_supported_float_type<T>::value,
5661
1.73M
                "only some floating-point types are supported");
5662
1.73M
  static_assert(is_supported_char_type<UC>::value,
5663
1.73M
                "only char, wchar_t, char16_t and char32_t are supported");
5664
5665
1.73M
  from_chars_result_t<UC> answer;
5666
5667
1.73M
  answer.ec = tinyobj_ff::ff_errc(); // be optimistic
5668
1.73M
  answer.ptr = pns.lastmatch;
5669
  // The implementation of the Clinger's fast path is convoluted because
5670
  // we want round-to-nearest in all cases, irrespective of the rounding mode
5671
  // selected on the thread.
5672
  // We proceed optimistically, assuming that detail::rounds_to_nearest()
5673
  // returns true.
5674
1.73M
  if (binary_format<T>::min_exponent_fast_path() <= pns.exponent &&
5675
1.71M
      pns.exponent <= binary_format<T>::max_exponent_fast_path() &&
5676
1.70M
      !pns.too_many_digits) {
5677
    // Unfortunately, the conventional Clinger's fast path is only possible
5678
    // when the system rounds to the nearest float.
5679
    //
5680
    // We expect the next branch to almost always be selected.
5681
    // We could check it first (before the previous branch), but
5682
    // there might be performance advantages at having the check
5683
    // be last.
5684
1.66M
    if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
5685
      // We have that fegetround() == FE_TONEAREST.
5686
      // Next is Clinger's fast path.
5687
1.66M
      if (pns.mantissa <= binary_format<T>::max_mantissa_fast_path()) {
5688
1.65M
        value = T(pns.mantissa);
5689
1.65M
        if (pns.exponent < 0) {
5690
29.0k
          value = value / binary_format<T>::exact_power_of_ten(-pns.exponent);
5691
1.63M
        } else {
5692
1.63M
          value = value * binary_format<T>::exact_power_of_ten(pns.exponent);
5693
1.63M
        }
5694
1.65M
        if (pns.negative) {
5695
4.18k
          value = -value;
5696
4.18k
        }
5697
1.65M
        return answer;
5698
1.65M
      }
5699
1.66M
    } else {
5700
      // We do not have that fegetround() == FE_TONEAREST.
5701
      // Next is a modified Clinger's fast path, inspired by Jakub Jelínek's
5702
      // proposal
5703
0
      if (pns.exponent >= 0 &&
5704
0
          pns.mantissa <=
5705
0
              binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
5706
0
#if defined(__clang__) || defined(FASTFLOAT_32BIT)
5707
        // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
5708
0
        if (pns.mantissa == 0) {
5709
0
          value = pns.negative ? T(-0.) : T(0.);
5710
0
          return answer;
5711
0
        }
5712
0
#endif
5713
0
        value = T(pns.mantissa) *
5714
0
                binary_format<T>::exact_power_of_ten(pns.exponent);
5715
0
        if (pns.negative) {
5716
0
          value = -value;
5717
0
        }
5718
0
        return answer;
5719
0
      }
5720
0
    }
5721
1.66M
  }
5722
74.6k
  adjusted_mantissa am =
5723
74.6k
      compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
5724
74.6k
  if (pns.too_many_digits && am.power2 >= 0) {
5725
68.9k
    if (am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
5726
35.1k
      am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
5727
35.1k
    }
5728
68.9k
  }
5729
  // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa)
5730
  // and we have an invalid power (am.power2 < 0), then we need to go the long
5731
  // way around again. This is very uncommon.
5732
74.6k
  if (am.power2 < 0) {
5733
35.1k
    am = digit_comp<T>(pns, am);
5734
35.1k
  }
5735
74.6k
  to_float(pns.negative, am, value);
5736
  // Test for over/underflow.
5737
74.6k
  if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) ||
5738
73.0k
      am.power2 == binary_format<T>::infinite_power()) {
5739
5.37k
    answer.ec = tinyobj_ff::ff_errc::result_out_of_range;
5740
5.37k
  }
5741
74.6k
  return answer;
5742
1.73M
}
fast_float::from_chars_result_t<char> fast_float::from_chars_advanced<double, char>(fast_float::parsed_number_string_t<char>&, double&)
Line
Count
Source
5658
1.73M
from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
5659
5660
1.73M
  static_assert(is_supported_float_type<T>::value,
5661
1.73M
                "only some floating-point types are supported");
5662
1.73M
  static_assert(is_supported_char_type<UC>::value,
5663
1.73M
                "only char, wchar_t, char16_t and char32_t are supported");
5664
5665
1.73M
  from_chars_result_t<UC> answer;
5666
5667
1.73M
  answer.ec = tinyobj_ff::ff_errc(); // be optimistic
5668
1.73M
  answer.ptr = pns.lastmatch;
5669
  // The implementation of the Clinger's fast path is convoluted because
5670
  // we want round-to-nearest in all cases, irrespective of the rounding mode
5671
  // selected on the thread.
5672
  // We proceed optimistically, assuming that detail::rounds_to_nearest()
5673
  // returns true.
5674
1.73M
  if (binary_format<T>::min_exponent_fast_path() <= pns.exponent &&
5675
1.71M
      pns.exponent <= binary_format<T>::max_exponent_fast_path() &&
5676
1.70M
      !pns.too_many_digits) {
5677
    // Unfortunately, the conventional Clinger's fast path is only possible
5678
    // when the system rounds to the nearest float.
5679
    //
5680
    // We expect the next branch to almost always be selected.
5681
    // We could check it first (before the previous branch), but
5682
    // there might be performance advantages at having the check
5683
    // be last.
5684
1.66M
    if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
5685
      // We have that fegetround() == FE_TONEAREST.
5686
      // Next is Clinger's fast path.
5687
1.66M
      if (pns.mantissa <= binary_format<T>::max_mantissa_fast_path()) {
5688
1.65M
        value = T(pns.mantissa);
5689
1.65M
        if (pns.exponent < 0) {
5690
29.0k
          value = value / binary_format<T>::exact_power_of_ten(-pns.exponent);
5691
1.63M
        } else {
5692
1.63M
          value = value * binary_format<T>::exact_power_of_ten(pns.exponent);
5693
1.63M
        }
5694
1.65M
        if (pns.negative) {
5695
4.18k
          value = -value;
5696
4.18k
        }
5697
1.65M
        return answer;
5698
1.65M
      }
5699
1.66M
    } else {
5700
      // We do not have that fegetround() == FE_TONEAREST.
5701
      // Next is a modified Clinger's fast path, inspired by Jakub Jelínek's
5702
      // proposal
5703
0
      if (pns.exponent >= 0 &&
5704
0
          pns.mantissa <=
5705
0
              binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
5706
0
#if defined(__clang__) || defined(FASTFLOAT_32BIT)
5707
        // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
5708
0
        if (pns.mantissa == 0) {
5709
0
          value = pns.negative ? T(-0.) : T(0.);
5710
0
          return answer;
5711
0
        }
5712
0
#endif
5713
0
        value = T(pns.mantissa) *
5714
0
                binary_format<T>::exact_power_of_ten(pns.exponent);
5715
0
        if (pns.negative) {
5716
0
          value = -value;
5717
0
        }
5718
0
        return answer;
5719
0
      }
5720
0
    }
5721
1.66M
  }
5722
74.6k
  adjusted_mantissa am =
5723
74.6k
      compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
5724
74.6k
  if (pns.too_many_digits && am.power2 >= 0) {
5725
68.9k
    if (am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
5726
35.1k
      am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
5727
35.1k
    }
5728
68.9k
  }
5729
  // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa)
5730
  // and we have an invalid power (am.power2 < 0), then we need to go the long
5731
  // way around again. This is very uncommon.
5732
74.6k
  if (am.power2 < 0) {
5733
35.1k
    am = digit_comp<T>(pns, am);
5734
35.1k
  }
5735
74.6k
  to_float(pns.negative, am, value);
5736
  // Test for over/underflow.
5737
74.6k
  if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) ||
5738
73.0k
      am.power2 == binary_format<T>::infinite_power()) {
5739
5.37k
    answer.ec = tinyobj_ff::ff_errc::result_out_of_range;
5740
5.37k
  }
5741
74.6k
  return answer;
5742
1.73M
}
Unexecuted instantiation: fast_float::from_chars_result_t<char> fast_float::from_chars_advanced<float, char>(fast_float::parsed_number_string_t<char>&, float&)
5743
5744
template <typename T, typename UC>
5745
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
5746
from_chars_float_advanced(UC const *first, UC const *last, T &value,
5747
1.87M
                          parse_options_t<UC> options) noexcept {
5748
5749
1.87M
  static_assert(is_supported_float_type<T>::value,
5750
1.87M
                "only some floating-point types are supported");
5751
1.87M
  static_assert(is_supported_char_type<UC>::value,
5752
1.87M
                "only char, wchar_t, char16_t and char32_t are supported");
5753
5754
1.87M
  chars_format const fmt = detail::adjust_for_feature_macros(options.format);
5755
5756
1.87M
  from_chars_result_t<UC> answer;
5757
1.87M
  if (uint64_t(fmt & chars_format::skip_white_space)) {
5758
0
    while ((first != last) && fast_float::is_space(*first)) {
5759
0
      first++;
5760
0
    }
5761
0
  }
5762
1.87M
  if (first == last) {
5763
0
    answer.ec = tinyobj_ff::ff_errc::invalid_argument;
5764
0
    answer.ptr = first;
5765
0
    return answer;
5766
0
  }
5767
1.87M
  parsed_number_string_t<UC> pns =
5768
1.87M
      uint64_t(fmt & detail::basic_json_fmt)
5769
1.87M
          ? parse_number_string<true, UC>(first, last, options)
5770
1.87M
          : parse_number_string<false, UC>(first, last, options);
5771
1.87M
  if (!pns.valid) {
5772
142k
    if (uint64_t(fmt & chars_format::no_infnan)) {
5773
0
      answer.ec = tinyobj_ff::ff_errc::invalid_argument;
5774
0
      answer.ptr = first;
5775
0
      return answer;
5776
142k
    } else {
5777
142k
      return detail::parse_infnan(first, last, value, fmt);
5778
142k
    }
5779
142k
  }
5780
5781
  // call overload that takes parsed_number_string_t directly.
5782
1.73M
  return from_chars_advanced(pns, value);
5783
1.87M
}
fast_float::from_chars_result_t<char> fast_float::from_chars_float_advanced<double, char>(char const*, char const*, double&, fast_float::parse_options_t<char>)
Line
Count
Source
5747
1.87M
                          parse_options_t<UC> options) noexcept {
5748
5749
1.87M
  static_assert(is_supported_float_type<T>::value,
5750
1.87M
                "only some floating-point types are supported");
5751
1.87M
  static_assert(is_supported_char_type<UC>::value,
5752
1.87M
                "only char, wchar_t, char16_t and char32_t are supported");
5753
5754
1.87M
  chars_format const fmt = detail::adjust_for_feature_macros(options.format);
5755
5756
1.87M
  from_chars_result_t<UC> answer;
5757
1.87M
  if (uint64_t(fmt & chars_format::skip_white_space)) {
5758
0
    while ((first != last) && fast_float::is_space(*first)) {
5759
0
      first++;
5760
0
    }
5761
0
  }
5762
1.87M
  if (first == last) {
5763
0
    answer.ec = tinyobj_ff::ff_errc::invalid_argument;
5764
0
    answer.ptr = first;
5765
0
    return answer;
5766
0
  }
5767
1.87M
  parsed_number_string_t<UC> pns =
5768
1.87M
      uint64_t(fmt & detail::basic_json_fmt)
5769
1.87M
          ? parse_number_string<true, UC>(first, last, options)
5770
1.87M
          : parse_number_string<false, UC>(first, last, options);
5771
1.87M
  if (!pns.valid) {
5772
142k
    if (uint64_t(fmt & chars_format::no_infnan)) {
5773
0
      answer.ec = tinyobj_ff::ff_errc::invalid_argument;
5774
0
      answer.ptr = first;
5775
0
      return answer;
5776
142k
    } else {
5777
142k
      return detail::parse_infnan(first, last, value, fmt);
5778
142k
    }
5779
142k
  }
5780
5781
  // call overload that takes parsed_number_string_t directly.
5782
1.73M
  return from_chars_advanced(pns, value);
5783
1.87M
}
Unexecuted instantiation: fast_float::from_chars_result_t<char> fast_float::from_chars_float_advanced<float, char>(char const*, char const*, float&, fast_float::parse_options_t<char>)
5784
5785
template <typename T, typename UC, typename>
5786
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
5787
from_chars(UC const *first, UC const *last, T &value, int base) noexcept {
5788
5789
  static_assert(is_supported_integer_type<T>::value,
5790
                "only integer types are supported");
5791
  static_assert(is_supported_char_type<UC>::value,
5792
                "only char, wchar_t, char16_t and char32_t are supported");
5793
5794
  parse_options_t<UC> options;
5795
  options.base = base;
5796
  return from_chars_advanced(first, last, value, options);
5797
}
5798
5799
template <typename T, typename UC>
5800
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
5801
from_chars_int_advanced(UC const *first, UC const *last, T &value,
5802
                        parse_options_t<UC> options) noexcept {
5803
5804
  static_assert(is_supported_integer_type<T>::value,
5805
                "only integer types are supported");
5806
  static_assert(is_supported_char_type<UC>::value,
5807
                "only char, wchar_t, char16_t and char32_t are supported");
5808
5809
  chars_format const fmt = detail::adjust_for_feature_macros(options.format);
5810
  int const base = options.base;
5811
5812
  from_chars_result_t<UC> answer;
5813
  if (uint64_t(fmt & chars_format::skip_white_space)) {
5814
    while ((first != last) && fast_float::is_space(*first)) {
5815
      first++;
5816
    }
5817
  }
5818
  if (first == last || base < 2 || base > 36) {
5819
    answer.ec = tinyobj_ff::ff_errc::invalid_argument;
5820
    answer.ptr = first;
5821
    return answer;
5822
  }
5823
5824
  return parse_int_string(first, last, value, options);
5825
}
5826
5827
template <size_t TypeIx> struct from_chars_advanced_caller {
5828
  static_assert(TypeIx > 0, "unsupported type");
5829
};
5830
5831
template <> struct from_chars_advanced_caller<1> {
5832
  template <typename T, typename UC>
5833
  FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
5834
  call(UC const *first, UC const *last, T &value,
5835
1.87M
       parse_options_t<UC> options) noexcept {
5836
1.87M
    return from_chars_float_advanced(first, last, value, options);
5837
1.87M
  }
fast_float::from_chars_result_t<char> fast_float::from_chars_advanced_caller<1ul>::call<double, char>(char const*, char const*, double&, fast_float::parse_options_t<char>)
Line
Count
Source
5835
1.87M
       parse_options_t<UC> options) noexcept {
5836
1.87M
    return from_chars_float_advanced(first, last, value, options);
5837
1.87M
  }
Unexecuted instantiation: fast_float::from_chars_result_t<char> fast_float::from_chars_advanced_caller<1ul>::call<float, char>(char const*, char const*, float&, fast_float::parse_options_t<char>)
5838
};
5839
5840
template <> struct from_chars_advanced_caller<2> {
5841
  template <typename T, typename UC>
5842
  FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
5843
  call(UC const *first, UC const *last, T &value,
5844
       parse_options_t<UC> options) noexcept {
5845
    return from_chars_int_advanced(first, last, value, options);
5846
  }
5847
};
5848
5849
template <typename T, typename UC>
5850
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
5851
from_chars_advanced(UC const *first, UC const *last, T &value,
5852
1.87M
                    parse_options_t<UC> options) noexcept {
5853
1.87M
  return from_chars_advanced_caller<
5854
1.87M
      size_t(is_supported_float_type<T>::value) +
5855
1.87M
      2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value,
5856
1.87M
                                                             options);
5857
1.87M
}
fast_float::from_chars_result_t<char> fast_float::from_chars_advanced<double, char>(char const*, char const*, double&, fast_float::parse_options_t<char>)
Line
Count
Source
5852
1.87M
                    parse_options_t<UC> options) noexcept {
5853
1.87M
  return from_chars_advanced_caller<
5854
1.87M
      size_t(is_supported_float_type<T>::value) +
5855
1.87M
      2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value,
5856
1.87M
                                                             options);
5857
1.87M
}
Unexecuted instantiation: fast_float::from_chars_result_t<char> fast_float::from_chars_advanced<float, char>(char const*, char const*, float&, fast_float::parse_options_t<char>)
5858
5859
} // namespace fast_float
5860
5861
#endif
5862
5863
5864
// --- End embedded fast_float ---
5865
5866
// Clean up fast_float macros to avoid polluting the user's namespace.
5867
#undef FASTFLOAT_32BIT
5868
#undef FASTFLOAT_32BIT_LIMB
5869
#undef FASTFLOAT_64BIT
5870
#undef FASTFLOAT_64BIT_LIMB
5871
#undef FASTFLOAT_ASCII_NUMBER_H
5872
#undef FASTFLOAT_ASSERT
5873
#undef FASTFLOAT_BIGINT_H
5874
#undef FASTFLOAT_CONSTEXPR14
5875
#undef FASTFLOAT_CONSTEXPR20
5876
#undef FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
5877
#undef FASTFLOAT_DEBUG_ASSERT
5878
#undef FASTFLOAT_DECIMAL_TO_BINARY_H
5879
#undef FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
5880
#undef FASTFLOAT_DIGIT_COMPARISON_H
5881
#undef FASTFLOAT_ENABLE_IF
5882
#undef FASTFLOAT_FAST_FLOAT_H
5883
#undef FASTFLOAT_FAST_TABLE_H
5884
#undef FASTFLOAT_FLOAT_COMMON_H
5885
#undef FASTFLOAT_HAS_BIT_CAST
5886
#undef FASTFLOAT_HAS_IS_CONSTANT_EVALUATED
5887
#undef FASTFLOAT_HAS_SIMD
5888
#undef FASTFLOAT_IF_CONSTEXPR17
5889
#undef FASTFLOAT_IS_BIG_ENDIAN
5890
#undef FASTFLOAT_IS_CONSTEXPR
5891
#undef FASTFLOAT_NEON
5892
#undef FASTFLOAT_PARSE_NUMBER_H
5893
#undef fastfloat_really_inline
5894
#undef FASTFLOAT_SIMD_DISABLE_WARNINGS
5895
#undef FASTFLOAT_SIMD_RESTORE_WARNINGS
5896
#undef FASTFLOAT_SSE2
5897
#undef FASTFLOAT_STRINGIZE
5898
#undef FASTFLOAT_STRINGIZE_IMPL
5899
#undef FASTFLOAT_TRY
5900
#undef FASTFLOAT_VERSION
5901
#undef FASTFLOAT_VERSION_MAJOR
5902
#undef FASTFLOAT_VERSION_MINOR
5903
#undef FASTFLOAT_VERSION_PATCH
5904
#undef FASTFLOAT_VERSION_STR
5905
#undef FASTFLOAT_VISUAL_STUDIO
5906
5907
#endif  // TINYOBJLOADER_DISABLE_FAST_FLOAT
5908
5909
namespace tinyobj {
5910
5911
11.4k
MaterialReader::~MaterialReader() {}
5912
5913
// Byte-stream reader for bounds-checked text parsing.
5914
// Replaces raw `const char*` token pointers with `(buf, len, idx)` triple.
5915
// Every byte access is guarded by an EOF check.
5916
class StreamReader {
5917
 public:
5918
// Maximum number of bytes StreamReader will buffer from std::istream.
5919
// Define this macro to a larger value if your application needs to parse
5920
// very large streamed OBJ/MTL content.
5921
#ifndef TINYOBJLOADER_STREAM_READER_MAX_BYTES
5922
23.3k
#define TINYOBJLOADER_STREAM_READER_MAX_BYTES (size_t(256) * size_t(1024) * size_t(1024))
5923
#endif
5924
5925
  StreamReader(const char *buf, size_t length)
5926
0
      : buf_(buf), length_(length), idx_(0), line_num_(1), col_num_(1) {}
5927
5928
  // Non-copyable, non-movable: buf_ may point into owned_buf_.
5929
  StreamReader(const StreamReader &) /* = delete */;
5930
  StreamReader &operator=(const StreamReader &) /* = delete */;
5931
5932
  // Build from std::istream by reading all content into an internal buffer.
5933
23.3k
  explicit StreamReader(std::istream &is) : buf_(NULL), length_(0), idx_(0), line_num_(1), col_num_(1) {
5934
23.3k
    const size_t max_stream_bytes = TINYOBJLOADER_STREAM_READER_MAX_BYTES;
5935
23.3k
    std::streampos start_pos = is.tellg();
5936
23.3k
    bool can_seek = (start_pos != std::streampos(-1));
5937
23.3k
    if (can_seek) {
5938
23.3k
      is.seekg(0, std::ios::end);
5939
23.3k
      std::streampos end_pos = is.tellg();
5940
23.3k
      if (end_pos >= start_pos) {
5941
23.3k
        std::streamoff remaining_off = static_cast<std::streamoff>(end_pos - start_pos);
5942
23.3k
        is.seekg(start_pos);
5943
23.3k
        unsigned long long remaining_ull = static_cast<unsigned long long>(remaining_off);
5944
23.3k
        if (remaining_ull > static_cast<unsigned long long>((std::numeric_limits<size_t>::max)())) {
5945
0
          std::stringstream ss;
5946
0
          ss << "input stream too large for this platform (" << remaining_ull
5947
0
             << " bytes exceeds size_t max " << (std::numeric_limits<size_t>::max)() << ")\n";
5948
0
          push_error(ss.str());
5949
0
          buf_ = "";
5950
0
          length_ = 0;
5951
0
          return;
5952
0
        }
5953
23.3k
        size_t remaining_size = static_cast<size_t>(remaining_ull);
5954
23.3k
        if (remaining_size > max_stream_bytes) {
5955
0
          std::stringstream ss;
5956
0
          ss << "input stream too large (" << remaining_size
5957
0
             << " bytes exceeds limit " << max_stream_bytes << " bytes)\n";
5958
0
          push_error(ss.str());
5959
0
          buf_ = "";
5960
0
          length_ = 0;
5961
0
          return;
5962
0
        }
5963
23.3k
        owned_buf_.resize(remaining_size);
5964
23.3k
        if (remaining_size > 0) {
5965
16.7k
          is.read(&owned_buf_[0], static_cast<std::streamsize>(remaining_size));
5966
16.7k
        }
5967
23.3k
        size_t actually_read = static_cast<size_t>(is.gcount());
5968
23.3k
        owned_buf_.resize(actually_read);
5969
23.3k
      }
5970
23.3k
    }
5971
23.3k
    if (!can_seek || owned_buf_.empty()) {
5972
      // Stream doesn't support seeking, or seek probing failed.
5973
13
      if (can_seek) is.seekg(start_pos);
5974
13
      is.clear();
5975
13
      std::vector<char> content;
5976
13
      char chunk[4096];
5977
13
      size_t total_read = 0;
5978
13
      while (is.good()) {
5979
13
        is.read(chunk, static_cast<std::streamsize>(sizeof(chunk)));
5980
13
        std::streamsize nread = is.gcount();
5981
13
        if (nread <= 0) break;
5982
0
        size_t n = static_cast<size_t>(nread);
5983
0
        if (n > (max_stream_bytes - total_read)) {
5984
0
          std::stringstream ss;
5985
0
          ss << "input stream too large (exceeds limit " << max_stream_bytes
5986
0
             << " bytes)\n";
5987
0
          push_error(ss.str());
5988
0
          owned_buf_.clear();
5989
0
          buf_ = "";
5990
0
          length_ = 0;
5991
0
          return;
5992
0
        }
5993
0
        content.insert(content.end(), chunk, chunk + n);
5994
0
        total_read += n;
5995
0
      }
5996
13
      owned_buf_.swap(content);
5997
13
    }
5998
23.3k
    buf_ = owned_buf_.empty() ? "" : &owned_buf_[0];
5999
23.3k
    length_ = owned_buf_.size();
6000
23.3k
  }
6001
6002
49.3M
  bool eof() const { return idx_ >= length_; }
6003
0
  size_t tell() const { return idx_; }
6004
0
  size_t size() const { return length_; }
6005
8.60M
  size_t line_num() const { return line_num_; }
6006
0
  size_t col_num() const { return col_num_; }
6007
6008
123M
  char peek() const {
6009
123M
    if (idx_ >= length_) return '\0';
6010
123M
    return buf_[idx_];
6011
123M
  }
6012
6013
0
  char get() {
6014
0
    if (idx_ >= length_) return '\0';
6015
0
    char c = buf_[idx_++];
6016
0
    if (c == '\n') { line_num_++; col_num_ = 1; } else { col_num_++; }
6017
0
    return c;
6018
0
  }
6019
6020
53.4M
  void advance(size_t n) {
6021
131M
    for (size_t i = 0; i < n && idx_ < length_; i++) {
6022
77.7M
      if (buf_[idx_] == '\n') { line_num_++; col_num_ = 1; } else { col_num_++; }
6023
77.7M
      idx_++;
6024
77.7M
    }
6025
53.4M
  }
6026
6027
52.6M
  void skip_space() {
6028
54.8M
    while (idx_ < length_ && (buf_[idx_] == ' ' || buf_[idx_] == '\t')) {
6029
2.22M
      col_num_++;
6030
2.22M
      idx_++;
6031
2.22M
    }
6032
52.6M
  }
6033
6034
6.16M
  void skip_space_and_cr() {
6035
12.1M
    while (idx_ < length_ && (buf_[idx_] == ' ' || buf_[idx_] == '\t' || buf_[idx_] == '\r')) {
6036
5.98M
      col_num_++;
6037
5.98M
      idx_++;
6038
5.98M
    }
6039
6.16M
  }
6040
6041
9.60M
  void skip_line() {
6042
107M
    while (idx_ < length_) {
6043
107M
      char c = buf_[idx_];
6044
107M
      if (c == '\n') {
6045
7.13M
        idx_++;
6046
7.13M
        line_num_++;
6047
7.13M
        col_num_ = 1;
6048
7.13M
        return;
6049
7.13M
      }
6050
99.9M
      if (c == '\r') {
6051
2.45M
        idx_++;
6052
2.45M
        if (idx_ < length_ && buf_[idx_] == '\n') {
6053
184k
          idx_++;
6054
184k
        }
6055
2.45M
        line_num_++;
6056
2.45M
        col_num_ = 1;
6057
2.45M
        return;
6058
2.45M
      }
6059
97.5M
      col_num_++;
6060
97.5M
      idx_++;
6061
97.5M
    }
6062
9.60M
  }
6063
6064
19.7M
  bool at_line_end() const {
6065
19.7M
    if (idx_ >= length_) return true;
6066
19.7M
    char c = buf_[idx_];
6067
19.7M
    return (c == '\n' || c == '\r' || c == '\0');
6068
19.7M
  }
6069
6070
2.38M
  std::string read_line() {
6071
2.38M
    std::string result;
6072
120M
    while (idx_ < length_) {
6073
120M
      char c = buf_[idx_];
6074
120M
      if (c == '\n' || c == '\r') break;
6075
118M
      result += c;
6076
118M
      col_num_++;
6077
118M
      idx_++;
6078
118M
    }
6079
2.38M
    return result;
6080
2.38M
  }
6081
6082
  // Reads a whitespace-delimited token. Used by tests and as a general utility.
6083
0
  std::string read_token() {
6084
0
    skip_space();
6085
0
    std::string result;
6086
0
    while (idx_ < length_) {
6087
0
      char c = buf_[idx_];
6088
0
      if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break;
6089
0
      result += c;
6090
0
      col_num_++;
6091
0
      idx_++;
6092
0
    }
6093
0
    return result;
6094
0
  }
6095
6096
47.1M
  bool match(const char *prefix, size_t len) const {
6097
47.1M
    if (idx_ >= length_ || len > length_ - idx_) return false;
6098
47.1M
    return (memcmp(buf_ + idx_, prefix, len) == 0);
6099
47.1M
  }
6100
6101
0
  bool char_at(size_t offset, char c) const {
6102
0
    if (idx_ >= length_ || offset >= length_ - idx_) return false;
6103
0
    return buf_[idx_ + offset] == c;
6104
0
  }
6105
6106
16.1M
  char peek_at(size_t offset) const {
6107
16.1M
    if (idx_ >= length_ || offset >= length_ - idx_) return '\0';
6108
16.1M
    return buf_[idx_ + offset];
6109
16.1M
  }
6110
6111
34.1M
  const char *current_ptr() const {
6112
34.1M
    if (idx_ >= length_) return "";
6113
33.2M
    return buf_ + idx_;
6114
34.1M
  }
6115
6116
35.9M
  size_t remaining() const {
6117
35.9M
    return (idx_ < length_) ? (length_ - idx_) : 0;
6118
35.9M
  }
6119
6120
  // Returns the full text of the current line (for diagnostic display).
6121
1.40k
  std::string current_line_text() const {
6122
    // Scan backward to find line start
6123
1.40k
    size_t line_start = idx_;
6124
3.56M
    while (line_start > 0 && buf_[line_start - 1] != '\n' && buf_[line_start - 1] != '\r') {
6125
3.56M
      line_start--;
6126
3.56M
    }
6127
    // Scan forward to find line end
6128
1.40k
    size_t line_end = idx_;
6129
16.3M
    while (line_end < length_ && buf_[line_end] != '\n' && buf_[line_end] != '\r') {
6130
16.3M
      line_end++;
6131
16.3M
    }
6132
1.40k
    return std::string(buf_ + line_start, line_end - line_start);
6133
1.40k
  }
6134
6135
  // Clang-style formatted error with file:line:col and caret.
6136
1.40k
  std::string format_error(const std::string &filename, const std::string &msg) const {
6137
1.40k
    std::stringstream line_ss, col_ss;
6138
1.40k
    line_ss << line_num_;
6139
1.40k
    col_ss << col_num_;
6140
1.40k
    std::string result;
6141
1.40k
    result += filename + ":" + line_ss.str() + ":" + col_ss.str() + ": error: " + msg + "\n";
6142
1.40k
    std::string line_text = current_line_text();
6143
1.40k
    result += line_text + "\n";
6144
    // Build caret line preserving tab alignment
6145
1.40k
    std::string caret;
6146
1.40k
    size_t caret_pos = (col_num_ > 0) ? (col_num_ - 1) : 0;
6147
3.56M
    for (size_t i = 0; i < caret_pos && i < line_text.size(); i++) {
6148
3.56M
      caret += (line_text[i] == '\t') ? '\t' : ' ';
6149
3.56M
    }
6150
1.40k
    caret += "^";
6151
1.40k
    result += caret + "\n";
6152
1.40k
    return result;
6153
1.40k
  }
6154
6155
0
  std::string format_error(const std::string &msg) const {
6156
0
    return format_error("<input>", msg);
6157
0
  }
6158
6159
  // Error stack
6160
0
  void push_error(const std::string &msg) {
6161
0
    errors_.push_back(msg);
6162
0
  }
6163
6164
0
  void push_formatted_error(const std::string &filename, const std::string &msg) {
6165
0
    errors_.push_back(format_error(filename, msg));
6166
0
  }
6167
6168
23.3k
  bool has_errors() const { return !errors_.empty(); }
6169
6170
0
  std::string get_errors() const {
6171
0
    std::string result;
6172
0
    for (size_t i = 0; i < errors_.size(); i++) {
6173
0
      result += errors_[i];
6174
0
    }
6175
0
    return result;
6176
0
  }
6177
6178
0
  const std::vector<std::string> &error_stack() const { return errors_; }
6179
6180
0
  void clear_errors() { errors_.clear(); }
6181
6182
 private:
6183
  const char *buf_;
6184
  size_t length_;
6185
  size_t idx_;
6186
  size_t line_num_;
6187
  size_t col_num_;
6188
  std::vector<char> owned_buf_;
6189
  std::vector<std::string> errors_;
6190
};
6191
6192
#ifdef TINYOBJLOADER_USE_MMAP
6193
// RAII wrapper for memory-mapped file I/O.
6194
// Opens a file and maps it into memory; the mapping is released on destruction.
6195
// For empty files, data is set to "" and is_mapped remains false so close()
6196
// will not attempt to unmap a string literal.
6197
struct MappedFile {
6198
  const char *data;
6199
  size_t size;
6200
  bool is_mapped;  // true when data points to an actual mapped region
6201
#if defined(_WIN32)
6202
  HANDLE hFile;
6203
  HANDLE hMapping;
6204
#else
6205
  void *mapped_ptr;
6206
#endif
6207
6208
  MappedFile() : data(NULL), size(0), is_mapped(false)
6209
#if defined(_WIN32)
6210
    , hFile(INVALID_HANDLE_VALUE), hMapping(NULL)
6211
#else
6212
    , mapped_ptr(NULL)
6213
#endif
6214
  {}
6215
6216
  // Opens and maps the file. Returns true on success.
6217
  bool open(const char *filepath) {
6218
#if defined(_WIN32)
6219
    std::wstring wfilepath = LongPathW(UTF8ToWchar(std::string(filepath)));
6220
    hFile = CreateFileW(wfilepath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
6221
                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
6222
    if (hFile == INVALID_HANDLE_VALUE) return false;
6223
    LARGE_INTEGER fileSize;
6224
    if (!GetFileSizeEx(hFile, &fileSize)) { close(); return false; }
6225
    if (fileSize.QuadPart < 0) { close(); return false; }
6226
    unsigned long long fsize = static_cast<unsigned long long>(fileSize.QuadPart);
6227
    if (fsize > static_cast<unsigned long long>((std::numeric_limits<size_t>::max)())) {
6228
      close();
6229
      return false;
6230
    }
6231
    size = static_cast<size_t>(fsize);
6232
    if (size == 0) { data = ""; return true; }  // valid but empty; is_mapped stays false
6233
    hMapping = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
6234
    if (hMapping == NULL) { close(); return false; }
6235
    data = static_cast<const char *>(MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0));
6236
    if (!data) { close(); return false; }
6237
    is_mapped = true;
6238
    return true;
6239
#else
6240
    int fd = ::open(filepath, O_RDONLY);
6241
    if (fd == -1) return false;
6242
    struct stat sb;
6243
    if (fstat(fd, &sb) != 0) { ::close(fd); return false; }
6244
    if (sb.st_size < 0) { ::close(fd); return false; }
6245
    if (static_cast<unsigned long long>(sb.st_size) >
6246
        static_cast<unsigned long long>((std::numeric_limits<size_t>::max)())) {
6247
      ::close(fd);
6248
      return false;
6249
    }
6250
    size = static_cast<size_t>(sb.st_size);
6251
    if (size == 0) { ::close(fd); data = ""; return true; }  // valid but empty
6252
    mapped_ptr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
6253
    ::close(fd);
6254
    if (mapped_ptr == MAP_FAILED) { mapped_ptr = NULL; size = 0; return false; }
6255
    data = static_cast<const char *>(mapped_ptr);
6256
    is_mapped = true;
6257
    return true;
6258
#endif
6259
  }
6260
6261
  void close() {
6262
#if defined(_WIN32)
6263
    if (is_mapped && data) { UnmapViewOfFile(data); }
6264
    data = NULL;
6265
    is_mapped = false;
6266
    if (hMapping != NULL) { CloseHandle(hMapping); hMapping = NULL; }
6267
    if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; }
6268
#else
6269
    if (is_mapped && mapped_ptr && mapped_ptr != MAP_FAILED) { munmap(mapped_ptr, size); }
6270
    mapped_ptr = NULL;
6271
    data = NULL;
6272
    is_mapped = false;
6273
#endif
6274
    size = 0;
6275
  }
6276
6277
  ~MappedFile() { close(); }
6278
6279
 private:
6280
  MappedFile(const MappedFile &);             // non-copyable
6281
  MappedFile &operator=(const MappedFile &);  // non-copyable
6282
};
6283
#endif  // TINYOBJLOADER_USE_MMAP
6284
6285
6286
struct vertex_index_t {
6287
  int v_idx, vt_idx, vn_idx;
6288
5.03M
  vertex_index_t() : v_idx(-1), vt_idx(-1), vn_idx(-1) {}
6289
4.99M
  explicit vertex_index_t(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {}
6290
  vertex_index_t(int vidx, int vtidx, int vnidx)
6291
0
      : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}
6292
};
6293
6294
// Internal data structure for face representation
6295
// index + smoothing group.
6296
struct face_t {
6297
  unsigned int
6298
      smoothing_group_id;  // smoothing group id. 0 = smoothing groupd is off.
6299
  int pad_;
6300
  std::vector<vertex_index_t> vertex_indices;  // face vertex indices.
6301
6302
529k
  face_t() : smoothing_group_id(0), pad_(0) {}
6303
};
6304
6305
// Internal data structure for line representation
6306
struct __line_t {
6307
  // l v1/vt1 v2/vt2 ...
6308
  // In the specification, line primitrive does not have normal index, but
6309
  // TinyObjLoader allow it
6310
  std::vector<vertex_index_t> vertex_indices;
6311
};
6312
6313
// Internal data structure for points representation
6314
struct __points_t {
6315
  // p v1 v2 ...
6316
  // In the specification, point primitrive does not have normal index and
6317
  // texture coord index, but TinyObjLoader allow it.
6318
  std::vector<vertex_index_t> vertex_indices;
6319
};
6320
6321
struct tag_sizes {
6322
903k
  tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {}
6323
  int num_ints;
6324
  int num_reals;
6325
  int num_strings;
6326
};
6327
6328
struct obj_shape {
6329
  std::vector<real_t> v;
6330
  std::vector<real_t> vn;
6331
  std::vector<real_t> vt;
6332
};
6333
6334
//
6335
// Manages group of primitives(face, line, points, ...)
6336
struct PrimGroup {
6337
  std::vector<face_t> faceGroup;
6338
  std::vector<__line_t> lineGroup;
6339
  std::vector<__points_t> pointsGroup;
6340
6341
457k
  void clear() {
6342
457k
    faceGroup.clear();
6343
457k
    lineGroup.clear();
6344
457k
    pointsGroup.clear();
6345
457k
  }
6346
6347
484k
  bool IsEmpty() const {
6348
484k
    return faceGroup.empty() && lineGroup.empty() && pointsGroup.empty();
6349
484k
  }
6350
6351
  // TODO(syoyo): bspline, surface, ...
6352
};
6353
6354
// See
6355
// http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf
6356
3.78M
#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t'))
6357
#define IS_DIGIT(x) \
6358
  (static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10))
6359
805k
#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0'))
6360
6361
template <typename T>
6362
18.0k
static inline std::string toString(const T &t) {
6363
18.0k
  std::stringstream ss;
6364
18.0k
  ss << t;
6365
18.0k
  return ss.str();
6366
18.0k
}
6367
6368
0
static inline std::string removeUtf8Bom(const std::string& input) {
6369
0
    // UTF-8 BOM = 0xEF,0xBB,0xBF
6370
0
    if (input.size() >= 3 &&
6371
0
        static_cast<unsigned char>(input[0]) == 0xEF &&
6372
0
        static_cast<unsigned char>(input[1]) == 0xBB &&
6373
0
        static_cast<unsigned char>(input[2]) == 0xBF) {
6374
0
        return input.substr(3); // Skip BOM
6375
0
    }
6376
0
    return input;
6377
0
}
6378
6379
// Trim trailing spaces and tabs from a string.
6380
2.02M
static inline std::string trimTrailingWhitespace(const std::string &s) {
6381
2.02M
  size_t end = s.find_last_not_of(" \t");
6382
2.02M
  if (end == std::string::npos) return "";
6383
2.01M
  return s.substr(0, end + 1);
6384
2.02M
}
6385
6386
struct warning_context {
6387
  std::string *warn;
6388
  size_t line_number;
6389
  std::string filename;
6390
};
6391
6392
// Safely convert size_t to int, clamping at INT_MAX to prevent overflow.
6393
14.9M
static inline int size_to_int(size_t sz) {
6394
14.9M
  return sz > static_cast<size_t>(INT_MAX) ? INT_MAX : static_cast<int>(sz);
6395
14.9M
}
6396
6397
// Make index zero-base, and also support relative index.
6398
static inline bool fixIndex(int idx, int n, int *ret, bool allow_zero,
6399
4.99M
                            const warning_context &context) {
6400
4.99M
  if (!ret) {
6401
0
    return false;
6402
0
  }
6403
6404
4.99M
  if (idx > 0) {
6405
4.97M
    (*ret) = idx - 1;
6406
4.97M
    return true;
6407
4.97M
  }
6408
6409
18.7k
  if (idx == 0) {
6410
    // zero is not allowed according to the spec.
6411
18.0k
    if (context.warn) {
6412
18.0k
      (*context.warn) +=
6413
18.0k
          context.filename + ":" + toString(context.line_number) +
6414
18.0k
          ": warning: zero value index found (will have a value of -1 for "
6415
18.0k
          "normal and tex indices)\n";
6416
18.0k
    }
6417
6418
18.0k
    (*ret) = idx - 1;
6419
18.0k
    return allow_zero;
6420
18.0k
  }
6421
6422
618
  if (idx < 0) {
6423
618
    (*ret) = n + idx;  // negative value = relative
6424
618
    if ((*ret) < 0) {
6425
46
      return false;  // invalid relative index
6426
46
    }
6427
572
    return true;
6428
618
  }
6429
6430
0
  return false;  // never reach here.
6431
618
}
6432
6433
4.00k
static inline std::string parseString(const char **token) {
6434
4.00k
  std::string s;
6435
4.00k
  (*token) += strspn((*token), " \t");
6436
4.00k
  size_t e = strcspn((*token), " \t\r");
6437
4.00k
  s = std::string((*token), &(*token)[e]);
6438
4.00k
  (*token) += e;
6439
4.00k
  return s;
6440
4.00k
}
6441
6442
1.83k
static inline int parseInt(const char **token) {
6443
1.83k
  (*token) += strspn((*token), " \t");
6444
1.83k
  int i = atoi((*token));
6445
1.83k
  (*token) += strcspn((*token), " \t\r");
6446
1.83k
  return i;
6447
1.83k
}
6448
6449
#ifndef TINYOBJLOADER_DISABLE_FAST_FLOAT
6450
6451
// ---- fast_float-based float parser (bit-exact with strtod, ~3x faster) ----
6452
6453
namespace detail_fp {
6454
6455
// Case-insensitive prefix match. Returns pointer past matched prefix, or NULL.
6456
static inline const char *match_iprefix(const char *p, const char *end,
6457
134k
                                        const char *prefix) {
6458
399k
  while (*prefix) {
6459
369k
    if (p == end) return NULL;
6460
364k
    char c = *p;
6461
364k
    char e = *prefix;
6462
364k
    if (c >= 'A' && c <= 'Z') c += 32;
6463
364k
    if (e >= 'A' && e <= 'Z') e += 32;
6464
364k
    if (c != e) return NULL;
6465
264k
    ++p;
6466
264k
    ++prefix;
6467
264k
  }
6468
29.7k
  return p;
6469
134k
}
6470
6471
// Try to parse nan/inf. Returns true if matched, sets *result and *end_ptr.
6472
static inline bool tryParseNanInf(const char *first, const char *last,
6473
49.2k
                                  double *result, const char **end_ptr) {
6474
49.2k
  if (first >= last) return false;
6475
6476
49.2k
  const char *p = first;
6477
49.2k
  bool negative = false;
6478
6479
49.2k
  if (*p == '-') {
6480
22.0k
    negative = true;
6481
22.0k
    ++p;
6482
27.2k
  } else if (*p == '+') {
6483
5.33k
    ++p;
6484
5.33k
  }
6485
6486
49.2k
  if (p >= last) return false;
6487
6488
  // Try "nan"
6489
49.2k
  const char *after = match_iprefix(p, last, "nan");
6490
49.2k
  if (after) {
6491
3.04k
    *result = 0.0;  // nan -> 0.0 for OBJ
6492
3.04k
    *end_ptr = after;
6493
3.04k
    return true;
6494
3.04k
  }
6495
6496
  // Try "infinity" first (longer match), then "inf"
6497
46.2k
  after = match_iprefix(p, last, "infinity");
6498
46.2k
  if (after) {
6499
6.78k
    *result = negative ? std::numeric_limits<double>::lowest()
6500
6.78k
                       : (std::numeric_limits<double>::max)();
6501
6.78k
    *end_ptr = after;
6502
6.78k
    return true;
6503
6.78k
  }
6504
6505
39.4k
  after = match_iprefix(p, last, "inf");
6506
39.4k
  if (after) {
6507
19.9k
    *result = negative ? std::numeric_limits<double>::lowest()
6508
19.9k
                       : (std::numeric_limits<double>::max)();
6509
19.9k
    *end_ptr = after;
6510
19.9k
    return true;
6511
19.9k
  }
6512
6513
19.5k
  return false;
6514
39.4k
}
6515
6516
}  // namespace detail_fp
6517
6518
// Tries to parse a floating point number located at s.
6519
// Uses fast_float::from_chars for bit-exact, high-performance parsing.
6520
// Handles OBJ quirks: leading '+', nan/inf with replacement values.
6521
//
6522
// s_end should be a location in the string where reading should absolutely
6523
// stop. For example at the end of the string, to prevent buffer overflows.
6524
//
6525
// If the parsing is a success, result is set to the parsed value and true
6526
// is returned.
6527
//
6528
1.94M
static bool tryParseDouble(const char *s, const char *s_end, double *result) {
6529
1.94M
  if (!s || !s_end || !result || s >= s_end) {
6530
37.5k
    return false;
6531
37.5k
  }
6532
6533
  // Check for nan/inf (starts with [nNiI] or [+-] followed by [nNiI])
6534
1.90M
  const char *p = s;
6535
1.90M
  if (p < s_end && (*p == '+' || *p == '-')) ++p;
6536
1.90M
  if (p < s_end) {
6537
1.89M
    char fc = *p;
6538
1.89M
    if (fc >= 'A' && fc <= 'Z') fc += 32;
6539
1.89M
    if (fc == 'n' || fc == 'i') {
6540
49.2k
      const char *end_ptr;
6541
49.2k
      if (detail_fp::tryParseNanInf(s, s_end, result, &end_ptr)) {
6542
29.7k
        return true;
6543
29.7k
      }
6544
49.2k
    }
6545
1.89M
  }
6546
6547
  // Use allow_leading_plus so fast_float handles '+' natively.
6548
1.87M
  double tmp;
6549
1.87M
  auto r = fast_float::from_chars(s, s_end, tmp,
6550
1.87M
      fast_float::chars_format::general |
6551
1.87M
      fast_float::chars_format::allow_leading_plus);
6552
1.87M
  if (r.ec == tinyobj_ff::ff_errc::ok) {
6553
1.72M
    *result = tmp;
6554
1.72M
    return true;
6555
1.72M
  }
6556
  // On error (invalid_argument, result_out_of_range), *result is unchanged.
6557
6558
147k
  return false;
6559
1.87M
}
6560
6561
172k
static inline real_t parseReal(const char **token, double default_value = 0.0) {
6562
172k
  (*token) += strspn((*token), " \t");
6563
172k
  const char *end = (*token) + strcspn((*token), " \t\r");
6564
172k
  double val = default_value;
6565
172k
  tryParseDouble((*token), end, &val);
6566
172k
  real_t f = static_cast<real_t>(val);
6567
172k
  (*token) = end;
6568
172k
  return f;
6569
172k
}
6570
6571
0
static inline bool parseReal(const char **token, real_t *out) {
6572
0
  (*token) += strspn((*token), " \t");
6573
0
  const char *end = (*token) + strcspn((*token), " \t\r");
6574
0
  double val;
6575
0
  bool ret = tryParseDouble((*token), end, &val);
6576
0
  if (ret) {
6577
0
    real_t f = static_cast<real_t>(val);
6578
0
    (*out) = f;
6579
0
  }
6580
0
  (*token) = end;
6581
0
  return ret;
6582
0
}
6583
6584
#else  // TINYOBJLOADER_DISABLE_FAST_FLOAT
6585
6586
// ---- Legacy hand-written float parser (fallback) ----
6587
6588
// Tries to parse a floating point number located at s.
6589
//
6590
// s_end should be a location in the string where reading should absolutely
6591
// stop. For example at the end of the string, to prevent buffer overflows.
6592
//
6593
// Parses the following EBNF grammar:
6594
//   sign    = "+" | "-" ;
6595
//   END     = ? anything not in digit ?
6596
//   digit   = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
6597
//   integer = [sign] , digit , {digit} ;
6598
//   decimal = integer , ["." , integer] ;
6599
//   float   = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ;
6600
//
6601
//  Valid strings are for example:
6602
//   -0  +3.1417e+2  -0.0E-3  1.0324  -1.41   11e2
6603
//
6604
// If the parsing is a success, result is set to the parsed value and true
6605
// is returned.
6606
//
6607
// The function is greedy and will parse until any of the following happens:
6608
//  - a non-conforming character is encountered.
6609
//  - s_end is reached.
6610
//
6611
// The following situations triggers a failure:
6612
//  - s >= s_end.
6613
//  - parse failure.
6614
//
6615
static bool tryParseDouble(const char *s, const char *s_end, double *result) {
6616
  if (s >= s_end) {
6617
    return false;
6618
  }
6619
6620
  double mantissa = 0.0;
6621
  // This exponent is base 2 rather than 10.
6622
  // However the exponent we parse is supposed to be one of ten,
6623
  // thus we must take care to convert the exponent/and or the
6624
  // mantissa to a * 2^E, where a is the mantissa and E is the
6625
  // exponent.
6626
  // To get the final double we will use ldexp, it requires the
6627
  // exponent to be in base 2.
6628
  int exponent = 0;
6629
6630
  // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED
6631
  // TO JUMP OVER DEFINITIONS.
6632
  char sign = '+';
6633
  char exp_sign = '+';
6634
  char const *curr = s;
6635
6636
  // How many characters were read in a loop.
6637
  int read = 0;
6638
  // Tells whether a loop terminated due to reaching s_end.
6639
  bool end_not_reached = false;
6640
  bool leading_decimal_dots = false;
6641
6642
  /*
6643
          BEGIN PARSING.
6644
  */
6645
6646
  // Find out what sign we've got.
6647
  if (*curr == '+' || *curr == '-') {
6648
    sign = *curr;
6649
    curr++;
6650
    if ((curr != s_end) && (*curr == '.')) {
6651
      // accept. Somethig like `.7e+2`, `-.5234`
6652
      leading_decimal_dots = true;
6653
    }
6654
  } else if (IS_DIGIT(*curr)) { /* Pass through. */
6655
  } else if (*curr == '.') {
6656
    // accept. Somethig like `.7e+2`, `-.5234`
6657
    leading_decimal_dots = true;
6658
  } else {
6659
    goto fail;
6660
  }
6661
6662
  // Read the integer part.
6663
  end_not_reached = (curr != s_end);
6664
  if (!leading_decimal_dots) {
6665
    while (end_not_reached && IS_DIGIT(*curr)) {
6666
      mantissa *= 10;
6667
      mantissa += static_cast<int>(*curr - 0x30);
6668
      curr++;
6669
      read++;
6670
      end_not_reached = (curr != s_end);
6671
    }
6672
6673
    // We must make sure we actually got something.
6674
    if (read == 0) goto fail;
6675
  }
6676
6677
  // We allow numbers of form "#", "###" etc.
6678
  if (!end_not_reached) goto assemble;
6679
6680
  // Read the decimal part.
6681
  if (*curr == '.') {
6682
    curr++;
6683
    read = 1;
6684
    end_not_reached = (curr != s_end);
6685
    while (end_not_reached && IS_DIGIT(*curr)) {
6686
      static const double pow_lut[] = {
6687
          1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001,
6688
      };
6689
      const int lut_entries = sizeof pow_lut / sizeof pow_lut[0];
6690
6691
      // NOTE: Don't use powf here, it will absolutely murder precision.
6692
      mantissa += static_cast<int>(*curr - 0x30) *
6693
                  (read < lut_entries ? pow_lut[read] : std::pow(10.0, -read));
6694
      read++;
6695
      curr++;
6696
      end_not_reached = (curr != s_end);
6697
    }
6698
  } else if (*curr == 'e' || *curr == 'E') {
6699
  } else {
6700
    goto assemble;
6701
  }
6702
6703
  if (!end_not_reached) goto assemble;
6704
6705
  // Read the exponent part.
6706
  if (*curr == 'e' || *curr == 'E') {
6707
    curr++;
6708
    // Figure out if a sign is present and if it is.
6709
    end_not_reached = (curr != s_end);
6710
    if (end_not_reached && (*curr == '+' || *curr == '-')) {
6711
      exp_sign = *curr;
6712
      curr++;
6713
    } else if (IS_DIGIT(*curr)) { /* Pass through. */
6714
    } else {
6715
      // Empty E is not allowed.
6716
      goto fail;
6717
    }
6718
6719
    read = 0;
6720
    end_not_reached = (curr != s_end);
6721
    while (end_not_reached && IS_DIGIT(*curr)) {
6722
      // To avoid annoying MSVC's min/max macro definiton,
6723
      // Use hardcoded int max value
6724
      if (exponent >
6725
          ((2147483647 - 9) / 10)) {  // (INT_MAX - 9) / 10, guards both multiply and add
6726
        // Integer overflow
6727
        goto fail;
6728
      }
6729
      exponent *= 10;
6730
      exponent += static_cast<int>(*curr - 0x30);
6731
      curr++;
6732
      read++;
6733
      end_not_reached = (curr != s_end);
6734
    }
6735
    exponent *= (exp_sign == '+' ? 1 : -1);
6736
    if (read == 0) goto fail;
6737
  }
6738
6739
assemble:
6740
  *result = (sign == '+' ? 1 : -1) *
6741
            (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent)
6742
                      : mantissa);
6743
  return true;
6744
fail:
6745
  return false;
6746
}
6747
6748
static inline real_t parseReal(const char **token, double default_value = 0.0) {
6749
  (*token) += strspn((*token), " \t");
6750
  const char *end = (*token) + strcspn((*token), " \t\r");
6751
  double val = default_value;
6752
  tryParseDouble((*token), end, &val);
6753
  real_t f = static_cast<real_t>(val);
6754
  (*token) = end;
6755
  return f;
6756
}
6757
6758
static inline bool parseReal(const char **token, real_t *out) {
6759
  (*token) += strspn((*token), " \t");
6760
  const char *end = (*token) + strcspn((*token), " \t\r");
6761
  double val;
6762
  bool ret = tryParseDouble((*token), end, &val);
6763
  if (ret) {
6764
    real_t f = static_cast<real_t>(val);
6765
    (*out) = f;
6766
  }
6767
  (*token) = end;
6768
  return ret;
6769
}
6770
6771
#endif  // TINYOBJLOADER_DISABLE_FAST_FLOAT
6772
6773
static inline void parseReal2(real_t *x, real_t *y, const char **token,
6774
                              const double default_x = 0.0,
6775
11.1k
                              const double default_y = 0.0) {
6776
11.1k
  (*x) = parseReal(token, default_x);
6777
11.1k
  (*y) = parseReal(token, default_y);
6778
11.1k
}
6779
6780
static inline void parseReal3(real_t *x, real_t *y, real_t *z,
6781
                              const char **token, const double default_x = 0.0,
6782
                              const double default_y = 0.0,
6783
46.2k
                              const double default_z = 0.0) {
6784
46.2k
  (*x) = parseReal(token, default_x);
6785
46.2k
  (*y) = parseReal(token, default_y);
6786
46.2k
  (*z) = parseReal(token, default_z);
6787
46.2k
}
6788
6789
#if 0  // not used
6790
static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w,
6791
                          const char **token, const double default_x = 0.0,
6792
                          const double default_y = 0.0,
6793
                          const double default_z = 0.0,
6794
                          const double default_w = 1.0) {
6795
  (*x) = parseReal(token, default_x);
6796
  (*y) = parseReal(token, default_y);
6797
  (*z) = parseReal(token, default_z);
6798
  (*w) = parseReal(token, default_w);
6799
}
6800
#endif
6801
6802
// Extension: parse vertex with colors(6 items)
6803
// Return 3: xyz, 4: xyzw, 6: xyzrgb
6804
// `r`: red(case 6) or [w](case 4)
6805
// NOTE: This classic path caps at 6 components per the de-facto vertex-color
6806
// extension (xyz / xyzw / xyzrgb; weight and color are mutually exclusive) and
6807
// ignores any further tokens.  LoadObjOpt / the experimental stream loader
6808
// additionally accept a non-standard 7-component `v x y z w r g b` (weight +
6809
// color) form, so they diverge from this function for 7+ token vertices.
6810
static inline int parseVertexWithColor(real_t *x, real_t *y, real_t *z,
6811
                                       real_t *r, real_t *g, real_t *b,
6812
                                       const char **token,
6813
                                       const double default_x = 0.0,
6814
                                       const double default_y = 0.0,
6815
0
                                       const double default_z = 0.0) {
6816
0
  // TODO: Check error
6817
0
  (*x) = parseReal(token, default_x);
6818
0
  (*y) = parseReal(token, default_y);
6819
0
  (*z) = parseReal(token, default_z);
6820
0
6821
0
  // - 4 components(x, y, z, w) ot 6 components
6822
0
  bool has_r = parseReal(token, r);
6823
0
6824
0
  if (!has_r) {
6825
0
    (*r) = (*g) = (*b) = 1.0;
6826
0
    return 3;
6827
0
  }
6828
0
6829
0
  bool has_g = parseReal(token, g);
6830
0
6831
0
  if (!has_g) {
6832
0
    (*g) = (*b) = 1.0;
6833
0
    return 4;
6834
0
  }
6835
0
6836
0
  bool has_b = parseReal(token, b);
6837
0
6838
0
  if (!has_b) {
6839
0
    (*r) = (*g) = (*b) = 1.0;
6840
0
    return 3;  // treated as xyz
6841
0
  }
6842
0
6843
0
  return 6;
6844
0
}
6845
6846
41.8k
static inline bool parseOnOff(const char **token, bool default_value = true) {
6847
41.8k
  (*token) += strspn((*token), " \t");
6848
41.8k
  const char *end = (*token) + strcspn((*token), " \t\r");
6849
6850
41.8k
  bool ret = default_value;
6851
41.8k
  if ((0 == strncmp((*token), "on", 2))) {
6852
12.6k
    ret = true;
6853
29.2k
  } else if ((0 == strncmp((*token), "off", 3))) {
6854
17.0k
    ret = false;
6855
17.0k
  }
6856
6857
41.8k
  (*token) = end;
6858
41.8k
  return ret;
6859
41.8k
}
6860
6861
static inline texture_type_t parseTextureType(
6862
12.3k
    const char **token, texture_type_t default_value = TEXTURE_TYPE_NONE) {
6863
12.3k
  (*token) += strspn((*token), " \t");
6864
12.3k
  const char *end = (*token) + strcspn((*token), " \t\r");
6865
12.3k
  texture_type_t ty = default_value;
6866
6867
12.3k
  if ((0 == strncmp((*token), "cube_top", strlen("cube_top")))) {
6868
2.69k
    ty = TEXTURE_TYPE_CUBE_TOP;
6869
9.65k
  } else if ((0 == strncmp((*token), "cube_bottom", strlen("cube_bottom")))) {
6870
379
    ty = TEXTURE_TYPE_CUBE_BOTTOM;
6871
9.28k
  } else if ((0 == strncmp((*token), "cube_left", strlen("cube_left")))) {
6872
2.48k
    ty = TEXTURE_TYPE_CUBE_LEFT;
6873
6.80k
  } else if ((0 == strncmp((*token), "cube_right", strlen("cube_right")))) {
6874
247
    ty = TEXTURE_TYPE_CUBE_RIGHT;
6875
6.55k
  } else if ((0 == strncmp((*token), "cube_front", strlen("cube_front")))) {
6876
229
    ty = TEXTURE_TYPE_CUBE_FRONT;
6877
6.32k
  } else if ((0 == strncmp((*token), "cube_back", strlen("cube_back")))) {
6878
1.01k
    ty = TEXTURE_TYPE_CUBE_BACK;
6879
5.30k
  } else if ((0 == strncmp((*token), "sphere", strlen("sphere")))) {
6880
354
    ty = TEXTURE_TYPE_SPHERE;
6881
354
  }
6882
6883
12.3k
  (*token) = end;
6884
12.3k
  return ty;
6885
12.3k
}
6886
6887
0
static tag_sizes parseTagTriple(const char **token) {
6888
0
  tag_sizes ts;
6889
0
6890
0
  (*token) += strspn((*token), " \t");
6891
0
  ts.num_ints = atoi((*token));
6892
0
  (*token) += strcspn((*token), "/ \t\r");
6893
0
  if ((*token)[0] != '/') {
6894
0
    return ts;
6895
0
  }
6896
0
6897
0
  (*token)++;  // Skip '/'
6898
0
6899
0
  (*token) += strspn((*token), " \t");
6900
0
  ts.num_reals = atoi((*token));
6901
0
  (*token) += strcspn((*token), "/ \t\r");
6902
0
  if ((*token)[0] != '/') {
6903
0
    return ts;
6904
0
  }
6905
0
  (*token)++;  // Skip '/'
6906
0
6907
0
  ts.num_strings = parseInt(token);
6908
0
6909
0
  return ts;
6910
0
}
6911
6912
// Parse triples with index offsets: i, i/j/k, i//k, i/j
6913
static bool parseTriple(const char **token, int vsize, int vnsize, int vtsize,
6914
0
                        vertex_index_t *ret, const warning_context &context) {
6915
0
  if (!ret) {
6916
0
    return false;
6917
0
  }
6918
0
6919
0
  vertex_index_t vi(-1);
6920
0
6921
0
  if (!fixIndex(atoi((*token)), vsize, &vi.v_idx, false, context)) {
6922
0
    return false;
6923
0
  }
6924
0
6925
0
  (*token) += strcspn((*token), "/ \t\r");
6926
0
  if ((*token)[0] != '/') {
6927
0
    (*ret) = vi;
6928
0
    return true;
6929
0
  }
6930
0
  (*token)++;
6931
0
6932
0
  // i//k
6933
0
  if ((*token)[0] == '/') {
6934
0
    (*token)++;
6935
0
    if (!fixIndex(atoi((*token)), vnsize, &vi.vn_idx, true, context)) {
6936
0
      return false;
6937
0
    }
6938
0
    (*token) += strcspn((*token), "/ \t\r");
6939
0
    (*ret) = vi;
6940
0
    return true;
6941
0
  }
6942
0
6943
0
  // i/j/k or i/j
6944
0
  if (!fixIndex(atoi((*token)), vtsize, &vi.vt_idx, true, context)) {
6945
0
    return false;
6946
0
  }
6947
0
6948
0
  (*token) += strcspn((*token), "/ \t\r");
6949
0
  if ((*token)[0] != '/') {
6950
0
    (*ret) = vi;
6951
0
    return true;
6952
0
  }
6953
0
6954
0
  // i/j/k
6955
0
  (*token)++;  // skip '/'
6956
0
  if (!fixIndex(atoi((*token)), vnsize, &vi.vn_idx, true, context)) {
6957
0
    return false;
6958
0
  }
6959
0
  (*token) += strcspn((*token), "/ \t\r");
6960
0
6961
0
  (*ret) = vi;
6962
0
6963
0
  return true;
6964
0
}
6965
6966
// Parse raw triples: i, i/j/k, i//k, i/j
6967
0
static vertex_index_t parseRawTriple(const char **token) {
6968
0
  vertex_index_t vi(static_cast<int>(0));  // 0 is an invalid index in OBJ
6969
0
6970
0
  vi.v_idx = atoi((*token));
6971
0
  (*token) += strcspn((*token), "/ \t\r");
6972
0
  if ((*token)[0] != '/') {
6973
0
    return vi;
6974
0
  }
6975
0
  (*token)++;
6976
0
6977
0
  // i//k
6978
0
  if ((*token)[0] == '/') {
6979
0
    (*token)++;
6980
0
    vi.vn_idx = atoi((*token));
6981
0
    (*token) += strcspn((*token), "/ \t\r");
6982
0
    return vi;
6983
0
  }
6984
0
6985
0
  // i/j/k or i/j
6986
0
  vi.vt_idx = atoi((*token));
6987
0
  (*token) += strcspn((*token), "/ \t\r");
6988
0
  if ((*token)[0] != '/') {
6989
0
    return vi;
6990
0
  }
6991
0
6992
0
  // i/j/k
6993
0
  (*token)++;  // skip '/'
6994
0
  vi.vn_idx = atoi((*token));
6995
0
  (*token) += strcspn((*token), "/ \t\r");
6996
0
  return vi;
6997
0
}
6998
6999
// --- Stream-based parse functions ---
7000
7001
8.33M
static inline std::string sr_parseString(StreamReader &sr) {
7002
8.33M
  sr.skip_space();
7003
8.33M
  std::string s;
7004
24.1M
  while (!sr.eof()) {
7005
23.9M
    char c = sr.peek();
7006
23.9M
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break;
7007
15.7M
    s += c;
7008
15.7M
    sr.advance(1);
7009
15.7M
  }
7010
8.33M
  return s;
7011
8.33M
}
7012
7013
6.22M
static inline int sr_parseInt(StreamReader &sr) {
7014
6.22M
  sr.skip_space();
7015
6.22M
  const char *start = sr.current_ptr();
7016
6.22M
  size_t rem = sr.remaining();
7017
6.22M
  size_t len = 0;
7018
12.5M
  while (len < rem) {
7019
12.1M
    char c = start[len];
7020
12.1M
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break;
7021
6.32M
    len++;
7022
6.32M
  }
7023
6.22M
  int i = 0;
7024
6.22M
  if (len > 0) {
7025
45.1k
    char tmp[64];
7026
45.1k
    size_t copy_len = len < 63 ? len : 63;
7027
45.1k
    if (copy_len != len) {
7028
3.92k
      sr.advance(len);
7029
3.92k
      return 0;
7030
3.92k
    }
7031
41.2k
    memcpy(tmp, start, copy_len);
7032
41.2k
    tmp[copy_len] = '\0';
7033
41.2k
    errno = 0;
7034
41.2k
    char *endptr = NULL;
7035
41.2k
    long val = strtol(tmp, &endptr, 10);
7036
41.2k
    const bool has_error =
7037
41.2k
        (errno == ERANGE || endptr == tmp ||
7038
23.2k
         val > (std::numeric_limits<int>::max)() ||
7039
22.9k
         val < (std::numeric_limits<int>::min)());
7040
41.2k
    if (!has_error) {
7041
22.9k
      i = static_cast<int>(val);
7042
22.9k
    }
7043
41.2k
  }
7044
6.22M
  sr.advance(len);
7045
6.22M
  return i;
7046
6.22M
}
7047
7048
15.2M
static inline real_t sr_parseReal(StreamReader &sr, double default_value = 0.0) {
7049
15.2M
  sr.skip_space();
7050
15.2M
  const char *start = sr.current_ptr();
7051
15.2M
  size_t rem = sr.remaining();
7052
15.2M
  size_t len = 0;
7053
25.2M
  while (len < rem) {
7054
24.7M
    char c = start[len];
7055
24.7M
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break;
7056
9.98M
    len++;
7057
9.98M
  }
7058
15.2M
  double val = default_value;
7059
15.2M
  if (len > 0) {
7060
1.29M
    tryParseDouble(start, start + len, &val);
7061
1.29M
  }
7062
15.2M
  sr.advance(len);
7063
15.2M
  return static_cast<real_t>(val);
7064
15.2M
}
7065
7066
1.29M
static inline bool sr_parseReal(StreamReader &sr, real_t *out) {
7067
1.29M
  sr.skip_space();
7068
1.29M
  const char *start = sr.current_ptr();
7069
1.29M
  size_t rem = sr.remaining();
7070
1.29M
  size_t len = 0;
7071
2.94M
  while (len < rem) {
7072
2.94M
    char c = start[len];
7073
2.94M
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break;
7074
1.65M
    len++;
7075
1.65M
  }
7076
1.29M
  if (len == 0) return false;
7077
106k
  double val;
7078
106k
  bool ret = tryParseDouble(start, start + len, &val);
7079
106k
  if (ret) {
7080
72.4k
    (*out) = static_cast<real_t>(val);
7081
72.4k
  }
7082
106k
  sr.advance(len);
7083
106k
  return ret;
7084
1.29M
}
7085
7086
static inline void sr_parseReal2(real_t *x, real_t *y, StreamReader &sr,
7087
                                 const double default_x = 0.0,
7088
621k
                                 const double default_y = 0.0) {
7089
621k
  (*x) = sr_parseReal(sr, default_x);
7090
621k
  (*y) = sr_parseReal(sr, default_y);
7091
621k
}
7092
7093
static inline void sr_parseReal3(real_t *x, real_t *y, real_t *z,
7094
                                 StreamReader &sr,
7095
                                 const double default_x = 0.0,
7096
                                 const double default_y = 0.0,
7097
0
                                 const double default_z = 0.0) {
7098
0
  (*x) = sr_parseReal(sr, default_x);
7099
0
  (*y) = sr_parseReal(sr, default_y);
7100
0
  (*z) = sr_parseReal(sr, default_z);
7101
0
}
7102
7103
static inline int sr_parseVertexWithColor(real_t *x, real_t *y, real_t *z,
7104
                                          real_t *r, real_t *g, real_t *b,
7105
                                          StreamReader &sr,
7106
                                          const double default_x = 0.0,
7107
                                          const double default_y = 0.0,
7108
0
                                          const double default_z = 0.0) {
7109
0
  (*x) = sr_parseReal(sr, default_x);
7110
0
  (*y) = sr_parseReal(sr, default_y);
7111
0
  (*z) = sr_parseReal(sr, default_z);
7112
0
7113
0
  bool has_r = sr_parseReal(sr, r);
7114
0
  if (!has_r) {
7115
0
    (*r) = (*g) = (*b) = 1.0;
7116
0
    return 3;
7117
0
  }
7118
0
7119
0
  bool has_g = sr_parseReal(sr, g);
7120
0
  if (!has_g) {
7121
0
    (*g) = (*b) = 1.0;
7122
0
    return 4;
7123
0
  }
7124
0
7125
0
  bool has_b = sr_parseReal(sr, b);
7126
0
  if (!has_b) {
7127
0
    (*r) = (*g) = (*b) = 1.0;
7128
0
    return 3;
7129
0
  }
7130
0
7131
0
  return 6;
7132
0
}
7133
7134
// --- Error-reporting overloads ---
7135
// These overloads push clang-style diagnostics into `err` when parsing fails
7136
// and return false so callers can early-return on unrecoverable parse errors.
7137
// The original signatures are preserved above for backward compatibility.
7138
7139
static inline bool sr_parseInt(StreamReader &sr, int *out, std::string *err,
7140
85.6k
                               const std::string &filename) {
7141
85.6k
  sr.skip_space();
7142
85.6k
  const char *start = sr.current_ptr();
7143
85.6k
  size_t rem = sr.remaining();
7144
85.6k
  size_t len = 0;
7145
1.85M
  while (len < rem) {
7146
1.85M
    char c = start[len];
7147
1.85M
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break;
7148
1.76M
    len++;
7149
1.76M
  }
7150
85.6k
  if (len == 0) {
7151
34
    if (err) {
7152
34
      (*err) += sr.format_error(filename, "expected integer value");
7153
34
    }
7154
34
    *out = 0;
7155
34
    return false;
7156
34
  }
7157
85.6k
  char tmp[64];
7158
85.6k
  size_t copy_len = len < 63 ? len : 63;
7159
85.6k
  memcpy(tmp, start, copy_len);
7160
85.6k
  tmp[copy_len] = '\0';
7161
85.6k
  if (copy_len != len) {
7162
38
    if (err) {
7163
38
      (*err) += sr.format_error(filename, "integer value too long");
7164
38
    }
7165
38
    *out = 0;
7166
38
    sr.advance(len);
7167
38
    return false;
7168
38
  }
7169
85.6k
  errno = 0;
7170
85.6k
  char *endptr = NULL;
7171
85.6k
  long val = strtol(tmp, &endptr, 10);
7172
85.6k
  if (errno == ERANGE || val > (std::numeric_limits<int>::max)() ||
7173
85.5k
      val < (std::numeric_limits<int>::min)()) {
7174
66
    if (err) {
7175
66
      (*err) += sr.format_error(filename,
7176
66
          "integer value out of range, got '" + std::string(tmp) + "'");
7177
66
    }
7178
66
    *out = 0;
7179
66
    sr.advance(len);
7180
66
    return false;
7181
66
  }
7182
85.5k
  if (endptr == tmp || (*endptr != '\0' && *endptr != ' ' && *endptr != '\t')) {
7183
73
    if (err) {
7184
73
      (*err) += sr.format_error(filename,
7185
73
          "expected integer, got '" + std::string(tmp) + "'");
7186
73
    }
7187
73
    *out = 0;
7188
73
    sr.advance(len);
7189
73
    return false;
7190
73
  }
7191
85.4k
  *out = static_cast<int>(val);
7192
85.4k
  sr.advance(len);
7193
85.4k
  return true;
7194
85.5k
}
7195
7196
static inline bool sr_parseReal(StreamReader &sr, real_t *out,
7197
                                 double default_value,
7198
                                 std::string *err,
7199
5.34M
                                 const std::string &filename) {
7200
5.34M
  sr.skip_space();
7201
5.34M
  const char *start = sr.current_ptr();
7202
5.34M
  size_t rem = sr.remaining();
7203
5.34M
  size_t len = 0;
7204
20.3M
  while (len < rem) {
7205
20.3M
    char c = start[len];
7206
20.3M
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0') break;
7207
15.0M
    len++;
7208
15.0M
  }
7209
5.34M
  if (len == 0) {
7210
    // No token to parse — not necessarily an error (e.g. optional component).
7211
4.97M
    *out = static_cast<real_t>(default_value);
7212
4.97M
    return true;
7213
4.97M
  }
7214
367k
  double val;
7215
367k
  if (!tryParseDouble(start, start + len, &val)) {
7216
610
    if (err) {
7217
610
      char tmp[64];
7218
610
      size_t copy_len = len < 63 ? len : 63;
7219
610
      memcpy(tmp, start, copy_len);
7220
610
      tmp[copy_len] = '\0';
7221
610
      (*err) += sr.format_error(filename,
7222
610
          "expected number, got '" + std::string(tmp) + "'");
7223
610
    }
7224
610
    *out = static_cast<real_t>(default_value);
7225
610
    sr.advance(len);
7226
610
    return false;
7227
610
  }
7228
366k
  *out = static_cast<real_t>(val);
7229
366k
  sr.advance(len);
7230
366k
  return true;
7231
367k
}
7232
7233
static inline bool sr_parseReal2(real_t *x, real_t *y, StreamReader &sr,
7234
                                  std::string *err,
7235
                                  const std::string &filename,
7236
                                  const double default_x = 0.0,
7237
359k
                                  const double default_y = 0.0) {
7238
359k
  if (!sr_parseReal(sr, x, default_x, err, filename)) return false;
7239
359k
  if (!sr_parseReal(sr, y, default_y, err, filename)) return false;
7240
359k
  return true;
7241
359k
}
7242
7243
static inline bool sr_parseReal3(real_t *x, real_t *y, real_t *z,
7244
                                  StreamReader &sr,
7245
                                  std::string *err,
7246
                                  const std::string &filename,
7247
                                  const double default_x = 0.0,
7248
                                  const double default_y = 0.0,
7249
614k
                                  const double default_z = 0.0) {
7250
614k
  if (!sr_parseReal(sr, x, default_x, err, filename)) return false;
7251
614k
  if (!sr_parseReal(sr, y, default_y, err, filename)) return false;
7252
614k
  if (!sr_parseReal(sr, z, default_z, err, filename)) return false;
7253
614k
  return true;
7254
614k
}
7255
7256
// Returns number of components parsed (3, 4, or 6) on success, -1 on error.
7257
static inline int sr_parseVertexWithColor(real_t *x, real_t *y, real_t *z,
7258
                                          real_t *r, real_t *g, real_t *b,
7259
                                          StreamReader &sr,
7260
                                          std::string *err,
7261
                                          const std::string &filename,
7262
                                          const double default_x = 0.0,
7263
                                          const double default_y = 0.0,
7264
888k
                                          const double default_z = 0.0) {
7265
888k
  if (!sr_parseReal(sr, x, default_x, err, filename)) return -1;
7266
888k
  if (!sr_parseReal(sr, y, default_y, err, filename)) return -1;
7267
888k
  if (!sr_parseReal(sr, z, default_z, err, filename)) return -1;
7268
7269
888k
  bool has_r = sr_parseReal(sr, r);
7270
888k
  if (!has_r) {
7271
859k
    (*r) = (*g) = (*b) = 1.0;
7272
859k
    return 3;
7273
859k
  }
7274
7275
29.1k
  bool has_g = sr_parseReal(sr, g);
7276
29.1k
  if (!has_g) {
7277
10.4k
    (*g) = (*b) = 1.0;
7278
10.4k
    return 4;
7279
10.4k
  }
7280
7281
18.7k
  bool has_b = sr_parseReal(sr, b);
7282
18.7k
  if (!has_b) {
7283
9.13k
    (*r) = (*g) = (*b) = 1.0;
7284
9.13k
    return 3;
7285
9.13k
  }
7286
7287
9.60k
  return 6;
7288
18.7k
}
7289
7290
static inline int sr_parseIntNoSkip(StreamReader &sr);
7291
7292
// Advance past remaining characters in a tag triple field (stops at '/', whitespace, or line end).
7293
988k
static inline void sr_skipTagField(StreamReader &sr) {
7294
2.21M
  while (!sr.eof() && !sr.at_line_end() && !IS_SPACE(sr.peek()) &&
7295
1.32M
         sr.peek() != '/') {
7296
1.22M
    sr.advance(1);
7297
1.22M
  }
7298
988k
}
7299
7300
903k
static tag_sizes sr_parseTagTriple(StreamReader &sr) {
7301
903k
  tag_sizes ts;
7302
7303
903k
  sr.skip_space();
7304
903k
  ts.num_ints = sr_parseIntNoSkip(sr);
7305
903k
  sr_skipTagField(sr);
7306
903k
  if (!sr.eof() && sr.peek() == '/') {
7307
85.5k
    sr.advance(1);
7308
85.5k
    sr.skip_space();
7309
85.5k
    ts.num_reals = sr_parseIntNoSkip(sr);
7310
85.5k
    sr_skipTagField(sr);
7311
85.5k
    if (!sr.eof() && sr.peek() == '/') {
7312
11.9k
      sr.advance(1);
7313
11.9k
      ts.num_strings = sr_parseInt(sr);
7314
11.9k
    }
7315
85.5k
  }
7316
903k
  return ts;
7317
903k
}
7318
7319
5.98M
static inline int sr_parseIntNoSkip(StreamReader &sr) {
7320
5.98M
  const char *start = sr.current_ptr();
7321
5.98M
  size_t rem = sr.remaining();
7322
5.98M
  size_t len = 0;
7323
5.98M
  if (len < rem && (start[len] == '+' || start[len] == '-')) len++;
7324
11.9M
  while (len < rem && start[len] >= '0' && start[len] <= '9') len++;
7325
5.98M
  int i = 0;
7326
5.98M
  if (len > 0) {
7327
5.13M
    char tmp[64];
7328
5.13M
    size_t copy_len = len < 63 ? len : 63;
7329
5.13M
    if (copy_len != len) {
7330
1.27k
      sr.advance(len);
7331
1.27k
      return 0;
7332
1.27k
    }
7333
5.13M
    memcpy(tmp, start, copy_len);
7334
5.13M
    tmp[copy_len] = '\0';
7335
5.13M
    errno = 0;
7336
5.13M
    char *endptr = NULL;
7337
5.13M
    long val = strtol(tmp, &endptr, 10);
7338
5.13M
    if (errno == 0 && endptr != tmp && *endptr == '\0' &&
7339
5.12M
        val <= (std::numeric_limits<int>::max)() &&
7340
5.11M
        val >= (std::numeric_limits<int>::min)()) {
7341
5.11M
      i = static_cast<int>(val);
7342
5.11M
    }
7343
5.13M
  }
7344
5.98M
  sr.advance(len);
7345
5.98M
  return i;
7346
5.98M
}
7347
7348
4.99M
static inline void sr_skipUntil(StreamReader &sr, const char *delims) {
7349
7.38M
  while (!sr.eof()) {
7350
7.38M
    char c = sr.peek();
7351
29.7M
    for (const char *d = delims; *d; d++) {
7352
27.3M
      if (c == *d) return;
7353
27.3M
    }
7354
2.39M
    sr.advance(1);
7355
2.39M
  }
7356
4.99M
}
7357
7358
static bool sr_parseTriple(StreamReader &sr, int vsize, int vnsize, int vtsize,
7359
4.97M
                           vertex_index_t *ret, const warning_context &context) {
7360
4.97M
  if (!ret) return false;
7361
7362
4.97M
  vertex_index_t vi(-1);
7363
7364
4.97M
  sr.skip_space();
7365
4.97M
  if (!fixIndex(sr_parseIntNoSkip(sr), vsize, &vi.v_idx, false, context)) {
7366
433
    return false;
7367
433
  }
7368
7369
4.97M
  sr_skipUntil(sr, "/ \t\r\n");
7370
4.97M
  if (sr.eof() || sr.peek() != '/') {
7371
4.95M
    (*ret) = vi;
7372
4.95M
    return true;
7373
4.95M
  }
7374
20.2k
  sr.advance(1);
7375
7376
  // i//k
7377
20.2k
  if (!sr.eof() && sr.peek() == '/') {
7378
9.22k
    sr.advance(1);
7379
9.22k
    if (!fixIndex(sr_parseIntNoSkip(sr), vnsize, &vi.vn_idx, true, context)) {
7380
2
      return false;
7381
2
    }
7382
9.22k
    sr_skipUntil(sr, "/ \t\r\n");
7383
9.22k
    (*ret) = vi;
7384
9.22k
    return true;
7385
9.22k
  }
7386
7387
  // i/j/k or i/j
7388
11.0k
  if (!fixIndex(sr_parseIntNoSkip(sr), vtsize, &vi.vt_idx, true, context)) {
7389
1
    return false;
7390
1
  }
7391
7392
11.0k
  sr_skipUntil(sr, "/ \t\r\n");
7393
11.0k
  if (sr.eof() || sr.peek() != '/') {
7394
9.92k
    (*ret) = vi;
7395
9.92k
    return true;
7396
9.92k
  }
7397
7398
  // i/j/k
7399
1.10k
  sr.advance(1);
7400
1.10k
  if (!fixIndex(sr_parseIntNoSkip(sr), vnsize, &vi.vn_idx, true, context)) {
7401
1
    return false;
7402
1
  }
7403
1.10k
  sr_skipUntil(sr, "/ \t\r\n");
7404
7405
1.10k
  (*ret) = vi;
7406
1.10k
  return true;
7407
1.10k
}
7408
7409
0
static vertex_index_t sr_parseRawTriple(StreamReader &sr) {
7410
0
  vertex_index_t vi(static_cast<int>(0));
7411
7412
0
  sr.skip_space();
7413
0
  vi.v_idx = sr_parseIntNoSkip(sr);
7414
0
  sr_skipUntil(sr, "/ \t\r\n");
7415
0
  if (sr.eof() || sr.peek() != '/') return vi;
7416
0
  sr.advance(1);
7417
7418
  // i//k
7419
0
  if (!sr.eof() && sr.peek() == '/') {
7420
0
    sr.advance(1);
7421
0
    vi.vn_idx = sr_parseIntNoSkip(sr);
7422
0
    sr_skipUntil(sr, "/ \t\r\n");
7423
0
    return vi;
7424
0
  }
7425
7426
  // i/j/k or i/j
7427
0
  vi.vt_idx = sr_parseIntNoSkip(sr);
7428
0
  sr_skipUntil(sr, "/ \t\r\n");
7429
0
  if (sr.eof() || sr.peek() != '/') return vi;
7430
7431
0
  sr.advance(1);
7432
0
  vi.vn_idx = sr_parseIntNoSkip(sr);
7433
0
  sr_skipUntil(sr, "/ \t\r\n");
7434
0
  return vi;
7435
0
}
7436
7437
bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt,
7438
370k
                               const char *linebuf) {
7439
  // @todo { write more robust lexer and parser. }
7440
370k
  bool found_texname = false;
7441
370k
  std::string texture_name;
7442
7443
370k
  const char *token = linebuf;  // Assume line ends with NULL
7444
7445
805k
  while (!IS_NEW_LINE((*token))) {
7446
434k
    token += strspn(token, " \t");  // skip space
7447
434k
    if ((0 == strncmp(token, "-blendu", 7)) && IS_SPACE((token[7]))) {
7448
3.88k
      token += 8;
7449
3.88k
      texopt->blendu = parseOnOff(&token, /* default */ true);
7450
430k
    } else if ((0 == strncmp(token, "-blendv", 7)) && IS_SPACE((token[7]))) {
7451
22.3k
      token += 8;
7452
22.3k
      texopt->blendv = parseOnOff(&token, /* default */ true);
7453
408k
    } else if ((0 == strncmp(token, "-clamp", 6)) && IS_SPACE((token[6]))) {
7454
15.6k
      token += 7;
7455
15.6k
      texopt->clamp = parseOnOff(&token, /* default */ true);
7456
392k
    } else if ((0 == strncmp(token, "-boost", 6)) && IS_SPACE((token[6]))) {
7457
11.0k
      token += 7;
7458
11.0k
      texopt->sharpness = parseReal(&token, 1.0);
7459
381k
    } else if ((0 == strncmp(token, "-bm", 3)) && IS_SPACE((token[3]))) {
7460
254
      token += 4;
7461
254
      texopt->bump_multiplier = parseReal(&token, 1.0);
7462
381k
    } else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) {
7463
21.9k
      token += 3;
7464
21.9k
      parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]),
7465
21.9k
                 &(texopt->origin_offset[2]), &token);
7466
359k
    } else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) {
7467
3.08k
      token += 3;
7468
3.08k
      parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]),
7469
3.08k
                 &token, 1.0, 1.0, 1.0);
7470
356k
    } else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) {
7471
21.2k
      token += 3;
7472
21.2k
      parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]),
7473
21.2k
                 &(texopt->turbulence[2]), &token);
7474
335k
    } else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) {
7475
12.3k
      token += 5;
7476
12.3k
      texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE);
7477
322k
    } else if ((0 == strncmp(token, "-texres", 7)) && IS_SPACE((token[7]))) {
7478
1.83k
      token += 7;
7479
      // TODO(syoyo): Check if arg is int type.
7480
1.83k
      texopt->texture_resolution = parseInt(&token);
7481
321k
    } else if ((0 == strncmp(token, "-imfchan", 8)) && IS_SPACE((token[8]))) {
7482
6.73k
      token += 9;
7483
6.73k
      token += strspn(token, " \t");
7484
6.73k
      const char *end = token + strcspn(token, " \t\r");
7485
6.73k
      if ((end - token) == 1) {  // Assume one char for -imfchan
7486
5.79k
        texopt->imfchan = (*token);
7487
5.79k
      }
7488
6.73k
      token = end;
7489
314k
    } else if ((0 == strncmp(token, "-mm", 3)) && IS_SPACE((token[3]))) {
7490
11.1k
      token += 4;
7491
11.1k
      parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0);
7492
303k
    } else if ((0 == strncmp(token, "-colorspace", 11)) &&
7493
4.64k
               IS_SPACE((token[11]))) {
7494
4.00k
      token += 12;
7495
4.00k
      texopt->colorspace = parseString(&token);
7496
299k
    } else {
7497
// Assume texture filename
7498
#if 0
7499
      size_t len = strcspn(token, " \t\r");  // untile next space
7500
      texture_name = std::string(token, token + len);
7501
      token += len;
7502
7503
      token += strspn(token, " \t");  // skip space
7504
#else
7505
      // Read filename until line end to parse filename containing whitespace
7506
      // TODO(syoyo): Support parsing texture option flag after the filename.
7507
299k
      texture_name = std::string(token);
7508
299k
      token += texture_name.length();
7509
299k
#endif
7510
7511
299k
      found_texname = true;
7512
299k
    }
7513
434k
  }
7514
7515
370k
  if (found_texname) {
7516
299k
    (*texname) = texture_name;
7517
299k
    return true;
7518
299k
  } else {
7519
71.6k
    return false;
7520
71.6k
  }
7521
370k
}
7522
7523
6.40M
static void InitTexOpt(texture_option_t *texopt, const bool is_bump) {
7524
6.40M
  if (is_bump) {
7525
492k
    texopt->imfchan = 'l';
7526
5.91M
  } else {
7527
5.91M
    texopt->imfchan = 'm';
7528
5.91M
  }
7529
6.40M
  texopt->bump_multiplier = static_cast<real_t>(1.0);
7530
6.40M
  texopt->clamp = false;
7531
6.40M
  texopt->blendu = true;
7532
6.40M
  texopt->blendv = true;
7533
6.40M
  texopt->sharpness = static_cast<real_t>(1.0);
7534
6.40M
  texopt->brightness = static_cast<real_t>(0.0);
7535
6.40M
  texopt->contrast = static_cast<real_t>(1.0);
7536
6.40M
  texopt->origin_offset[0] = static_cast<real_t>(0.0);
7537
6.40M
  texopt->origin_offset[1] = static_cast<real_t>(0.0);
7538
6.40M
  texopt->origin_offset[2] = static_cast<real_t>(0.0);
7539
6.40M
  texopt->scale[0] = static_cast<real_t>(1.0);
7540
6.40M
  texopt->scale[1] = static_cast<real_t>(1.0);
7541
6.40M
  texopt->scale[2] = static_cast<real_t>(1.0);
7542
6.40M
  texopt->turbulence[0] = static_cast<real_t>(0.0);
7543
6.40M
  texopt->turbulence[1] = static_cast<real_t>(0.0);
7544
6.40M
  texopt->turbulence[2] = static_cast<real_t>(0.0);
7545
6.40M
  texopt->texture_resolution = -1;
7546
6.40M
  texopt->type = TEXTURE_TYPE_NONE;
7547
6.40M
}
7548
7549
492k
static void InitMaterial(material_t *material) {
7550
492k
  InitTexOpt(&material->ambient_texopt, /* is_bump */ false);
7551
492k
  InitTexOpt(&material->diffuse_texopt, /* is_bump */ false);
7552
492k
  InitTexOpt(&material->specular_texopt, /* is_bump */ false);
7553
492k
  InitTexOpt(&material->specular_highlight_texopt, /* is_bump */ false);
7554
492k
  InitTexOpt(&material->bump_texopt, /* is_bump */ true);
7555
492k
  InitTexOpt(&material->displacement_texopt, /* is_bump */ false);
7556
492k
  InitTexOpt(&material->alpha_texopt, /* is_bump */ false);
7557
492k
  InitTexOpt(&material->reflection_texopt, /* is_bump */ false);
7558
492k
  InitTexOpt(&material->roughness_texopt, /* is_bump */ false);
7559
492k
  InitTexOpt(&material->metallic_texopt, /* is_bump */ false);
7560
492k
  InitTexOpt(&material->sheen_texopt, /* is_bump */ false);
7561
492k
  InitTexOpt(&material->emissive_texopt, /* is_bump */ false);
7562
492k
  InitTexOpt(&material->normal_texopt,
7563
492k
             /* is_bump */ false);  // @fixme { is_bump will be true? }
7564
492k
  material->name = "";
7565
492k
  material->ambient_texname = "";
7566
492k
  material->diffuse_texname = "";
7567
492k
  material->specular_texname = "";
7568
492k
  material->specular_highlight_texname = "";
7569
492k
  material->bump_texname = "";
7570
492k
  material->displacement_texname = "";
7571
492k
  material->reflection_texname = "";
7572
492k
  material->alpha_texname = "";
7573
1.97M
  for (int i = 0; i < 3; i++) {
7574
1.47M
    material->ambient[i] = static_cast<real_t>(0.0);
7575
1.47M
    material->diffuse[i] = static_cast<real_t>(0.0);
7576
1.47M
    material->specular[i] = static_cast<real_t>(0.0);
7577
1.47M
    material->transmittance[i] = static_cast<real_t>(0.0);
7578
1.47M
    material->emission[i] = static_cast<real_t>(0.0);
7579
1.47M
  }
7580
492k
  material->illum = 0;
7581
492k
  material->dissolve = static_cast<real_t>(1.0);
7582
492k
  material->shininess = static_cast<real_t>(1.0);
7583
492k
  material->ior = static_cast<real_t>(1.0);
7584
7585
492k
  material->roughness = static_cast<real_t>(0.0);
7586
492k
  material->metallic = static_cast<real_t>(0.0);
7587
492k
  material->sheen = static_cast<real_t>(0.0);
7588
492k
  material->clearcoat_thickness = static_cast<real_t>(0.0);
7589
492k
  material->clearcoat_roughness = static_cast<real_t>(0.0);
7590
492k
  material->anisotropy_rotation = static_cast<real_t>(0.0);
7591
492k
  material->anisotropy = static_cast<real_t>(0.0);
7592
492k
  material->roughness_texname = "";
7593
492k
  material->metallic_texname = "";
7594
492k
  material->sheen_texname = "";
7595
492k
  material->emissive_texname = "";
7596
492k
  material->normal_texname = "";
7597
7598
492k
  material->unknown_parameter.clear();
7599
492k
}
7600
7601
// code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
7602
template <typename T>
7603
176M
static int pnpoly(int nvert, T *vertx, T *verty, T testx, T testy) {
7604
176M
  int i, j, c = 0;
7605
707M
  for (i = 0, j = nvert - 1; i < nvert; j = i++) {
7606
530M
    if (((verty[i] > testy) != (verty[j] > testy)) &&
7607
42.9k
        (testx <
7608
42.9k
         (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) +
7609
42.9k
             vertx[i]))
7610
8.32k
      c = !c;
7611
530M
  }
7612
176M
  return c;
7613
176M
}
7614
7615
struct TinyObjPoint {
7616
  real_t x, y, z;
7617
0
  TinyObjPoint() : x(0), y(0), z(0) {}
7618
0
  TinyObjPoint(real_t x_, real_t y_, real_t z_) : x(x_), y(y_), z(z_) {}
7619
};
7620
7621
0
inline TinyObjPoint cross(const TinyObjPoint &v1, const TinyObjPoint &v2) {
7622
0
  return TinyObjPoint(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z,
7623
0
                      v1.x * v2.y - v1.y * v2.x);
7624
0
}
7625
7626
0
inline real_t dot(const TinyObjPoint &v1, const TinyObjPoint &v2) {
7627
0
  return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z);
7628
0
}
7629
7630
0
inline real_t GetLength(TinyObjPoint &e) {
7631
0
  return std::sqrt(e.x * e.x + e.y * e.y + e.z * e.z);
7632
0
}
7633
7634
0
inline TinyObjPoint Normalize(TinyObjPoint e) {
7635
0
  real_t len = GetLength(e);
7636
0
  if (len <= real_t(0)) return TinyObjPoint(real_t(0), real_t(0), real_t(0));
7637
0
  real_t inv_length = real_t(1) / len;
7638
0
  return TinyObjPoint(e.x * inv_length, e.y * inv_length, e.z * inv_length);
7639
0
}
7640
7641
inline TinyObjPoint WorldToLocal(const TinyObjPoint &a, const TinyObjPoint &u,
7642
0
                                 const TinyObjPoint &v, const TinyObjPoint &w) {
7643
0
  return TinyObjPoint(dot(a, u), dot(a, v), dot(a, w));
7644
0
}
7645
7646
// TODO(syoyo): refactor function.
7647
static bool exportGroupsToShape(shape_t *shape, const PrimGroup &prim_group,
7648
                                const std::vector<tag_t> &tags,
7649
                                const int material_id, const std::string &name,
7650
                                bool triangulate, const std::vector<real_t> &v,
7651
484k
                                std::string *warn) {
7652
484k
  if (prim_group.IsEmpty()) {
7653
243k
    return false;
7654
243k
  }
7655
7656
241k
  shape->name = name;
7657
7658
  // polygon
7659
241k
  if (!prim_group.faceGroup.empty()) {
7660
    // Flatten vertices and indices
7661
350k
    for (size_t i = 0; i < prim_group.faceGroup.size(); i++) {
7662
312k
      const face_t &face = prim_group.faceGroup[i];
7663
7664
312k
      size_t npolys = face.vertex_indices.size();
7665
7666
312k
      if (npolys < 3) {
7667
        // Face must have 3+ vertices.
7668
257k
        if (warn) {
7669
257k
          (*warn) += "Degenerated face found\n.";
7670
257k
        }
7671
257k
        continue;
7672
257k
      }
7673
7674
54.6k
      if (triangulate && npolys != 3) {
7675
43.0k
        if (npolys == 4) {
7676
22.4k
          vertex_index_t i0 = face.vertex_indices[0];
7677
22.4k
          vertex_index_t i1 = face.vertex_indices[1];
7678
22.4k
          vertex_index_t i2 = face.vertex_indices[2];
7679
22.4k
          vertex_index_t i3 = face.vertex_indices[3];
7680
7681
22.4k
          if (i0.v_idx < 0 || i1.v_idx < 0 || i2.v_idx < 0 || i3.v_idx < 0) {
7682
0
            if (warn) {
7683
0
              (*warn) += "Face with invalid vertex index found.\n";
7684
0
            }
7685
0
            continue;
7686
0
          }
7687
7688
22.4k
          size_t vi0 = size_t(i0.v_idx);
7689
22.4k
          size_t vi1 = size_t(i1.v_idx);
7690
22.4k
          size_t vi2 = size_t(i2.v_idx);
7691
22.4k
          size_t vi3 = size_t(i3.v_idx);
7692
7693
22.4k
          if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) ||
7694
18.0k
              ((3 * vi2 + 2) >= v.size()) || ((3 * vi3 + 2) >= v.size())) {
7695
            // Invalid triangle.
7696
            // FIXME(syoyo): Is it ok to simply skip this invalid triangle?
7697
6.05k
            if (warn) {
7698
6.05k
              (*warn) += "Face with invalid vertex index found.\n";
7699
6.05k
            }
7700
6.05k
            continue;
7701
6.05k
          }
7702
7703
16.4k
          real_t v0x = v[vi0 * 3 + 0];
7704
16.4k
          real_t v0y = v[vi0 * 3 + 1];
7705
16.4k
          real_t v0z = v[vi0 * 3 + 2];
7706
16.4k
          real_t v1x = v[vi1 * 3 + 0];
7707
16.4k
          real_t v1y = v[vi1 * 3 + 1];
7708
16.4k
          real_t v1z = v[vi1 * 3 + 2];
7709
16.4k
          real_t v2x = v[vi2 * 3 + 0];
7710
16.4k
          real_t v2y = v[vi2 * 3 + 1];
7711
16.4k
          real_t v2z = v[vi2 * 3 + 2];
7712
16.4k
          real_t v3x = v[vi3 * 3 + 0];
7713
16.4k
          real_t v3y = v[vi3 * 3 + 1];
7714
16.4k
          real_t v3z = v[vi3 * 3 + 2];
7715
7716
          // There are two candidates to split the quad into two triangles.
7717
          //
7718
          // Choose the shortest edge.
7719
          // TODO: Is it better to determine the edge to split by calculating
7720
          // the area of each triangle?
7721
          //
7722
          // +---+
7723
          // |\  |
7724
          // | \ |
7725
          // |  \|
7726
          // +---+
7727
          //
7728
          // +---+
7729
          // |  /|
7730
          // | / |
7731
          // |/  |
7732
          // +---+
7733
7734
16.4k
          real_t e02x = v2x - v0x;
7735
16.4k
          real_t e02y = v2y - v0y;
7736
16.4k
          real_t e02z = v2z - v0z;
7737
16.4k
          real_t e13x = v3x - v1x;
7738
16.4k
          real_t e13y = v3y - v1y;
7739
16.4k
          real_t e13z = v3z - v1z;
7740
7741
16.4k
          real_t sqr02 = e02x * e02x + e02y * e02y + e02z * e02z;
7742
16.4k
          real_t sqr13 = e13x * e13x + e13y * e13y + e13z * e13z;
7743
7744
16.4k
          index_t idx0, idx1, idx2, idx3;
7745
7746
16.4k
          idx0.vertex_index = i0.v_idx;
7747
16.4k
          idx0.normal_index = i0.vn_idx;
7748
16.4k
          idx0.texcoord_index = i0.vt_idx;
7749
16.4k
          idx1.vertex_index = i1.v_idx;
7750
16.4k
          idx1.normal_index = i1.vn_idx;
7751
16.4k
          idx1.texcoord_index = i1.vt_idx;
7752
16.4k
          idx2.vertex_index = i2.v_idx;
7753
16.4k
          idx2.normal_index = i2.vn_idx;
7754
16.4k
          idx2.texcoord_index = i2.vt_idx;
7755
16.4k
          idx3.vertex_index = i3.v_idx;
7756
16.4k
          idx3.normal_index = i3.vn_idx;
7757
16.4k
          idx3.texcoord_index = i3.vt_idx;
7758
7759
16.4k
          if (sqr02 < sqr13) {
7760
            // [0, 1, 2], [0, 2, 3]
7761
2.01k
            shape->mesh.indices.push_back(idx0);
7762
2.01k
            shape->mesh.indices.push_back(idx1);
7763
2.01k
            shape->mesh.indices.push_back(idx2);
7764
7765
2.01k
            shape->mesh.indices.push_back(idx0);
7766
2.01k
            shape->mesh.indices.push_back(idx2);
7767
2.01k
            shape->mesh.indices.push_back(idx3);
7768
14.4k
          } else {
7769
            // [0, 1, 3], [1, 2, 3]
7770
14.4k
            shape->mesh.indices.push_back(idx0);
7771
14.4k
            shape->mesh.indices.push_back(idx1);
7772
14.4k
            shape->mesh.indices.push_back(idx3);
7773
7774
14.4k
            shape->mesh.indices.push_back(idx1);
7775
14.4k
            shape->mesh.indices.push_back(idx2);
7776
14.4k
            shape->mesh.indices.push_back(idx3);
7777
14.4k
          }
7778
7779
          // Two triangle faces
7780
16.4k
          shape->mesh.num_face_vertices.push_back(3);
7781
16.4k
          shape->mesh.num_face_vertices.push_back(3);
7782
7783
16.4k
          shape->mesh.material_ids.push_back(material_id);
7784
16.4k
          shape->mesh.material_ids.push_back(material_id);
7785
7786
16.4k
          shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id);
7787
16.4k
          shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id);
7788
7789
20.5k
        } else {
7790
#ifdef TINYOBJLOADER_USE_MAPBOX_EARCUT
7791
          // Validate all vertex indices before accessing the vertex array.
7792
          {
7793
            bool valid_poly = true;
7794
            for (size_t k = 0; k < npolys; ++k) {
7795
              size_t vi = size_t(face.vertex_indices[k].v_idx);
7796
              if ((3 * vi + 2) >= v.size()) {
7797
                valid_poly = false;
7798
                break;
7799
              }
7800
            }
7801
            if (!valid_poly) {
7802
              if (warn) {
7803
                (*warn) += "Face with invalid vertex index found.\n";
7804
              }
7805
              continue;
7806
            }
7807
          }
7808
7809
          vertex_index_t i0 = face.vertex_indices[0];
7810
          vertex_index_t i0_2 = i0;
7811
7812
          // TMW change: Find the normal axis of the polygon using Newell's
7813
          // method
7814
          TinyObjPoint n;
7815
          for (size_t k = 0; k < npolys; ++k) {
7816
            i0 = face.vertex_indices[k % npolys];
7817
            size_t vi0 = size_t(i0.v_idx);
7818
7819
            size_t j = (k + 1) % npolys;
7820
            i0_2 = face.vertex_indices[j];
7821
            size_t vi0_2 = size_t(i0_2.v_idx);
7822
7823
            real_t v0x = v[vi0 * 3 + 0];
7824
            real_t v0y = v[vi0 * 3 + 1];
7825
            real_t v0z = v[vi0 * 3 + 2];
7826
7827
            real_t v0x_2 = v[vi0_2 * 3 + 0];
7828
            real_t v0y_2 = v[vi0_2 * 3 + 1];
7829
            real_t v0z_2 = v[vi0_2 * 3 + 2];
7830
7831
            const TinyObjPoint point1(v0x, v0y, v0z);
7832
            const TinyObjPoint point2(v0x_2, v0y_2, v0z_2);
7833
7834
            TinyObjPoint a(point1.x - point2.x, point1.y - point2.y,
7835
                           point1.z - point2.z);
7836
            TinyObjPoint b(point1.x + point2.x, point1.y + point2.y,
7837
                           point1.z + point2.z);
7838
7839
            n.x += (a.y * b.z);
7840
            n.y += (a.z * b.x);
7841
            n.z += (a.x * b.y);
7842
          }
7843
          real_t length_n = GetLength(n);
7844
          // Check if zero length normal
7845
          if (length_n <= 0) {
7846
            continue;
7847
          }
7848
          // Negative is to flip the normal to the correct direction
7849
          real_t inv_length = -real_t(1.0) / length_n;
7850
          n.x *= inv_length;
7851
          n.y *= inv_length;
7852
          n.z *= inv_length;
7853
7854
          TinyObjPoint axis_w, axis_v, axis_u;
7855
          axis_w = n;
7856
          TinyObjPoint a;
7857
          if (std::fabs(axis_w.x) > real_t(0.9999999)) {
7858
            a = TinyObjPoint(0, 1, 0);
7859
          } else {
7860
            a = TinyObjPoint(1, 0, 0);
7861
          }
7862
          axis_v = Normalize(cross(axis_w, a));
7863
          axis_u = cross(axis_w, axis_v);
7864
          using Point = std::array<real_t, 2>;
7865
7866
          // first polyline define the main polygon.
7867
          // following polylines define holes(not used in tinyobj).
7868
          std::vector<std::vector<Point> > polygon;
7869
7870
          std::vector<Point> polyline;
7871
7872
          // TMW change: Find best normal and project v0x and v0y to those
7873
          // coordinates, instead of picking a plane aligned with an axis (which
7874
          // can flip polygons).
7875
7876
          // Fill polygon data(facevarying vertices).
7877
          for (size_t k = 0; k < npolys; k++) {
7878
            i0 = face.vertex_indices[k];
7879
            size_t vi0 = size_t(i0.v_idx);
7880
7881
            assert(((3 * vi0 + 2) < v.size()));
7882
7883
            real_t v0x = v[vi0 * 3 + 0];
7884
            real_t v0y = v[vi0 * 3 + 1];
7885
            real_t v0z = v[vi0 * 3 + 2];
7886
7887
            TinyObjPoint polypoint(v0x, v0y, v0z);
7888
            TinyObjPoint loc = WorldToLocal(polypoint, axis_u, axis_v, axis_w);
7889
7890
            polyline.push_back({loc.x, loc.y});
7891
          }
7892
7893
          polygon.push_back(polyline);
7894
          std::vector<uint32_t> indices = mapbox::earcut<uint32_t>(polygon);
7895
          // => result = 3 * faces, clockwise
7896
7897
          assert(indices.size() % 3 == 0);
7898
7899
          // Reconstruct vertex_index_t
7900
          for (size_t k = 0; k < indices.size() / 3; k++) {
7901
            {
7902
              index_t idx0, idx1, idx2;
7903
              idx0.vertex_index = face.vertex_indices[indices[3 * k + 0]].v_idx;
7904
              idx0.normal_index =
7905
                  face.vertex_indices[indices[3 * k + 0]].vn_idx;
7906
              idx0.texcoord_index =
7907
                  face.vertex_indices[indices[3 * k + 0]].vt_idx;
7908
              idx1.vertex_index = face.vertex_indices[indices[3 * k + 1]].v_idx;
7909
              idx1.normal_index =
7910
                  face.vertex_indices[indices[3 * k + 1]].vn_idx;
7911
              idx1.texcoord_index =
7912
                  face.vertex_indices[indices[3 * k + 1]].vt_idx;
7913
              idx2.vertex_index = face.vertex_indices[indices[3 * k + 2]].v_idx;
7914
              idx2.normal_index =
7915
                  face.vertex_indices[indices[3 * k + 2]].vn_idx;
7916
              idx2.texcoord_index =
7917
                  face.vertex_indices[indices[3 * k + 2]].vt_idx;
7918
7919
              shape->mesh.indices.push_back(idx0);
7920
              shape->mesh.indices.push_back(idx1);
7921
              shape->mesh.indices.push_back(idx2);
7922
7923
              shape->mesh.num_face_vertices.push_back(3);
7924
              shape->mesh.material_ids.push_back(material_id);
7925
              shape->mesh.smoothing_group_ids.push_back(
7926
                  face.smoothing_group_id);
7927
            }
7928
          }
7929
7930
#else  // Built-in ear clipping triangulation
7931
20.5k
          vertex_index_t i0 = face.vertex_indices[0];
7932
20.5k
          vertex_index_t i1(-1);
7933
20.5k
          vertex_index_t i2 = face.vertex_indices[1];
7934
7935
          // find the two axes to work in
7936
20.5k
          size_t axes[2] = {1, 2};
7937
1.92M
          for (size_t k = 0; k < npolys; ++k) {
7938
1.90M
            i0 = face.vertex_indices[(k + 0) % npolys];
7939
1.90M
            i1 = face.vertex_indices[(k + 1) % npolys];
7940
1.90M
            i2 = face.vertex_indices[(k + 2) % npolys];
7941
1.90M
            size_t vi0 = size_t(i0.v_idx);
7942
1.90M
            size_t vi1 = size_t(i1.v_idx);
7943
1.90M
            size_t vi2 = size_t(i2.v_idx);
7944
7945
1.90M
            if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) ||
7946
1.84M
                ((3 * vi2 + 2) >= v.size())) {
7947
              // Invalid triangle.
7948
              // FIXME(syoyo): Is it ok to simply skip this invalid triangle?
7949
1.84M
              continue;
7950
1.84M
            }
7951
63.6k
            real_t v0x = v[vi0 * 3 + 0];
7952
63.6k
            real_t v0y = v[vi0 * 3 + 1];
7953
63.6k
            real_t v0z = v[vi0 * 3 + 2];
7954
63.6k
            real_t v1x = v[vi1 * 3 + 0];
7955
63.6k
            real_t v1y = v[vi1 * 3 + 1];
7956
63.6k
            real_t v1z = v[vi1 * 3 + 2];
7957
63.6k
            real_t v2x = v[vi2 * 3 + 0];
7958
63.6k
            real_t v2y = v[vi2 * 3 + 1];
7959
63.6k
            real_t v2z = v[vi2 * 3 + 2];
7960
63.6k
            real_t e0x = v1x - v0x;
7961
63.6k
            real_t e0y = v1y - v0y;
7962
63.6k
            real_t e0z = v1z - v0z;
7963
63.6k
            real_t e1x = v2x - v1x;
7964
63.6k
            real_t e1y = v2y - v1y;
7965
63.6k
            real_t e1z = v2z - v1z;
7966
63.6k
            real_t cx = std::fabs(e0y * e1z - e0z * e1y);
7967
63.6k
            real_t cy = std::fabs(e0z * e1x - e0x * e1z);
7968
63.6k
            real_t cz = std::fabs(e0x * e1y - e0y * e1x);
7969
63.6k
            const real_t epsilon = std::numeric_limits<real_t>::epsilon();
7970
            // std::cout << "cx " << cx << ", cy " << cy << ", cz " << cz <<
7971
            // "\n";
7972
63.6k
            if (cx > epsilon || cy > epsilon || cz > epsilon) {
7973
              // std::cout << "corner\n";
7974
              // found a corner
7975
4.31k
              if (cx > cy && cx > cz) {
7976
                // std::cout << "pattern0\n";
7977
2.85k
              } else {
7978
                // std::cout << "axes[0] = 0\n";
7979
2.85k
                axes[0] = 0;
7980
2.85k
                if (cz > cx && cz > cy) {
7981
                  // std::cout << "axes[1] = 1\n";
7982
1.64k
                  axes[1] = 1;
7983
1.64k
                }
7984
2.85k
              }
7985
4.31k
              break;
7986
4.31k
            }
7987
63.6k
          }
7988
7989
20.5k
          face_t remainingFace = face;  // copy
7990
20.5k
          size_t guess_vert = 0;
7991
20.5k
          vertex_index_t ind[3];
7992
20.5k
          real_t vx[3];
7993
20.5k
          real_t vy[3];
7994
7995
          // How many iterations can we do without decreasing the remaining
7996
          // vertices.
7997
20.5k
          size_t remainingIterations = face.vertex_indices.size();
7998
20.5k
          size_t previousRemainingVertices =
7999
20.5k
              remainingFace.vertex_indices.size();
8000
8001
1.88M
          while (remainingFace.vertex_indices.size() > 3 &&
8002
1.86M
                 remainingIterations > 0) {
8003
            // std::cout << "remainingIterations " << remainingIterations <<
8004
            // "\n";
8005
8006
1.86M
            npolys = remainingFace.vertex_indices.size();
8007
1.86M
            if (guess_vert >= npolys) {
8008
534
              guess_vert -= npolys;
8009
534
            }
8010
8011
1.86M
            if (previousRemainingVertices != npolys) {
8012
              // The number of remaining vertices decreased. Reset counters.
8013
1.83M
              previousRemainingVertices = npolys;
8014
1.83M
              remainingIterations = npolys;
8015
1.83M
            } else {
8016
              // We didn't consume a vertex on previous iteration, reduce the
8017
              // available iterations.
8018
26.9k
              remainingIterations--;
8019
26.9k
            }
8020
8021
7.46M
            for (size_t k = 0; k < 3; k++) {
8022
5.59M
              ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys];
8023
5.59M
              size_t vi = size_t(ind[k].v_idx);
8024
5.59M
              if (((vi * 3 + axes[0]) >= v.size()) ||
8025
5.37M
                  ((vi * 3 + axes[1]) >= v.size())) {
8026
                // ???
8027
5.37M
                vx[k] = static_cast<real_t>(0.0);
8028
5.37M
                vy[k] = static_cast<real_t>(0.0);
8029
5.37M
              } else {
8030
221k
                vx[k] = v[vi * 3 + axes[0]];
8031
221k
                vy[k] = v[vi * 3 + axes[1]];
8032
221k
              }
8033
5.59M
            }
8034
8035
            //
8036
            // area is calculated per face
8037
            //
8038
1.86M
            real_t e0x = vx[1] - vx[0];
8039
1.86M
            real_t e0y = vy[1] - vy[0];
8040
1.86M
            real_t e1x = vx[2] - vx[1];
8041
1.86M
            real_t e1y = vy[2] - vy[1];
8042
1.86M
            real_t cross = e0x * e1y - e0y * e1x;
8043
            // std::cout << "axes = " << axes[0] << ", " << axes[1] << "\n";
8044
            // std::cout << "e0x, e0y, e1x, e1y " << e0x << ", " << e0y << ", "
8045
            // << e1x << ", " << e1y << "\n";
8046
8047
1.86M
            real_t area =
8048
1.86M
                (vx[0] * vy[1] - vy[0] * vx[1]) * static_cast<real_t>(0.5);
8049
            // std::cout << "cross " << cross << ", area " << area << "\n";
8050
            // if an internal angle
8051
1.86M
            if (cross * area < static_cast<real_t>(0.0)) {
8052
              // std::cout << "internal \n";
8053
3.42k
              guess_vert += 1;
8054
              // std::cout << "guess vert : " << guess_vert << "\n";
8055
3.42k
              continue;
8056
3.42k
            }
8057
8058
            // check all other verts in case they are inside this triangle
8059
1.86M
            bool overlap = false;
8060
3.16G
            for (size_t otherVert = 3; otherVert < npolys; ++otherVert) {
8061
3.16G
              size_t idx = (guess_vert + otherVert) % npolys;
8062
8063
3.16G
              if (idx >= remainingFace.vertex_indices.size()) {
8064
                // std::cout << "???0\n";
8065
                // ???
8066
0
                continue;
8067
0
              }
8068
8069
3.16G
              size_t ovi = size_t(remainingFace.vertex_indices[idx].v_idx);
8070
8071
3.16G
              if (((ovi * 3 + axes[0]) >= v.size()) ||
8072
2.98G
                  ((ovi * 3 + axes[1]) >= v.size())) {
8073
                // std::cout << "???1\n";
8074
                // ???
8075
2.98G
                continue;
8076
2.98G
              }
8077
176M
              real_t tx = v[ovi * 3 + axes[0]];
8078
176M
              real_t ty = v[ovi * 3 + axes[1]];
8079
176M
              if (pnpoly(3, vx, vy, tx, ty)) {
8080
                // std::cout << "overlap\n";
8081
3.31k
                overlap = true;
8082
3.31k
                break;
8083
3.31k
              }
8084
176M
            }
8085
8086
1.86M
            if (overlap) {
8087
              // std::cout << "overlap2\n";
8088
3.31k
              guess_vert += 1;
8089
3.31k
              continue;
8090
3.31k
            }
8091
8092
            // this triangle is an ear
8093
1.85M
            {
8094
1.85M
              index_t idx0, idx1, idx2;
8095
1.85M
              idx0.vertex_index = ind[0].v_idx;
8096
1.85M
              idx0.normal_index = ind[0].vn_idx;
8097
1.85M
              idx0.texcoord_index = ind[0].vt_idx;
8098
1.85M
              idx1.vertex_index = ind[1].v_idx;
8099
1.85M
              idx1.normal_index = ind[1].vn_idx;
8100
1.85M
              idx1.texcoord_index = ind[1].vt_idx;
8101
1.85M
              idx2.vertex_index = ind[2].v_idx;
8102
1.85M
              idx2.normal_index = ind[2].vn_idx;
8103
1.85M
              idx2.texcoord_index = ind[2].vt_idx;
8104
8105
1.85M
              shape->mesh.indices.push_back(idx0);
8106
1.85M
              shape->mesh.indices.push_back(idx1);
8107
1.85M
              shape->mesh.indices.push_back(idx2);
8108
8109
1.85M
              shape->mesh.num_face_vertices.push_back(3);
8110
1.85M
              shape->mesh.material_ids.push_back(material_id);
8111
1.85M
              shape->mesh.smoothing_group_ids.push_back(
8112
1.85M
                  face.smoothing_group_id);
8113
1.85M
            }
8114
8115
            // remove v1 from the list
8116
1.85M
            size_t removed_vert_index = (guess_vert + 1) % npolys;
8117
3.16G
            while (removed_vert_index + 1 < npolys) {
8118
3.16G
              remainingFace.vertex_indices[removed_vert_index] =
8119
3.16G
                  remainingFace.vertex_indices[removed_vert_index + 1];
8120
3.16G
              removed_vert_index += 1;
8121
3.16G
            }
8122
1.85M
            remainingFace.vertex_indices.pop_back();
8123
1.85M
          }
8124
8125
          // std::cout << "remainingFace.vi.size = " <<
8126
          // remainingFace.vertex_indices.size() << "\n";
8127
20.5k
          if (remainingFace.vertex_indices.size() == 3) {
8128
20.1k
            i0 = remainingFace.vertex_indices[0];
8129
20.1k
            i1 = remainingFace.vertex_indices[1];
8130
20.1k
            i2 = remainingFace.vertex_indices[2];
8131
20.1k
            {
8132
20.1k
              index_t idx0, idx1, idx2;
8133
20.1k
              idx0.vertex_index = i0.v_idx;
8134
20.1k
              idx0.normal_index = i0.vn_idx;
8135
20.1k
              idx0.texcoord_index = i0.vt_idx;
8136
20.1k
              idx1.vertex_index = i1.v_idx;
8137
20.1k
              idx1.normal_index = i1.vn_idx;
8138
20.1k
              idx1.texcoord_index = i1.vt_idx;
8139
20.1k
              idx2.vertex_index = i2.v_idx;
8140
20.1k
              idx2.normal_index = i2.vn_idx;
8141
20.1k
              idx2.texcoord_index = i2.vt_idx;
8142
8143
20.1k
              shape->mesh.indices.push_back(idx0);
8144
20.1k
              shape->mesh.indices.push_back(idx1);
8145
20.1k
              shape->mesh.indices.push_back(idx2);
8146
8147
20.1k
              shape->mesh.num_face_vertices.push_back(3);
8148
20.1k
              shape->mesh.material_ids.push_back(material_id);
8149
20.1k
              shape->mesh.smoothing_group_ids.push_back(
8150
20.1k
                  face.smoothing_group_id);
8151
20.1k
            }
8152
20.1k
          }
8153
20.5k
#endif
8154
20.5k
        }  // npolys
8155
43.0k
      } else {
8156
46.5k
        for (size_t k = 0; k < npolys; k++) {
8157
34.9k
          index_t idx;
8158
34.9k
          idx.vertex_index = face.vertex_indices[k].v_idx;
8159
34.9k
          idx.normal_index = face.vertex_indices[k].vn_idx;
8160
34.9k
          idx.texcoord_index = face.vertex_indices[k].vt_idx;
8161
34.9k
          shape->mesh.indices.push_back(idx);
8162
34.9k
        }
8163
8164
11.6k
        shape->mesh.num_face_vertices.push_back(
8165
11.6k
            static_cast<unsigned int>(npolys));
8166
11.6k
        shape->mesh.material_ids.push_back(material_id);  // per face
8167
11.6k
        shape->mesh.smoothing_group_ids.push_back(
8168
11.6k
            face.smoothing_group_id);  // per face
8169
11.6k
      }
8170
54.6k
    }
8171
8172
37.8k
    shape->mesh.tags = tags;
8173
37.8k
  }
8174
8175
  // line
8176
241k
  if (!prim_group.lineGroup.empty()) {
8177
    // Flatten indices
8178
213M
    for (size_t i = 0; i < prim_group.lineGroup.size(); i++) {
8179
244M
      for (size_t j = 0; j < prim_group.lineGroup[i].vertex_indices.size();
8180
213M
           j++) {
8181
30.5M
        const vertex_index_t &vi = prim_group.lineGroup[i].vertex_indices[j];
8182
8183
30.5M
        index_t idx;
8184
30.5M
        idx.vertex_index = vi.v_idx;
8185
30.5M
        idx.normal_index = vi.vn_idx;
8186
30.5M
        idx.texcoord_index = vi.vt_idx;
8187
8188
30.5M
        shape->lines.indices.push_back(idx);
8189
30.5M
      }
8190
8191
213M
      shape->lines.num_line_vertices.push_back(
8192
213M
          int(prim_group.lineGroup[i].vertex_indices.size()));
8193
213M
    }
8194
35.9k
  }
8195
8196
  // points
8197
241k
  if (!prim_group.pointsGroup.empty()) {
8198
    // Flatten & convert indices
8199
497k
    for (size_t i = 0; i < prim_group.pointsGroup.size(); i++) {
8200
3.18M
      for (size_t j = 0; j < prim_group.pointsGroup[i].vertex_indices.size();
8201
2.87M
           j++) {
8202
2.87M
        const vertex_index_t &vi = prim_group.pointsGroup[i].vertex_indices[j];
8203
8204
2.87M
        index_t idx;
8205
2.87M
        idx.vertex_index = vi.v_idx;
8206
2.87M
        idx.normal_index = vi.vn_idx;
8207
2.87M
        idx.texcoord_index = vi.vt_idx;
8208
8209
2.87M
        shape->points.indices.push_back(idx);
8210
2.87M
      }
8211
312k
    }
8212
184k
  }
8213
8214
241k
  return true;
8215
484k
}
8216
8217
// Split a string with specified delimiter character and escape character.
8218
// https://rosettacode.org/wiki/Tokenize_a_string_with_escaping#C.2B.2B
8219
static void SplitString(const std::string &s, char delim, char escape,
8220
61.0k
                        std::vector<std::string> &elems) {
8221
61.0k
  std::string token;
8222
8223
61.0k
  bool escaping = false;
8224
17.3M
  for (size_t i = 0; i < s.size(); ++i) {
8225
17.3M
    char ch = s[i];
8226
17.3M
    if (escaping) {
8227
19.9k
      escaping = false;
8228
17.2M
    } else if (ch == escape) {
8229
22.6k
      if ((i + 1) < s.size()) {
8230
21.9k
        const char next = s[i + 1];
8231
21.9k
        if ((next == delim) || (next == escape)) {
8232
19.9k
          escaping = true;
8233
19.9k
          continue;
8234
19.9k
        }
8235
21.9k
      }
8236
17.2M
    } else if (ch == delim) {
8237
1.13M
      if (!token.empty()) {
8238
1.12M
        elems.push_back(token);
8239
1.12M
      }
8240
1.13M
      token.clear();
8241
1.13M
      continue;
8242
1.13M
    }
8243
16.1M
    token += ch;
8244
16.1M
  }
8245
8246
61.0k
  elems.push_back(token);
8247
61.0k
}
8248
8249
61.0k
static void RemoveEmptyTokens(std::vector<std::string> *tokens) {
8250
61.0k
  if (!tokens) return;
8251
8252
61.0k
  const std::vector<std::string> &src = *tokens;
8253
61.0k
  std::vector<std::string> filtered;
8254
61.0k
  filtered.reserve(src.size());
8255
1.24M
  for (size_t i = 0; i < src.size(); i++) {
8256
1.18M
    if (!src[i].empty()) {
8257
1.17M
      filtered.push_back(src[i]);
8258
1.17M
    }
8259
1.18M
  }
8260
61.0k
  tokens->swap(filtered);
8261
61.0k
}
8262
8263
static std::string JoinPath(const std::string &dir,
8264
0
                            const std::string &filename) {
8265
0
  if (dir.empty()) {
8266
0
    return filename;
8267
0
  } else {
8268
    // check '/'
8269
0
    char lastChar = *dir.rbegin();
8270
0
    if (lastChar != '/') {
8271
0
      return dir + std::string("/") + filename;
8272
0
    } else {
8273
0
      return dir + filename;
8274
0
    }
8275
0
  }
8276
0
}
8277
8278
static bool LoadMtlInternal(std::map<std::string, int> *material_map,
8279
                            std::vector<material_t> *materials,
8280
                            StreamReader &sr,
8281
                            std::string *warning, std::string *err,
8282
11.8k
                            const std::string &filename = "<stream>") {
8283
11.8k
  if (sr.has_errors()) {
8284
0
    if (err) {
8285
0
      (*err) += sr.get_errors();
8286
0
    }
8287
0
    return false;
8288
0
  }
8289
8290
11.8k
  material_t material;
8291
11.8k
  InitMaterial(&material);
8292
8293
  // Issue 43. `d` wins against `Tr` since `Tr` is not in the MTL specification.
8294
11.8k
  bool has_d = false;
8295
11.8k
  bool has_tr = false;
8296
8297
  // has_kd is used to set a default diffuse value when map_Kd is present
8298
  // and Kd is not.
8299
11.8k
  bool has_kd = false;
8300
8301
11.8k
  std::stringstream warn_ss;
8302
8303
  // Handle BOM
8304
11.8k
  if (sr.remaining() >= 3 &&
8305
9.32k
      static_cast<unsigned char>(sr.peek()) == 0xEF &&
8306
64
      static_cast<unsigned char>(sr.peek_at(1)) == 0xBB &&
8307
41
      static_cast<unsigned char>(sr.peek_at(2)) == 0xBF) {
8308
13
    sr.advance(3);
8309
13
  }
8310
8311
3.11M
  while (!sr.eof()) {
8312
3.10M
    sr.skip_space();
8313
3.10M
    if (sr.at_line_end()) { sr.skip_line(); continue; }
8314
2.71M
    if (sr.peek() == '#') { sr.skip_line(); continue; }
8315
8316
2.68M
    size_t line_num = sr.line_num();
8317
8318
    // new mtl
8319
2.68M
    if (sr.match("newmtl", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8320
      // flush previous material.
8321
480k
      if (!material.name.empty()) {
8322
478k
        material_map->insert(std::pair<std::string, int>(
8323
478k
            material.name, static_cast<int>(materials->size())));
8324
478k
        materials->push_back(material);
8325
478k
      }
8326
8327
480k
      InitMaterial(&material);
8328
8329
480k
      has_d = false;
8330
480k
      has_tr = false;
8331
480k
      has_kd = false;
8332
8333
480k
      sr.advance(7);
8334
480k
      {
8335
480k
        std::string namebuf = sr_parseString(sr);
8336
480k
        if (namebuf.empty()) {
8337
751
          if (warning) {
8338
751
            (*warning) += "empty material name in `newmtl`\n";
8339
751
          }
8340
751
        }
8341
480k
        material.name = namebuf;
8342
480k
      }
8343
480k
      sr.skip_line();
8344
480k
      continue;
8345
480k
    }
8346
8347
    // ambient
8348
2.20M
    if (sr.peek() == 'K' && sr.peek_at(1) == 'a' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8349
19.6k
      sr.advance(2);
8350
19.6k
      real_t r, g, b;
8351
19.6k
      if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false;
8352
19.6k
      material.ambient[0] = r;
8353
19.6k
      material.ambient[1] = g;
8354
19.6k
      material.ambient[2] = b;
8355
19.6k
      sr.skip_line();
8356
19.6k
      continue;
8357
19.6k
    }
8358
8359
    // diffuse
8360
2.18M
    if (sr.peek() == 'K' && sr.peek_at(1) == 'd' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8361
6.11k
      sr.advance(2);
8362
6.11k
      real_t r, g, b;
8363
6.11k
      if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false;
8364
6.06k
      material.diffuse[0] = r;
8365
6.06k
      material.diffuse[1] = g;
8366
6.06k
      material.diffuse[2] = b;
8367
6.06k
      has_kd = true;
8368
6.06k
      sr.skip_line();
8369
6.06k
      continue;
8370
6.11k
    }
8371
8372
    // specular
8373
2.18M
    if (sr.peek() == 'K' && sr.peek_at(1) == 's' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8374
6.13k
      sr.advance(2);
8375
6.13k
      real_t r, g, b;
8376
6.13k
      if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false;
8377
6.10k
      material.specular[0] = r;
8378
6.10k
      material.specular[1] = g;
8379
6.10k
      material.specular[2] = b;
8380
6.10k
      sr.skip_line();
8381
6.10k
      continue;
8382
6.13k
    }
8383
8384
    // transmittance
8385
2.17M
    if ((sr.peek() == 'K' && sr.peek_at(1) == 't' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) ||
8386
2.17M
        (sr.peek() == 'T' && sr.peek_at(1) == 'f' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t'))) {
8387
39.9k
      sr.advance(2);
8388
39.9k
      real_t r, g, b;
8389
39.9k
      if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false;
8390
39.9k
      material.transmittance[0] = r;
8391
39.9k
      material.transmittance[1] = g;
8392
39.9k
      material.transmittance[2] = b;
8393
39.9k
      sr.skip_line();
8394
39.9k
      continue;
8395
39.9k
    }
8396
8397
    // ior(index of refraction)
8398
2.13M
    if (sr.peek() == 'N' && sr.peek_at(1) == 'i' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8399
14.4k
      sr.advance(2);
8400
14.4k
      if (!sr_parseReal(sr, &material.ior, 0.0, err, filename)) return false;
8401
14.4k
      sr.skip_line();
8402
14.4k
      continue;
8403
14.4k
    }
8404
8405
    // emission
8406
2.12M
    if (sr.peek() == 'K' && sr.peek_at(1) == 'e' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8407
25.7k
      sr.advance(2);
8408
25.7k
      real_t r, g, b;
8409
25.7k
      if (!sr_parseReal3(&r, &g, &b, sr, err, filename)) return false;
8410
25.7k
      material.emission[0] = r;
8411
25.7k
      material.emission[1] = g;
8412
25.7k
      material.emission[2] = b;
8413
25.7k
      sr.skip_line();
8414
25.7k
      continue;
8415
25.7k
    }
8416
8417
    // shininess
8418
2.09M
    if (sr.peek() == 'N' && sr.peek_at(1) == 's' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8419
21.3k
      sr.advance(2);
8420
21.3k
      if (!sr_parseReal(sr, &material.shininess, 0.0, err, filename)) return false;
8421
21.3k
      sr.skip_line();
8422
21.3k
      continue;
8423
21.3k
    }
8424
8425
    // illum model
8426
2.07M
    if (sr.match("illum", 5) && (sr.peek_at(5) == ' ' || sr.peek_at(5) == '\t')) {
8427
6.14k
      sr.advance(6);
8428
6.14k
      if (!sr_parseInt(sr, &material.illum, err, filename)) return false;
8429
6.10k
      sr.skip_line();
8430
6.10k
      continue;
8431
6.14k
    }
8432
8433
    // dissolve
8434
2.06M
    if (sr.peek() == 'd' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
8435
13.7k
      sr.advance(1);
8436
13.7k
      if (!sr_parseReal(sr, &material.dissolve, 0.0, err, filename)) return false;
8437
8438
13.7k
      if (has_tr) {
8439
9.70k
        warn_ss << "Both `d` and `Tr` parameters defined for \""
8440
9.70k
                << material.name
8441
9.70k
                << "\". Use the value of `d` for dissolve (line " << line_num
8442
9.70k
                << " in .mtl.)\n";
8443
9.70k
      }
8444
13.7k
      has_d = true;
8445
13.7k
      sr.skip_line();
8446
13.7k
      continue;
8447
13.7k
    }
8448
2.05M
    if (sr.peek() == 'T' && sr.peek_at(1) == 'r' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8449
56.2k
      sr.advance(2);
8450
56.2k
      if (has_d) {
8451
27.1k
        warn_ss << "Both `d` and `Tr` parameters defined for \""
8452
27.1k
                << material.name
8453
27.1k
                << "\". Use the value of `d` for dissolve (line " << line_num
8454
27.1k
                << " in .mtl.)\n";
8455
29.1k
      } else {
8456
29.1k
        real_t tr_val;
8457
29.1k
        if (!sr_parseReal(sr, &tr_val, 0.0, err, filename)) return false;
8458
29.1k
        material.dissolve = static_cast<real_t>(1.0) - tr_val;
8459
29.1k
      }
8460
56.2k
      has_tr = true;
8461
56.2k
      sr.skip_line();
8462
56.2k
      continue;
8463
56.2k
    }
8464
8465
    // PBR: roughness
8466
1.99M
    if (sr.peek() == 'P' && sr.peek_at(1) == 'r' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8467
4.40k
      sr.advance(2);
8468
4.40k
      if (!sr_parseReal(sr, &material.roughness, 0.0, err, filename)) return false;
8469
4.40k
      sr.skip_line();
8470
4.40k
      continue;
8471
4.40k
    }
8472
8473
    // PBR: metallic
8474
1.99M
    if (sr.peek() == 'P' && sr.peek_at(1) == 'm' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8475
2.07k
      sr.advance(2);
8476
2.07k
      if (!sr_parseReal(sr, &material.metallic, 0.0, err, filename)) return false;
8477
2.06k
      sr.skip_line();
8478
2.06k
      continue;
8479
2.07k
    }
8480
8481
    // PBR: sheen
8482
1.99M
    if (sr.peek() == 'P' && sr.peek_at(1) == 's' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8483
2.32k
      sr.advance(2);
8484
2.32k
      if (!sr_parseReal(sr, &material.sheen, 0.0, err, filename)) return false;
8485
2.31k
      sr.skip_line();
8486
2.31k
      continue;
8487
2.32k
    }
8488
8489
    // PBR: clearcoat thickness
8490
1.98M
    if (sr.peek() == 'P' && sr.peek_at(1) == 'c' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8491
2.89k
      sr.advance(2);
8492
2.89k
      if (!sr_parseReal(sr, &material.clearcoat_thickness, 0.0, err, filename)) return false;
8493
2.88k
      sr.skip_line();
8494
2.88k
      continue;
8495
2.89k
    }
8496
8497
    // PBR: clearcoat roughness
8498
1.98M
    if (sr.match("Pcr", 3) && (sr.peek_at(3) == ' ' || sr.peek_at(3) == '\t')) {
8499
12.8k
      sr.advance(4);
8500
12.8k
      if (!sr_parseReal(sr, &material.clearcoat_roughness, 0.0, err, filename)) return false;
8501
12.7k
      sr.skip_line();
8502
12.7k
      continue;
8503
12.8k
    }
8504
8505
    // PBR: anisotropy
8506
1.97M
    if (sr.match("aniso", 5) && (sr.peek_at(5) == ' ' || sr.peek_at(5) == '\t')) {
8507
1.51k
      sr.advance(6);
8508
1.51k
      if (!sr_parseReal(sr, &material.anisotropy, 0.0, err, filename)) return false;
8509
1.51k
      sr.skip_line();
8510
1.51k
      continue;
8511
1.51k
    }
8512
8513
    // PBR: anisotropy rotation
8514
1.97M
    if (sr.match("anisor", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8515
7.11k
      sr.advance(7);
8516
7.11k
      if (!sr_parseReal(sr, &material.anisotropy_rotation, 0.0, err, filename)) return false;
8517
7.10k
      sr.skip_line();
8518
7.10k
      continue;
8519
7.11k
    }
8520
8521
    // For texture directives, read rest of line and delegate to
8522
    // ParseTextureNameAndOption (which uses the old const char* parse functions).
8523
8524
    // ambient or ambient occlusion texture
8525
1.96M
    if (sr.match("map_Ka", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8526
3.17k
      sr.advance(7);
8527
3.17k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8528
3.17k
      ParseTextureNameAndOption(&(material.ambient_texname),
8529
3.17k
                                &(material.ambient_texopt), line_rest.c_str());
8530
3.17k
      sr.skip_line();
8531
3.17k
      continue;
8532
3.17k
    }
8533
8534
    // diffuse texture
8535
1.96M
    if (sr.match("map_Kd", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8536
12.9k
      sr.advance(7);
8537
12.9k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8538
12.9k
      ParseTextureNameAndOption(&(material.diffuse_texname),
8539
12.9k
                                &(material.diffuse_texopt), line_rest.c_str());
8540
12.9k
      if (!has_kd) {
8541
12.6k
        material.diffuse[0] = static_cast<real_t>(0.6);
8542
12.6k
        material.diffuse[1] = static_cast<real_t>(0.6);
8543
12.6k
        material.diffuse[2] = static_cast<real_t>(0.6);
8544
12.6k
      }
8545
12.9k
      sr.skip_line();
8546
12.9k
      continue;
8547
12.9k
    }
8548
8549
    // specular texture
8550
1.94M
    if (sr.match("map_Ks", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8551
2.01k
      sr.advance(7);
8552
2.01k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8553
2.01k
      ParseTextureNameAndOption(&(material.specular_texname),
8554
2.01k
                                &(material.specular_texopt), line_rest.c_str());
8555
2.01k
      sr.skip_line();
8556
2.01k
      continue;
8557
2.01k
    }
8558
8559
    // specular highlight texture
8560
1.94M
    if (sr.match("map_Ns", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8561
3.55k
      sr.advance(7);
8562
3.55k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8563
3.55k
      ParseTextureNameAndOption(&(material.specular_highlight_texname),
8564
3.55k
                                &(material.specular_highlight_texopt), line_rest.c_str());
8565
3.55k
      sr.skip_line();
8566
3.55k
      continue;
8567
3.55k
    }
8568
8569
    // bump texture
8570
1.94M
    if ((sr.match("map_bump", 8) || sr.match("map_Bump", 8)) &&
8571
6.50k
        (sr.peek_at(8) == ' ' || sr.peek_at(8) == '\t')) {
8572
3.93k
      sr.advance(9);
8573
3.93k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8574
3.93k
      ParseTextureNameAndOption(&(material.bump_texname),
8575
3.93k
                                &(material.bump_texopt), line_rest.c_str());
8576
3.93k
      sr.skip_line();
8577
3.93k
      continue;
8578
3.93k
    }
8579
8580
    // bump texture (short form)
8581
1.93M
    if (sr.match("bump", 4) && (sr.peek_at(4) == ' ' || sr.peek_at(4) == '\t')) {
8582
16.1k
      sr.advance(5);
8583
16.1k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8584
16.1k
      ParseTextureNameAndOption(&(material.bump_texname),
8585
16.1k
                                &(material.bump_texopt), line_rest.c_str());
8586
16.1k
      sr.skip_line();
8587
16.1k
      continue;
8588
16.1k
    }
8589
8590
    // alpha texture
8591
1.92M
    if (sr.match("map_d", 5) && (sr.peek_at(5) == ' ' || sr.peek_at(5) == '\t')) {
8592
4.86k
      sr.advance(6);
8593
4.86k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8594
4.86k
      ParseTextureNameAndOption(&(material.alpha_texname),
8595
4.86k
                                &(material.alpha_texopt), line_rest.c_str());
8596
4.86k
      sr.skip_line();
8597
4.86k
      continue;
8598
4.86k
    }
8599
8600
    // displacement texture
8601
1.91M
    if ((sr.match("map_disp", 8) || sr.match("map_Disp", 8)) &&
8602
28.5k
        (sr.peek_at(8) == ' ' || sr.peek_at(8) == '\t')) {
8603
24.7k
      sr.advance(9);
8604
24.7k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8605
24.7k
      ParseTextureNameAndOption(&(material.displacement_texname),
8606
24.7k
                                &(material.displacement_texopt), line_rest.c_str());
8607
24.7k
      sr.skip_line();
8608
24.7k
      continue;
8609
24.7k
    }
8610
8611
    // displacement texture (short form)
8612
1.89M
    if (sr.match("disp", 4) && (sr.peek_at(4) == ' ' || sr.peek_at(4) == '\t')) {
8613
33.4k
      sr.advance(5);
8614
33.4k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8615
33.4k
      ParseTextureNameAndOption(&(material.displacement_texname),
8616
33.4k
                                &(material.displacement_texopt), line_rest.c_str());
8617
33.4k
      sr.skip_line();
8618
33.4k
      continue;
8619
33.4k
    }
8620
8621
    // reflection map
8622
1.85M
    if (sr.match("refl", 4) && (sr.peek_at(4) == ' ' || sr.peek_at(4) == '\t')) {
8623
128k
      sr.advance(5);
8624
128k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8625
128k
      ParseTextureNameAndOption(&(material.reflection_texname),
8626
128k
                                &(material.reflection_texopt), line_rest.c_str());
8627
128k
      sr.skip_line();
8628
128k
      continue;
8629
128k
    }
8630
8631
    // PBR: roughness texture
8632
1.73M
    if (sr.match("map_Pr", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8633
4.58k
      sr.advance(7);
8634
4.58k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8635
4.58k
      ParseTextureNameAndOption(&(material.roughness_texname),
8636
4.58k
                                &(material.roughness_texopt), line_rest.c_str());
8637
4.58k
      sr.skip_line();
8638
4.58k
      continue;
8639
4.58k
    }
8640
8641
    // PBR: metallic texture
8642
1.72M
    if (sr.match("map_Pm", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8643
5.32k
      sr.advance(7);
8644
5.32k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8645
5.32k
      ParseTextureNameAndOption(&(material.metallic_texname),
8646
5.32k
                                &(material.metallic_texopt), line_rest.c_str());
8647
5.32k
      sr.skip_line();
8648
5.32k
      continue;
8649
5.32k
    }
8650
8651
    // PBR: sheen texture
8652
1.72M
    if (sr.match("map_Ps", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8653
92.4k
      sr.advance(7);
8654
92.4k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8655
92.4k
      ParseTextureNameAndOption(&(material.sheen_texname),
8656
92.4k
                                &(material.sheen_texopt), line_rest.c_str());
8657
92.4k
      sr.skip_line();
8658
92.4k
      continue;
8659
92.4k
    }
8660
8661
    // PBR: emissive texture
8662
1.62M
    if (sr.match("map_Ke", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
8663
14.5k
      sr.advance(7);
8664
14.5k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8665
14.5k
      ParseTextureNameAndOption(&(material.emissive_texname),
8666
14.5k
                                &(material.emissive_texopt), line_rest.c_str());
8667
14.5k
      sr.skip_line();
8668
14.5k
      continue;
8669
14.5k
    }
8670
8671
    // PBR: normal map texture
8672
1.61M
    if (sr.match("norm", 4) && (sr.peek_at(4) == ' ' || sr.peek_at(4) == '\t')) {
8673
20.4k
      sr.advance(5);
8674
20.4k
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8675
20.4k
      ParseTextureNameAndOption(&(material.normal_texname),
8676
20.4k
                                &(material.normal_texopt), line_rest.c_str());
8677
20.4k
      sr.skip_line();
8678
20.4k
      continue;
8679
20.4k
    }
8680
8681
    // unknown parameter
8682
1.59M
    {
8683
1.59M
      std::string line_rest = trimTrailingWhitespace(sr.read_line());
8684
1.59M
      const char *_lp = line_rest.c_str();
8685
1.59M
      const char *_space = strchr(_lp, ' ');
8686
1.59M
      if (!_space) {
8687
1.21M
        _space = strchr(_lp, '\t');
8688
1.21M
      }
8689
1.59M
      if (_space) {
8690
629k
        std::ptrdiff_t len = _space - _lp;
8691
629k
        std::string key(_lp, static_cast<size_t>(len));
8692
629k
        std::string value = _space + 1;
8693
629k
        material.unknown_parameter.insert(
8694
629k
            std::pair<std::string, std::string>(key, value));
8695
629k
      }
8696
1.59M
    }
8697
1.59M
    sr.skip_line();
8698
1.59M
  }
8699
  // flush last material (only if it was actually defined).
8700
11.6k
  if (!material.name.empty()) {
8701
1.29k
    material_map->insert(std::pair<std::string, int>(
8702
1.29k
        material.name, static_cast<int>(materials->size())));
8703
1.29k
    materials->push_back(material);
8704
1.29k
  }
8705
8706
11.6k
  if (warning) {
8707
11.6k
    (*warning) += warn_ss.str();
8708
11.6k
  }
8709
8710
11.6k
  return true;
8711
11.8k
}
8712
8713
void LoadMtl(std::map<std::string, int> *material_map,
8714
             std::vector<material_t> *materials, std::istream *inStream,
8715
0
             std::string *warning, std::string *err) {
8716
0
  StreamReader sr(*inStream);
8717
0
  LoadMtlInternal(material_map, materials, sr, warning, err);
8718
0
}
8719
8720
8721
bool MaterialFileReader::operator()(const std::string &matId,
8722
                                    std::vector<material_t> *materials,
8723
                                    std::map<std::string, int> *matMap,
8724
0
                                    std::string *warn, std::string *err) {
8725
0
  if (!m_mtlBaseDir.empty()) {
8726
#ifdef _WIN32
8727
    char sep = ';';
8728
#else
8729
0
    char sep = ':';
8730
0
#endif
8731
8732
    // https://stackoverflow.com/questions/5167625/splitting-a-c-stdstring-using-tokens-e-g
8733
0
    std::vector<std::string> paths;
8734
0
    std::istringstream f(m_mtlBaseDir);
8735
8736
0
    std::string s;
8737
0
    while (getline(f, s, sep)) {
8738
0
      paths.push_back(s);
8739
0
    }
8740
8741
0
    for (size_t i = 0; i < paths.size(); i++) {
8742
0
      std::string filepath = JoinPath(paths[i], matId);
8743
8744
#ifdef TINYOBJLOADER_USE_MMAP
8745
      {
8746
        MappedFile mf;
8747
        if (!mf.open(filepath.c_str())) continue;
8748
        if (mf.size > TINYOBJLOADER_STREAM_READER_MAX_BYTES) {
8749
          if (err) {
8750
            std::stringstream ss;
8751
            ss << "input stream too large (" << mf.size
8752
               << " bytes exceeds limit "
8753
               << TINYOBJLOADER_STREAM_READER_MAX_BYTES << " bytes)\n";
8754
            (*err) += ss.str();
8755
          }
8756
          return false;
8757
        }
8758
        StreamReader sr(mf.data, mf.size);
8759
        return LoadMtlInternal(matMap, materials, sr, warn, err, filepath);
8760
      }
8761
#else   // !TINYOBJLOADER_USE_MMAP
8762
#ifdef _WIN32
8763
      std::ifstream matIStream(LongPathW(UTF8ToWchar(filepath)).c_str());
8764
#else
8765
0
      std::ifstream matIStream(filepath.c_str());
8766
0
#endif
8767
0
      if (matIStream) {
8768
0
        StreamReader mtl_sr(matIStream);
8769
0
        return LoadMtlInternal(matMap, materials, mtl_sr, warn, err, filepath);
8770
0
      }
8771
0
#endif  // TINYOBJLOADER_USE_MMAP
8772
0
    }
8773
8774
0
    std::stringstream ss;
8775
0
    ss << "Material file [ " << matId
8776
0
       << " ] not found in a path : " << m_mtlBaseDir << "\n";
8777
0
    if (warn) {
8778
0
      (*warn) += ss.str();
8779
0
    }
8780
0
    return false;
8781
8782
0
  } else {
8783
0
    std::string filepath = matId;
8784
8785
#ifdef TINYOBJLOADER_USE_MMAP
8786
    {
8787
      MappedFile mf;
8788
      if (mf.open(filepath.c_str())) {
8789
        if (mf.size > TINYOBJLOADER_STREAM_READER_MAX_BYTES) {
8790
          if (err) {
8791
            std::stringstream ss;
8792
            ss << "input stream too large (" << mf.size
8793
               << " bytes exceeds limit "
8794
               << TINYOBJLOADER_STREAM_READER_MAX_BYTES << " bytes)\n";
8795
            (*err) += ss.str();
8796
          }
8797
          return false;
8798
        }
8799
        StreamReader sr(mf.data, mf.size);
8800
        return LoadMtlInternal(matMap, materials, sr, warn, err, filepath);
8801
      }
8802
    }
8803
#else   // !TINYOBJLOADER_USE_MMAP
8804
#ifdef _WIN32
8805
    std::ifstream matIStream(LongPathW(UTF8ToWchar(filepath)).c_str());
8806
#else
8807
0
    std::ifstream matIStream(filepath.c_str());
8808
0
#endif
8809
0
    if (matIStream) {
8810
0
      StreamReader mtl_sr(matIStream);
8811
0
      return LoadMtlInternal(matMap, materials, mtl_sr, warn, err, filepath);
8812
0
    }
8813
0
#endif  // TINYOBJLOADER_USE_MMAP
8814
8815
0
    std::stringstream ss;
8816
0
    ss << "Material file [ " << filepath
8817
0
       << " ] not found in a path : " << m_mtlBaseDir << "\n";
8818
0
    if (warn) {
8819
0
      (*warn) += ss.str();
8820
0
    }
8821
8822
0
    return false;
8823
0
  }
8824
0
}
8825
8826
bool MaterialStreamReader::operator()(const std::string &matId,
8827
                                      std::vector<material_t> *materials,
8828
                                      std::map<std::string, int> *matMap,
8829
11.8k
                                      std::string *warn, std::string *err) {
8830
11.8k
  (void)matId;
8831
11.8k
  if (!m_inStream) {
8832
0
    std::stringstream ss;
8833
0
    ss << "Material stream in error state. \n";
8834
0
    if (warn) {
8835
0
      (*warn) += ss.str();
8836
0
    }
8837
0
    return false;
8838
0
  }
8839
8840
11.8k
  StreamReader mtl_sr(m_inStream);
8841
11.8k
  return LoadMtlInternal(matMap, materials, mtl_sr, warn, err, "<stream>");
8842
11.8k
}
8843
8844
static bool LoadObjInternal(attrib_t *attrib, std::vector<shape_t> *shapes,
8845
                            std::vector<material_t> *materials,
8846
                            std::string *warn, std::string *err,
8847
                            StreamReader &sr,
8848
                            MaterialReader *readMatFn, bool triangulate,
8849
                            bool default_vcols_fallback,
8850
11.4k
                            const std::string &filename = "<stream>") {
8851
11.4k
  if (sr.has_errors()) {
8852
0
    if (err) {
8853
0
      (*err) += sr.get_errors();
8854
0
    }
8855
0
    return false;
8856
0
  }
8857
8858
11.4k
  std::vector<real_t> v;
8859
11.4k
  std::vector<real_t> vertex_weights;
8860
11.4k
  std::vector<real_t> vn;
8861
11.4k
  std::vector<real_t> vt;
8862
11.4k
  std::vector<real_t> vt_w;  // optional [w] component in `vt`
8863
11.4k
  std::vector<real_t> vc;
8864
11.4k
  std::vector<skin_weight_t> vw;
8865
11.4k
  std::vector<tag_t> tags;
8866
11.4k
  PrimGroup prim_group;
8867
11.4k
  std::string name;
8868
8869
  // material
8870
11.4k
  std::set<std::string> material_filenames;
8871
11.4k
  std::map<std::string, int> material_map;
8872
11.4k
  int material = -1;
8873
8874
11.4k
  unsigned int current_smoothing_id = 0;
8875
8876
11.4k
  int greatest_v_idx = -1;
8877
11.4k
  int greatest_vn_idx = -1;
8878
11.4k
  int greatest_vt_idx = -1;
8879
8880
11.4k
  shape_t shape;
8881
8882
11.4k
  bool found_all_colors = true;
8883
8884
  // Handle BOM
8885
11.4k
  if (sr.remaining() >= 3 &&
8886
11.2k
      static_cast<unsigned char>(sr.peek()) == 0xEF &&
8887
66
      static_cast<unsigned char>(sr.peek_at(1)) == 0xBB &&
8888
49
      static_cast<unsigned char>(sr.peek_at(2)) == 0xBF) {
8889
35
    sr.advance(3);
8890
35
  }
8891
8892
11.4k
  warning_context context;
8893
11.4k
  context.warn = warn;
8894
11.4k
  context.filename = filename;
8895
8896
6.51M
  while (!sr.eof()) {
8897
6.50M
    sr.skip_space();
8898
6.50M
    if (sr.at_line_end()) { sr.skip_line(); continue; }
8899
5.95M
    if (sr.peek() == '#') { sr.skip_line(); continue; }
8900
8901
5.91M
    size_t line_num = sr.line_num();
8902
8903
    // vertex
8904
5.91M
    if (sr.peek() == 'v' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
8905
888k
      sr.advance(2);
8906
888k
      real_t x, y, z;
8907
888k
      real_t r, g, b;
8908
8909
888k
      int num_components = sr_parseVertexWithColor(&x, &y, &z, &r, &g, &b, sr, err, filename);
8910
888k
      if (num_components < 0) return false;
8911
888k
      found_all_colors &= (num_components == 6);
8912
8913
888k
      v.push_back(x);
8914
888k
      v.push_back(y);
8915
888k
      v.push_back(z);
8916
8917
888k
      vertex_weights.push_back(r);
8918
8919
888k
      if ((num_components == 6) || default_vcols_fallback) {
8920
888k
        vc.push_back(r);
8921
888k
        vc.push_back(g);
8922
888k
        vc.push_back(b);
8923
888k
      }
8924
8925
888k
      sr.skip_line();
8926
888k
      continue;
8927
888k
    }
8928
8929
    // normal
8930
5.02M
    if (sr.peek() == 'v' && sr.peek_at(1) == 'n' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8931
516k
      sr.advance(3);
8932
516k
      real_t x, y, z;
8933
516k
      if (!sr_parseReal3(&x, &y, &z, sr, err, filename)) return false;
8934
516k
      vn.push_back(x);
8935
516k
      vn.push_back(y);
8936
516k
      vn.push_back(z);
8937
516k
      sr.skip_line();
8938
516k
      continue;
8939
516k
    }
8940
8941
    // texcoord
8942
4.50M
    if (sr.peek() == 'v' && sr.peek_at(1) == 't' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8943
359k
      sr.advance(3);
8944
359k
      real_t x, y;
8945
359k
      if (!sr_parseReal2(&x, &y, sr, err, filename)) return false;
8946
359k
      vt.push_back(x);
8947
359k
      vt.push_back(y);
8948
8949
      // Parse optional w component
8950
359k
      real_t w = static_cast<real_t>(0.0);
8951
359k
      sr_parseReal(sr, &w);
8952
359k
      vt_w.push_back(w);
8953
8954
359k
      sr.skip_line();
8955
359k
      continue;
8956
359k
    }
8957
8958
    // skin weight. tinyobj extension
8959
4.14M
    if (sr.peek() == 'v' && sr.peek_at(1) == 'w' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
8960
79.5k
      sr.advance(3);
8961
8962
79.5k
      int vid;
8963
79.5k
      if (!sr_parseInt(sr, &vid, err, filename)) return false;
8964
8965
79.3k
      skin_weight_t sw;
8966
79.3k
      sw.vertex_id = vid;
8967
8968
79.3k
      size_t vw_loop_max = sr.remaining() + 1;
8969
79.3k
      size_t vw_loop_iter = 0;
8970
700k
      while (!sr.at_line_end() && sr.peek() != '#' &&
8971
621k
             vw_loop_iter < vw_loop_max) {
8972
621k
        real_t j, w;
8973
621k
        sr_parseReal2(&j, &w, sr, -1.0);
8974
8975
621k
        if (j < static_cast<real_t>(0)) {
8976
125
          if (err) {
8977
125
            (*err) += sr.format_error(filename,
8978
125
                "failed to parse `vw' line: joint_id is negative");
8979
125
          }
8980
125
          return false;
8981
125
        }
8982
8983
        // Clamp to int range to avoid UB on float-to-int overflow.
8984
621k
        if (j > static_cast<real_t>(std::numeric_limits<int>::max())) {
8985
18
          if (err) {
8986
18
            (*err) += sr.format_error(filename,
8987
18
                "failed to parse `vw' line: joint_id overflow");
8988
18
          }
8989
18
          return false;
8990
18
        }
8991
621k
        joint_and_weight_t jw;
8992
621k
        jw.joint_id = static_cast<int>(j);
8993
621k
        jw.weight = w;
8994
8995
621k
        sw.weightValues.push_back(jw);
8996
621k
        sr.skip_space_and_cr();
8997
621k
        vw_loop_iter++;
8998
621k
      }
8999
9000
79.2k
      vw.push_back(sw);
9001
79.2k
      sr.skip_line();
9002
79.2k
      continue;
9003
79.3k
    }
9004
9005
4.06M
    context.line_number = line_num;
9006
9007
    // line
9008
4.06M
    if (sr.peek() == 'l' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9009
526k
      sr.advance(2);
9010
9011
526k
      __line_t line;
9012
9013
526k
      size_t l_loop_max = sr.remaining() + 1;
9014
526k
      size_t l_loop_iter = 0;
9015
610k
      while (!sr.at_line_end() && sr.peek() != '#' &&
9016
84.2k
             l_loop_iter < l_loop_max) {
9017
84.2k
        vertex_index_t vi;
9018
84.2k
        if (!sr_parseTriple(sr, size_to_int(v.size() / 3),
9019
84.2k
                         size_to_int(vn.size() / 3),
9020
84.2k
                         size_to_int(vt.size() / 2), &vi, context)) {
9021
108
          if (err) {
9022
108
            (*err) += sr.format_error(filename,
9023
108
                "failed to parse `l' line (invalid vertex index)");
9024
108
          }
9025
108
          return false;
9026
108
        }
9027
9028
84.0k
        line.vertex_indices.push_back(vi);
9029
84.0k
        sr.skip_space_and_cr();
9030
84.0k
        l_loop_iter++;
9031
84.0k
      }
9032
9033
526k
      prim_group.lineGroup.push_back(line);
9034
526k
      sr.skip_line();
9035
526k
      continue;
9036
526k
    }
9037
9038
    // points
9039
3.54M
    if (sr.peek() == 'p' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9040
515k
      sr.advance(2);
9041
9042
515k
      __points_t pts;
9043
9044
515k
      size_t p_loop_max = sr.remaining() + 1;
9045
515k
      size_t p_loop_iter = 0;
9046
3.13M
      while (!sr.at_line_end() && sr.peek() != '#' &&
9047
2.62M
             p_loop_iter < p_loop_max) {
9048
2.62M
        vertex_index_t vi;
9049
2.62M
        if (!sr_parseTriple(sr, size_to_int(v.size() / 3),
9050
2.62M
                         size_to_int(vn.size() / 3),
9051
2.62M
                         size_to_int(vt.size() / 2), &vi, context)) {
9052
97
          if (err) {
9053
97
            (*err) += sr.format_error(filename,
9054
97
                "failed to parse `p' line (invalid vertex index)");
9055
97
          }
9056
97
          return false;
9057
97
        }
9058
9059
2.62M
        pts.vertex_indices.push_back(vi);
9060
2.62M
        sr.skip_space_and_cr();
9061
2.62M
        p_loop_iter++;
9062
2.62M
      }
9063
9064
515k
      prim_group.pointsGroup.push_back(pts);
9065
515k
      sr.skip_line();
9066
515k
      continue;
9067
515k
    }
9068
9069
    // face
9070
3.02M
    if (sr.peek() == 'f' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9071
529k
      sr.advance(2);
9072
529k
      sr.skip_space();
9073
9074
529k
      face_t face;
9075
9076
529k
      face.smoothing_group_id = current_smoothing_id;
9077
529k
      face.vertex_indices.reserve(3);
9078
9079
529k
      size_t f_loop_max = sr.remaining() + 1;
9080
529k
      size_t f_loop_iter = 0;
9081
2.79M
      while (!sr.at_line_end() && sr.peek() != '#' &&
9082
2.26M
             f_loop_iter < f_loop_max) {
9083
2.26M
        vertex_index_t vi;
9084
2.26M
        if (!sr_parseTriple(sr, size_to_int(v.size() / 3),
9085
2.26M
                         size_to_int(vn.size() / 3),
9086
2.26M
                         size_to_int(vt.size() / 2), &vi, context)) {
9087
232
          if (err) {
9088
232
            (*err) += sr.format_error(filename,
9089
232
                "failed to parse `f' line (invalid vertex index)");
9090
232
          }
9091
232
          return false;
9092
232
        }
9093
9094
2.26M
        greatest_v_idx = greatest_v_idx > vi.v_idx ? greatest_v_idx : vi.v_idx;
9095
2.26M
        greatest_vn_idx =
9096
2.26M
            greatest_vn_idx > vi.vn_idx ? greatest_vn_idx : vi.vn_idx;
9097
2.26M
        greatest_vt_idx =
9098
2.26M
            greatest_vt_idx > vi.vt_idx ? greatest_vt_idx : vi.vt_idx;
9099
9100
2.26M
        face.vertex_indices.push_back(vi);
9101
2.26M
        sr.skip_space_and_cr();
9102
2.26M
        f_loop_iter++;
9103
2.26M
      }
9104
9105
529k
      prim_group.faceGroup.push_back(face);
9106
529k
      sr.skip_line();
9107
529k
      continue;
9108
529k
    }
9109
9110
    // use mtl
9111
2.49M
    if (sr.match("usemtl", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
9112
62.0k
      sr.advance(6);
9113
62.0k
      std::string namebuf = sr_parseString(sr);
9114
9115
62.0k
      int newMaterialId = -1;
9116
62.0k
      std::map<std::string, int>::const_iterator it =
9117
62.0k
          material_map.find(namebuf);
9118
62.0k
      if (it != material_map.end()) {
9119
18.3k
        newMaterialId = it->second;
9120
43.6k
      } else {
9121
43.6k
        if (warn) {
9122
43.6k
          (*warn) += "material [ '" + namebuf + "' ] not found in .mtl\n";
9123
43.6k
        }
9124
43.6k
      }
9125
9126
62.0k
      if (newMaterialId != material) {
9127
26.2k
        exportGroupsToShape(&shape, prim_group, tags, material, name,
9128
26.2k
                            triangulate, v, warn);
9129
26.2k
        prim_group.faceGroup.clear();
9130
26.2k
        material = newMaterialId;
9131
26.2k
      }
9132
9133
62.0k
      sr.skip_line();
9134
62.0k
      continue;
9135
62.0k
    }
9136
9137
    // load mtl
9138
2.43M
    if (sr.match("mtllib", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
9139
61.0k
      if (readMatFn) {
9140
61.0k
        sr.advance(7);
9141
9142
61.0k
        std::string line_rest = trimTrailingWhitespace(sr.read_line());
9143
61.0k
        std::vector<std::string> filenames;
9144
61.0k
        SplitString(line_rest, ' ', '\\', filenames);
9145
61.0k
        RemoveEmptyTokens(&filenames);
9146
9147
61.0k
        if (filenames.empty()) {
9148
7.43k
          if (warn) {
9149
7.43k
            std::stringstream ss;
9150
7.43k
            ss << "Looks like empty filename for mtllib. Use default "
9151
7.43k
                  "material (line "
9152
7.43k
               << line_num << ".)\n";
9153
9154
7.43k
            (*warn) += ss.str();
9155
7.43k
          }
9156
53.6k
        } else {
9157
53.6k
          bool found = false;
9158
157k
          for (size_t s = 0; s < filenames.size(); s++) {
9159
115k
            if (material_filenames.count(filenames[s]) > 0) {
9160
103k
              found = true;
9161
103k
              continue;
9162
103k
            }
9163
9164
11.8k
            std::string warn_mtl;
9165
11.8k
            std::string err_mtl;
9166
11.8k
            bool ok = (*readMatFn)(filenames[s].c_str(), materials,
9167
11.8k
                                   &material_map, &warn_mtl, &err_mtl);
9168
11.8k
            if (warn && (!warn_mtl.empty())) {
9169
254
              (*warn) += warn_mtl;
9170
254
            }
9171
9172
11.8k
            if (err && (!err_mtl.empty())) {
9173
276
              (*err) += err_mtl;
9174
276
            }
9175
9176
11.8k
            if (ok) {
9177
11.6k
              found = true;
9178
11.6k
              material_filenames.insert(filenames[s]);
9179
11.6k
              break;
9180
11.6k
            }
9181
11.8k
          }
9182
9183
53.6k
          if (!found) {
9184
246
            if (warn) {
9185
246
              (*warn) +=
9186
246
                  "Failed to load material file(s). Use default "
9187
246
                  "material.\n";
9188
246
            }
9189
246
          }
9190
53.6k
        }
9191
61.0k
      }
9192
9193
61.0k
      sr.skip_line();
9194
61.0k
      continue;
9195
61.0k
    }
9196
9197
    // group name
9198
2.37M
    if (sr.peek() == 'g' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9199
      // flush previous face group.
9200
85.8k
      bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name,
9201
85.8k
                                     triangulate, v, warn);
9202
85.8k
      (void)ret;
9203
9204
85.8k
      if (shape.mesh.indices.size() > 0) {
9205
3.82k
        shapes->push_back(shape);
9206
3.82k
      }
9207
9208
85.8k
      shape = shape_t();
9209
9210
      // material = -1;
9211
85.8k
      prim_group.clear();
9212
9213
85.8k
      std::vector<std::string> names;
9214
9215
85.8k
      size_t g_loop_max = sr.remaining() + 1;
9216
85.8k
      size_t g_loop_iter = 0;
9217
654k
      while (!sr.at_line_end() && sr.peek() != '#' &&
9218
568k
             g_loop_iter < g_loop_max) {
9219
568k
        std::string str = sr_parseString(sr);
9220
568k
        names.push_back(str);
9221
568k
        sr.skip_space_and_cr();
9222
568k
        g_loop_iter++;
9223
568k
      }
9224
9225
      // names[0] must be 'g'
9226
9227
85.8k
      if (names.size() < 2) {
9228
        // 'g' with empty names
9229
51.3k
        if (warn) {
9230
51.3k
          std::stringstream ss;
9231
51.3k
          ss << "Empty group name. line: " << line_num << "\n";
9232
51.3k
          (*warn) += ss.str();
9233
51.3k
          name = "";
9234
51.3k
        }
9235
51.3k
      } else {
9236
34.4k
        std::stringstream ss;
9237
34.4k
        ss << names[1];
9238
9239
483k
        for (size_t i = 2; i < names.size(); i++) {
9240
448k
          ss << " " << names[i];
9241
448k
        }
9242
9243
34.4k
        name = ss.str();
9244
34.4k
      }
9245
9246
85.8k
      sr.skip_line();
9247
85.8k
      continue;
9248
85.8k
    }
9249
9250
    // object name
9251
2.28M
    if (sr.peek() == 'o' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9252
      // flush previous face group.
9253
361k
      bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name,
9254
361k
                                     triangulate, v, warn);
9255
361k
      (void)ret;
9256
9257
361k
      if (shape.mesh.indices.size() > 0 || shape.lines.indices.size() > 0 ||
9258
351k
          shape.points.indices.size() > 0) {
9259
191k
        shapes->push_back(shape);
9260
191k
      }
9261
9262
      // material = -1;
9263
361k
      prim_group.clear();
9264
361k
      shape = shape_t();
9265
9266
361k
      sr.advance(2);
9267
361k
      std::string rest = sr.read_line();
9268
361k
      name = rest;
9269
9270
361k
      sr.skip_line();
9271
361k
      continue;
9272
361k
    }
9273
9274
1.92M
    if (sr.peek() == 't' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9275
903k
      const int max_tag_nums = 8192;
9276
903k
      tag_t tag;
9277
9278
903k
      sr.advance(2);
9279
9280
903k
      tag.name = sr_parseString(sr);
9281
9282
903k
      tag_sizes ts = sr_parseTagTriple(sr);
9283
9284
903k
      if (ts.num_ints < 0) {
9285
81.5k
        ts.num_ints = 0;
9286
81.5k
      }
9287
903k
      if (ts.num_ints > max_tag_nums) {
9288
409
        ts.num_ints = max_tag_nums;
9289
409
      }
9290
9291
903k
      if (ts.num_reals < 0) {
9292
13.5k
        ts.num_reals = 0;
9293
13.5k
      }
9294
903k
      if (ts.num_reals > max_tag_nums) {
9295
1.60k
        ts.num_reals = max_tag_nums;
9296
1.60k
      }
9297
9298
903k
      if (ts.num_strings < 0) {
9299
523
        ts.num_strings = 0;
9300
523
      }
9301
903k
      if (ts.num_strings > max_tag_nums) {
9302
747
        ts.num_strings = max_tag_nums;
9303
747
      }
9304
9305
903k
      tag.intValues.resize(static_cast<size_t>(ts.num_ints));
9306
9307
7.09M
      for (size_t i = 0; i < static_cast<size_t>(ts.num_ints); ++i) {
9308
6.19M
        tag.intValues[i] = sr_parseInt(sr);
9309
6.19M
      }
9310
9311
903k
      tag.floatValues.resize(static_cast<size_t>(ts.num_reals));
9312
14.8M
      for (size_t i = 0; i < static_cast<size_t>(ts.num_reals); ++i) {
9313
13.9M
        tag.floatValues[i] = sr_parseReal(sr);
9314
13.9M
      }
9315
9316
903k
      tag.stringValues.resize(static_cast<size_t>(ts.num_strings));
9317
7.22M
      for (size_t i = 0; i < static_cast<size_t>(ts.num_strings); ++i) {
9318
6.32M
        tag.stringValues[i] = sr_parseString(sr);
9319
6.32M
      }
9320
9321
903k
      tags.push_back(tag);
9322
9323
903k
      sr.skip_line();
9324
903k
      continue;
9325
903k
    }
9326
9327
1.02M
    if (sr.peek() == 's' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9328
      // smoothing group id
9329
23.7k
      sr.advance(2);
9330
23.7k
      sr.skip_space();
9331
9332
23.7k
      if (sr.at_line_end()) {
9333
4.72k
        sr.skip_line();
9334
4.72k
        continue;
9335
4.72k
      }
9336
9337
19.0k
      if (sr.peek() == '\r') {
9338
0
        sr.skip_line();
9339
0
        continue;
9340
0
      }
9341
9342
19.0k
      if (sr.remaining() >= 3 && sr.match("off", 3)) {
9343
683
        current_smoothing_id = 0;
9344
18.3k
      } else {
9345
18.3k
        int smGroupId = sr_parseInt(sr);
9346
18.3k
        if (smGroupId < 0) {
9347
1.97k
          current_smoothing_id = 0;
9348
16.3k
        } else {
9349
16.3k
          current_smoothing_id = static_cast<unsigned int>(smGroupId);
9350
16.3k
        }
9351
18.3k
      }
9352
9353
19.0k
      sr.skip_line();
9354
19.0k
      continue;
9355
19.0k
    }
9356
9357
    // Ignore unknown command.
9358
999k
    sr.skip_line();
9359
999k
  }
9360
9361
  // not all vertices have colors, no default colors desired? -> clear colors
9362
10.3k
  if (!found_all_colors && !default_vcols_fallback) {
9363
0
    vc.clear();
9364
0
  }
9365
9366
10.3k
  if (greatest_v_idx >= size_to_int(v.size() / 3)) {
9367
1.06k
    if (warn) {
9368
1.06k
      std::stringstream ss;
9369
1.06k
      ss << "Vertex indices out of bounds (line " << sr.line_num() << ".)\n\n";
9370
1.06k
      (*warn) += ss.str();
9371
1.06k
    }
9372
1.06k
  }
9373
10.3k
  if (greatest_vn_idx >= size_to_int(vn.size() / 3)) {
9374
85
    if (warn) {
9375
85
      std::stringstream ss;
9376
85
      ss << "Vertex normal indices out of bounds (line " << sr.line_num()
9377
85
         << ".)\n\n";
9378
85
      (*warn) += ss.str();
9379
85
    }
9380
85
  }
9381
10.3k
  if (greatest_vt_idx >= size_to_int(vt.size() / 2)) {
9382
151
    if (warn) {
9383
151
      std::stringstream ss;
9384
151
      ss << "Vertex texcoord indices out of bounds (line " << sr.line_num()
9385
151
         << ".)\n\n";
9386
151
      (*warn) += ss.str();
9387
151
    }
9388
151
  }
9389
9390
10.3k
  bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name,
9391
10.3k
                                 triangulate, v, warn);
9392
10.3k
  if (ret || shape.mesh.indices.size()) {
9393
1.76k
    shapes->push_back(shape);
9394
1.76k
  }
9395
10.3k
  prim_group.clear();
9396
9397
10.3k
  attrib->vertices.swap(v);
9398
10.3k
  attrib->vertex_weights.swap(vertex_weights);
9399
10.3k
  attrib->normals.swap(vn);
9400
10.3k
  attrib->texcoords.swap(vt);
9401
10.3k
  attrib->texcoord_ws.swap(vt_w);
9402
10.3k
  attrib->colors.swap(vc);
9403
10.3k
  attrib->skin_weights.swap(vw);
9404
9405
10.3k
  return true;
9406
11.4k
}
9407
9408
bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
9409
             std::vector<material_t> *materials, std::string *warn,
9410
             std::string *err, const char *filename, const char *mtl_basedir,
9411
0
             bool triangulate, bool default_vcols_fallback) {
9412
0
  attrib->vertices.clear();
9413
0
  attrib->vertex_weights.clear();
9414
0
  attrib->normals.clear();
9415
0
  attrib->texcoords.clear();
9416
0
  attrib->texcoord_ws.clear();
9417
0
  attrib->colors.clear();
9418
0
  attrib->skin_weights.clear();
9419
0
  shapes->clear();
9420
9421
0
  std::string baseDir = mtl_basedir ? mtl_basedir : "";
9422
0
  if (!baseDir.empty()) {
9423
0
#ifndef _WIN32
9424
0
    const char dirsep = '/';
9425
#else
9426
    const char dirsep = '\\';
9427
#endif
9428
0
    if (baseDir[baseDir.length() - 1] != dirsep) baseDir += dirsep;
9429
0
  }
9430
0
  MaterialFileReader matFileReader(baseDir);
9431
9432
#ifdef TINYOBJLOADER_USE_MMAP
9433
  {
9434
    MappedFile mf;
9435
    if (!mf.open(filename)) {
9436
      if (err) {
9437
        std::stringstream ss;
9438
        ss << "Cannot open file [" << filename << "]\n";
9439
        (*err) = ss.str();
9440
      }
9441
      return false;
9442
    }
9443
    if (mf.size > TINYOBJLOADER_STREAM_READER_MAX_BYTES) {
9444
      if (err) {
9445
        std::stringstream ss;
9446
        ss << "input stream too large (" << mf.size
9447
           << " bytes exceeds limit "
9448
           << TINYOBJLOADER_STREAM_READER_MAX_BYTES << " bytes)\n";
9449
        (*err) += ss.str();
9450
      }
9451
      return false;
9452
    }
9453
    StreamReader sr(mf.data, mf.size);
9454
    return LoadObjInternal(attrib, shapes, materials, warn, err, sr,
9455
                           &matFileReader, triangulate, default_vcols_fallback,
9456
                           filename);
9457
  }
9458
#else   // !TINYOBJLOADER_USE_MMAP
9459
#ifdef _WIN32
9460
  std::ifstream ifs(LongPathW(UTF8ToWchar(filename)).c_str());
9461
#else
9462
0
  std::ifstream ifs(filename);
9463
0
#endif
9464
0
  if (!ifs) {
9465
0
    if (err) {
9466
0
      std::stringstream ss;
9467
0
      ss << "Cannot open file [" << filename << "]\n";
9468
0
      (*err) = ss.str();
9469
0
    }
9470
0
    return false;
9471
0
  }
9472
0
  {
9473
0
    StreamReader sr(ifs);
9474
0
    return LoadObjInternal(attrib, shapes, materials, warn, err, sr,
9475
0
                           &matFileReader, triangulate, default_vcols_fallback,
9476
0
                           filename);
9477
0
  }
9478
0
#endif  // TINYOBJLOADER_USE_MMAP
9479
0
}
9480
9481
bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
9482
             std::vector<material_t> *materials, std::string *warn,
9483
             std::string *err, std::istream *inStream,
9484
             MaterialReader *readMatFn /*= NULL*/, bool triangulate,
9485
11.4k
             bool default_vcols_fallback) {
9486
11.4k
  attrib->vertices.clear();
9487
11.4k
  attrib->vertex_weights.clear();
9488
11.4k
  attrib->normals.clear();
9489
11.4k
  attrib->texcoords.clear();
9490
11.4k
  attrib->texcoord_ws.clear();
9491
11.4k
  attrib->colors.clear();
9492
11.4k
  attrib->skin_weights.clear();
9493
11.4k
  shapes->clear();
9494
9495
11.4k
  StreamReader sr(*inStream);
9496
11.4k
  return LoadObjInternal(attrib, shapes, materials, warn, err, sr,
9497
11.4k
                         readMatFn, triangulate, default_vcols_fallback);
9498
11.4k
}
9499
9500
9501
static bool LoadObjWithCallbackInternal(StreamReader &sr,
9502
                                        const callback_t &callback,
9503
                                        void *user_data,
9504
                                        MaterialReader *readMatFn,
9505
                                        std::string *warn,
9506
0
                                        std::string *err) {
9507
0
  if (sr.has_errors()) {
9508
0
    if (err) {
9509
0
      (*err) += sr.get_errors();
9510
0
    }
9511
0
    return false;
9512
0
  }
9513
9514
  // material
9515
0
  std::set<std::string> material_filenames;
9516
0
  std::map<std::string, int> material_map;
9517
0
  int material_id = -1;
9518
9519
0
  std::vector<index_t> indices;
9520
0
  std::vector<material_t> materials;
9521
0
  std::vector<std::string> names;
9522
0
  names.reserve(2);
9523
0
  std::vector<const char *> names_out;
9524
9525
  // Handle BOM
9526
0
  if (sr.remaining() >= 3 &&
9527
0
      static_cast<unsigned char>(sr.peek()) == 0xEF &&
9528
0
      static_cast<unsigned char>(sr.peek_at(1)) == 0xBB &&
9529
0
      static_cast<unsigned char>(sr.peek_at(2)) == 0xBF) {
9530
0
    sr.advance(3);
9531
0
  }
9532
9533
0
  while (!sr.eof()) {
9534
0
    sr.skip_space();
9535
0
    if (sr.at_line_end()) { sr.skip_line(); continue; }
9536
0
    if (sr.peek() == '#') { sr.skip_line(); continue; }
9537
9538
    // vertex
9539
0
    if (sr.peek() == 'v' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9540
0
      sr.advance(2);
9541
0
      real_t x, y, z;
9542
0
      real_t r, g, b;
9543
9544
0
      int num_components = sr_parseVertexWithColor(&x, &y, &z, &r, &g, &b, sr, err, std::string());
9545
0
      if (num_components < 0) {
9546
0
        return false;
9547
0
      }
9548
0
      if (callback.vertex_cb) {
9549
0
        callback.vertex_cb(user_data, x, y, z, r);
9550
0
      }
9551
0
      if (callback.vertex_color_cb) {
9552
0
        bool found_color = (num_components == 6);
9553
0
        callback.vertex_color_cb(user_data, x, y, z, r, g, b, found_color);
9554
0
      }
9555
0
      sr.skip_line();
9556
0
      continue;
9557
0
    }
9558
9559
    // normal
9560
0
    if (sr.peek() == 'v' && sr.peek_at(1) == 'n' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
9561
0
      sr.advance(3);
9562
0
      real_t x, y, z;
9563
0
      sr_parseReal3(&x, &y, &z, sr);
9564
0
      if (callback.normal_cb) {
9565
0
        callback.normal_cb(user_data, x, y, z);
9566
0
      }
9567
0
      sr.skip_line();
9568
0
      continue;
9569
0
    }
9570
9571
    // texcoord
9572
0
    if (sr.peek() == 'v' && sr.peek_at(1) == 't' && (sr.peek_at(2) == ' ' || sr.peek_at(2) == '\t')) {
9573
0
      sr.advance(3);
9574
0
      real_t x, y, z;
9575
0
      sr_parseReal3(&x, &y, &z, sr);
9576
0
      if (callback.texcoord_cb) {
9577
0
        callback.texcoord_cb(user_data, x, y, z);
9578
0
      }
9579
0
      sr.skip_line();
9580
0
      continue;
9581
0
    }
9582
9583
    // face
9584
0
    if (sr.peek() == 'f' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9585
0
      sr.advance(2);
9586
0
      sr.skip_space();
9587
9588
0
      indices.clear();
9589
0
      size_t cf_loop_max = sr.remaining() + 1;
9590
0
      size_t cf_loop_iter = 0;
9591
0
      while (!sr.at_line_end() && sr.peek() != '#' &&
9592
0
             cf_loop_iter < cf_loop_max) {
9593
0
        vertex_index_t vi = sr_parseRawTriple(sr);
9594
9595
0
        index_t idx;
9596
0
        idx.vertex_index = vi.v_idx;
9597
0
        idx.normal_index = vi.vn_idx;
9598
0
        idx.texcoord_index = vi.vt_idx;
9599
9600
0
        indices.push_back(idx);
9601
0
        sr.skip_space_and_cr();
9602
0
        cf_loop_iter++;
9603
0
      }
9604
9605
0
      if (callback.index_cb && indices.size() > 0) {
9606
0
        callback.index_cb(user_data, &indices.at(0),
9607
0
                          static_cast<int>(indices.size()));
9608
0
      }
9609
9610
0
      sr.skip_line();
9611
0
      continue;
9612
0
    }
9613
9614
    // use mtl
9615
0
    if (sr.match("usemtl", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
9616
0
      sr.advance(6);
9617
0
      std::string namebuf = sr_parseString(sr);
9618
9619
0
      int newMaterialId = -1;
9620
0
      std::map<std::string, int>::const_iterator it =
9621
0
          material_map.find(namebuf);
9622
0
      if (it != material_map.end()) {
9623
0
        newMaterialId = it->second;
9624
0
      } else {
9625
0
        if (warn && (!callback.usemtl_cb)) {
9626
0
          (*warn) += "material [ " + namebuf + " ] not found in .mtl\n";
9627
0
        }
9628
0
      }
9629
9630
0
      if (newMaterialId != material_id) {
9631
0
        material_id = newMaterialId;
9632
0
      }
9633
9634
0
      if (callback.usemtl_cb) {
9635
0
        callback.usemtl_cb(user_data, namebuf.c_str(), material_id);
9636
0
      }
9637
9638
0
      sr.skip_line();
9639
0
      continue;
9640
0
    }
9641
9642
    // load mtl
9643
0
    if (sr.match("mtllib", 6) && (sr.peek_at(6) == ' ' || sr.peek_at(6) == '\t')) {
9644
0
      if (readMatFn) {
9645
0
        sr.advance(7);
9646
9647
0
        std::string line_rest = trimTrailingWhitespace(sr.read_line());
9648
0
        std::vector<std::string> filenames;
9649
0
        SplitString(line_rest, ' ', '\\', filenames);
9650
0
        RemoveEmptyTokens(&filenames);
9651
9652
0
        if (filenames.empty()) {
9653
0
          if (warn) {
9654
0
            (*warn) +=
9655
0
                "Looks like empty filename for mtllib. Use default "
9656
0
                "material. \n";
9657
0
          }
9658
0
        } else {
9659
0
          bool found = false;
9660
0
          for (size_t s = 0; s < filenames.size(); s++) {
9661
0
            if (material_filenames.count(filenames[s]) > 0) {
9662
0
              found = true;
9663
0
              continue;
9664
0
            }
9665
9666
0
            std::string warn_mtl;
9667
0
            std::string err_mtl;
9668
0
            bool ok = (*readMatFn)(filenames[s].c_str(), &materials,
9669
0
                                   &material_map, &warn_mtl, &err_mtl);
9670
9671
0
            if (warn && (!warn_mtl.empty())) {
9672
0
              (*warn) += warn_mtl;
9673
0
            }
9674
9675
0
            if (err && (!err_mtl.empty())) {
9676
0
              (*err) += err_mtl;
9677
0
            }
9678
9679
0
            if (ok) {
9680
0
              found = true;
9681
0
              material_filenames.insert(filenames[s]);
9682
0
              break;
9683
0
            }
9684
0
          }
9685
9686
0
          if (!found) {
9687
0
            if (warn) {
9688
0
              (*warn) +=
9689
0
                  "Failed to load material file(s). Use default "
9690
0
                  "material.\n";
9691
0
            }
9692
0
          } else {
9693
0
            if (callback.mtllib_cb && !materials.empty()) {
9694
0
              callback.mtllib_cb(user_data, &materials.at(0),
9695
0
                                 static_cast<int>(materials.size()));
9696
0
            }
9697
0
          }
9698
0
        }
9699
0
      }
9700
9701
0
      sr.skip_line();
9702
0
      continue;
9703
0
    }
9704
9705
    // group name
9706
0
    if (sr.peek() == 'g' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9707
0
      names.clear();
9708
9709
0
      size_t cg_loop_max = sr.remaining() + 1;
9710
0
      size_t cg_loop_iter = 0;
9711
0
      while (!sr.at_line_end() && sr.peek() != '#' &&
9712
0
             cg_loop_iter < cg_loop_max) {
9713
0
        std::string str = sr_parseString(sr);
9714
0
        names.push_back(str);
9715
0
        sr.skip_space_and_cr();
9716
0
        cg_loop_iter++;
9717
0
      }
9718
9719
0
      assert(names.size() > 0);
9720
9721
0
      if (callback.group_cb) {
9722
0
        if (names.size() > 1) {
9723
0
          names_out.resize(names.size() - 1);
9724
0
          for (size_t j = 0; j < names_out.size(); j++) {
9725
0
            names_out[j] = names[j + 1].c_str();
9726
0
          }
9727
0
          callback.group_cb(user_data, &names_out.at(0),
9728
0
                            static_cast<int>(names_out.size()));
9729
9730
0
        } else {
9731
0
          callback.group_cb(user_data, NULL, 0);
9732
0
        }
9733
0
      }
9734
9735
0
      sr.skip_line();
9736
0
      continue;
9737
0
    }
9738
9739
    // object name
9740
0
    if (sr.peek() == 'o' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9741
0
      sr.advance(2);
9742
0
      std::string object_name = sr.read_line();
9743
9744
0
      if (callback.object_cb) {
9745
0
        callback.object_cb(user_data, object_name.c_str());
9746
0
      }
9747
9748
0
      sr.skip_line();
9749
0
      continue;
9750
0
    }
9751
9752
#if 0  // @todo
9753
    if (sr.peek() == 't' && (sr.peek_at(1) == ' ' || sr.peek_at(1) == '\t')) {
9754
      tag_t tag;
9755
9756
      sr.advance(2);
9757
      tag.name = sr_parseString(sr);
9758
9759
      tag_sizes ts = sr_parseTagTriple(sr);
9760
9761
      tag.intValues.resize(static_cast<size_t>(ts.num_ints));
9762
9763
      for (size_t i = 0; i < static_cast<size_t>(ts.num_ints); ++i) {
9764
        tag.intValues[i] = sr_parseInt(sr);
9765
      }
9766
9767
      tag.floatValues.resize(static_cast<size_t>(ts.num_reals));
9768
      for (size_t i = 0; i < static_cast<size_t>(ts.num_reals); ++i) {
9769
        tag.floatValues[i] = sr_parseReal(sr);
9770
      }
9771
9772
      tag.stringValues.resize(static_cast<size_t>(ts.num_strings));
9773
      for (size_t i = 0; i < static_cast<size_t>(ts.num_strings); ++i) {
9774
        tag.stringValues[i] = sr_parseString(sr);
9775
      }
9776
9777
      tags.push_back(tag);
9778
    }
9779
#endif
9780
9781
    // Ignore unknown command.
9782
0
    sr.skip_line();
9783
0
  }
9784
9785
0
  return true;
9786
0
}
9787
9788
bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
9789
                         void *user_data /*= NULL*/,
9790
                         MaterialReader *readMatFn /*= NULL*/,
9791
                         std::string *warn, /* = NULL*/
9792
0
                         std::string *err /*= NULL*/) {
9793
0
  StreamReader sr(inStream);
9794
0
  return LoadObjWithCallbackInternal(sr, callback, user_data, readMatFn,
9795
0
                                     warn, err);
9796
0
}
9797
9798
bool ObjReader::ParseFromFile(const std::string &filename,
9799
0
                              const ObjReaderConfig &config) {
9800
0
  std::string mtl_search_path;
9801
9802
0
  if (config.mtl_search_path.empty()) {
9803
    //
9804
    // split at last '/'(for unixish system) or '\\'(for windows) to get
9805
    // the base directory of .obj file
9806
    //
9807
0
    size_t pos = filename.find_last_of("/\\");
9808
0
    if (pos != std::string::npos) {
9809
0
      mtl_search_path = filename.substr(0, pos);
9810
0
    }
9811
0
  } else {
9812
0
    mtl_search_path = config.mtl_search_path;
9813
0
  }
9814
9815
0
  valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_,
9816
0
                   filename.c_str(), mtl_search_path.c_str(),
9817
0
                   config.triangulate, config.vertex_color);
9818
9819
0
  return valid_;
9820
0
}
9821
9822
bool ObjReader::ParseFromString(const std::string &obj_text,
9823
                                const std::string &mtl_text,
9824
11.4k
                                const ObjReaderConfig &config) {
9825
11.4k
  std::stringbuf obj_buf(obj_text);
9826
11.4k
  std::stringbuf mtl_buf(mtl_text);
9827
9828
11.4k
  std::istream obj_ifs(&obj_buf);
9829
11.4k
  std::istream mtl_ifs(&mtl_buf);
9830
9831
11.4k
  MaterialStreamReader mtl_ss(mtl_ifs);
9832
9833
11.4k
  valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_,
9834
11.4k
                   &obj_ifs, &mtl_ss, config.triangulate, config.vertex_color);
9835
9836
11.4k
  return valid_;
9837
11.4k
}
9838
9839
// ===========================================================================
9840
// Optimized API implementation (C++11+)
9841
// ===========================================================================
9842
// ---- ArenaAllocator implementation ----
9843
9844
0
void *ArenaAllocator::allocate(size_t bytes, size_t alignment) {
9845
0
  if (bytes == 0) bytes = 1;
9846
9847
  // Try to allocate from current block
9848
0
  if (head_) {
9849
0
    size_t space = head_->capacity - head_->used;
9850
0
    void *ptr = head_->data + head_->used;
9851
0
    if (std::align(alignment, bytes, ptr, space)) {
9852
0
      head_->used = static_cast<size_t>(static_cast<unsigned char *>(ptr) -
9853
0
                                        head_->data) +
9854
0
                    bytes;
9855
0
      return ptr;
9856
0
    }
9857
0
  }
9858
9859
  // Guard against size_t overflow in bytes + alignment
9860
0
  if (bytes > SIZE_MAX - alignment) {
9861
#ifdef TINYOBJLOADER_ENABLE_EXCEPTION
9862
    throw std::bad_alloc();
9863
#else
9864
0
    return nullptr;
9865
0
#endif
9866
0
  }
9867
9868
  // Need a new block
9869
0
  Block *b = new_block(bytes + alignment);
9870
0
  if (!b) return nullptr;
9871
0
  size_t space = b->capacity;
9872
0
  void *ptr = b->data;
9873
0
  if (!std::align(alignment, bytes, ptr, space)) {
9874
    // Defensive guard: the block was allocated with capacity >= bytes + alignment,
9875
    // so alignment should always succeed.  This handles edge cases where the
9876
    // capacity was insufficient due to unusual alignment requirements.
9877
#ifdef TINYOBJLOADER_ENABLE_EXCEPTION
9878
    throw std::bad_alloc();
9879
#else
9880
0
    return nullptr;
9881
0
#endif
9882
0
  }
9883
0
  b->used =
9884
0
      static_cast<size_t>(static_cast<unsigned char *>(ptr) - b->data) + bytes;
9885
0
  return ptr;
9886
0
}
9887
9888
0
void ArenaAllocator::reset() { destroy(); }
9889
9890
0
ArenaAllocator::Block *ArenaAllocator::new_block(size_t min_bytes) {
9891
0
  size_t cap = (min_bytes > default_block_size_) ? min_bytes
9892
0
                                                 : default_block_size_;
9893
#ifdef TINYOBJLOADER_ENABLE_EXCEPTION
9894
  // Allocate data buffer first: if this throws std::bad_alloc, no Block
9895
  // struct is leaked.  If the subsequent Block allocation throws (very
9896
  // unlikely for a small POD), the data buffer is cleaned up.
9897
  unsigned char *data = new unsigned char[cap];
9898
  Block *b;
9899
  try {
9900
    b = new Block;
9901
  } catch (...) {
9902
    delete[] data;
9903
    throw;
9904
  }
9905
  b->data = data;
9906
#else
9907
0
  Block *b = new (std::nothrow) Block;
9908
0
  if (!b) return nullptr;
9909
0
  b->data = new (std::nothrow) unsigned char[cap];
9910
0
  if (!b->data) { delete b; return nullptr; }
9911
0
#endif
9912
0
  b->capacity = cap;
9913
0
  b->used = 0;
9914
0
  b->next = head_;
9915
0
  head_ = b;
9916
0
  return b;
9917
0
}
9918
9919
0
void ArenaAllocator::destroy() {
9920
0
  Block *b = head_;
9921
0
  while (b) {
9922
0
    Block *next = b->next;
9923
0
    delete[] b->data;
9924
0
    delete b;
9925
0
    b = next;
9926
0
  }
9927
0
  head_ = nullptr;
9928
0
}
9929
9930
// ---- Optimized parser internals ----
9931
9932
namespace opt_internal {
9933
9934
static const int kOptMaxThreads = 32;
9935
9936
struct LineInfo {
9937
  size_t pos;
9938
  size_t len;
9939
};
9940
9941
0
#define TINYOBJ_OPT_IS_SPACE(x) (((x) == ' ') || ((x) == '\t'))
9942
#define TINYOBJ_OPT_IS_DIGIT(x) \
9943
0
  (static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10))
9944
#define TINYOBJ_OPT_IS_NEW_LINE(x) \
9945
0
  (((x) == '\r') || ((x) == '\n') || ((x) == '\0'))
9946
9947
0
static inline void opt_skip_space(const char **token) {
9948
0
  while ((**token) == ' ' || (**token) == '\t') {
9949
0
    (*token)++;
9950
0
  }
9951
0
}
9952
9953
0
static inline int opt_until_space(const char *token) {
9954
0
  const char *p = token;
9955
0
  while (p[0] != '\0' && p[0] != ' ' && p[0] != '\t' && p[0] != '\r' &&
9956
0
         p[0] != '\n') {
9957
0
    p++;
9958
0
  }
9959
0
  return static_cast<int>(p - token);
9960
0
}
9961
9962
0
static inline int opt_my_atoi(const char *c) {
9963
0
  unsigned int value = 0;
9964
0
  int sign = 1;
9965
0
  if (*c == '+' || *c == '-') {
9966
0
    if (*c == '-') sign = -1;
9967
0
    c++;
9968
0
  }
9969
0
  while ((*c >= '0') && (*c <= '9')) {
9970
0
    const unsigned int digit = static_cast<unsigned int>(*c - '0');
9971
0
    const unsigned int limit = (sign < 0)
9972
0
                                   ? static_cast<unsigned int>(INT_MAX) + 1u
9973
0
                                   : static_cast<unsigned int>(INT_MAX);
9974
0
    if (value > (limit / 10u) ||
9975
0
        (value == (limit / 10u) && digit > (limit % 10u))) {
9976
0
      return (sign < 0) ? INT_MIN : INT_MAX;
9977
0
    }
9978
0
    value = value * 10u + digit;
9979
0
    c++;
9980
0
  }
9981
0
  if (sign < 0) {
9982
0
    if (value == static_cast<unsigned int>(INT_MAX) + 1u) {
9983
0
      return INT_MIN;
9984
0
    }
9985
0
    return -static_cast<int>(value);
9986
0
  }
9987
0
  return static_cast<int>(value);
9988
0
}
9989
9990
0
static inline const char *opt_find_index_token_end(const char *token) {
9991
0
  const char *end = token;
9992
0
  while (*end != '\0' && *end != '/' && *end != ' ' && *end != '\t' &&
9993
0
         *end != '\r' && *end != '\n') {
9994
0
    end++;
9995
0
  }
9996
0
  return end;
9997
0
}
9998
9999
static inline bool opt_tryParseIndexToken(const char *token, const char *end,
10000
0
                                          int *value) {
10001
0
  if (!value || !token || !end || token >= end) return false;
10002
10003
0
  const char *cursor = token;
10004
0
  if (*cursor == '+' || *cursor == '-') {
10005
0
    cursor++;
10006
0
  }
10007
0
  if (cursor >= end) return false;
10008
0
  while (cursor < end) {
10009
0
    if (!TINYOBJ_OPT_IS_DIGIT(*cursor)) return false;
10010
0
    cursor++;
10011
0
  }
10012
10013
0
  *value = opt_my_atoi(token);
10014
0
  return true;
10015
0
}
10016
10017
static inline bool opt_resolveIndexLikeLegacy(int idx, int n, int *ret,
10018
0
                                              bool allow_zero) {
10019
0
  if (!ret) return false;
10020
0
  if (idx > 0) {
10021
0
    (*ret) = idx - 1;
10022
0
    return true;
10023
0
  }
10024
0
  if (idx == 0) {
10025
0
    (*ret) = -1;
10026
0
    return allow_zero;
10027
0
  }
10028
10029
0
  (*ret) = n + idx;
10030
0
  return ((*ret) >= 0);
10031
0
}
10032
10033
static inline void opt_appendZeroIndexWarning(std::string *warn,
10034
                                              const std::string &source_name,
10035
0
                                              size_t line_num) {
10036
0
  if (!warn) return;
10037
10038
0
  std::stringstream ss;
10039
0
  ss << source_name << ":" << line_num
10040
0
     << ": warning: zero value index found (will have a value of -1 for "
10041
0
        "normal and tex indices)\n";
10042
0
  (*warn) += ss.str();
10043
0
}
10044
10045
static inline bool opt_validateAndResolveFaceIndexLikeLegacy(
10046
    int raw_idx, int n, bool allow_zero, const std::string &source_name,
10047
0
    size_t line_num, std::string *warn, int *resolved_idx) {
10048
0
  if (raw_idx > 0) {
10049
0
    if (resolved_idx) {
10050
0
      (*resolved_idx) = raw_idx - 1;
10051
0
    }
10052
0
    return true;
10053
0
  }
10054
10055
0
  if (raw_idx == 0) {
10056
0
    opt_appendZeroIndexWarning(warn, source_name, line_num);
10057
0
    if (resolved_idx) {
10058
0
      (*resolved_idx) = -1;
10059
0
    }
10060
0
    return allow_zero;
10061
0
  }
10062
10063
0
  if (resolved_idx) {
10064
0
    (*resolved_idx) = n + raw_idx;
10065
0
    return ((*resolved_idx) >= 0);
10066
0
  }
10067
10068
0
  return ((n + raw_idx) >= 0);
10069
0
}
10070
10071
0
static inline void opt_updateGreatestIndex(int idx, int *greatest) {
10072
0
  if (!greatest) return;
10073
0
  if (idx > *greatest) {
10074
0
    *greatest = idx;
10075
0
  }
10076
0
}
10077
10078
static inline void opt_appendOutOfBoundsWarnings(std::string *warn,
10079
                                                 int greatest_v_idx,
10080
                                                 int greatest_vn_idx,
10081
                                                 int greatest_vt_idx,
10082
                                                 int num_vertices,
10083
                                                 int num_normals,
10084
                                                 int num_texcoords,
10085
0
                                                 size_t line_num) {
10086
0
  if (!warn) return;
10087
10088
0
  if (greatest_v_idx >= num_vertices) {
10089
0
    std::stringstream ss;
10090
0
    ss << "Vertex indices out of bounds (line " << line_num << ".)\n\n";
10091
0
    (*warn) += ss.str();
10092
0
  }
10093
0
  if (greatest_vn_idx >= num_normals) {
10094
0
    std::stringstream ss;
10095
0
    ss << "Vertex normal indices out of bounds (line " << line_num << ".)\n\n";
10096
0
    (*warn) += ss.str();
10097
0
  }
10098
0
  if (greatest_vt_idx >= num_texcoords) {
10099
0
    std::stringstream ss;
10100
0
    ss << "Vertex texcoord indices out of bounds (line " << line_num
10101
0
       << ".)\n\n";
10102
0
    (*warn) += ss.str();
10103
0
  }
10104
0
}
10105
10106
// Hand-written fallback double parser.  Compiled only when fast_float is
10107
// disabled (TINYOBJLOADER_DISABLE_FAST_FLOAT); otherwise callers use
10108
// fast_float::from_chars directly and this function is not needed.
10109
#ifdef TINYOBJLOADER_DISABLE_FAST_FLOAT
10110
static bool opt_tryParseDouble(const char *s, const char *s_end,
10111
                               double *result) {
10112
  if (s >= s_end) return false;
10113
10114
  // Handle nan/inf keywords with OBJ-compatible replacement values.
10115
  {
10116
    const char *p = s;
10117
    bool neg = false;
10118
    if (p < s_end && *p == '-') { neg = true; ++p; }
10119
    else if (p < s_end && *p == '+') { ++p; }
10120
    if (p < s_end) {
10121
      char fc = *p;
10122
      if (fc >= 'A' && fc <= 'Z') fc += 32;
10123
      if (fc == 'n' && (p + 2 < s_end)) {
10124
        char c1 = p[1], c2 = p[2];
10125
        if (c1 >= 'A' && c1 <= 'Z') c1 += 32;
10126
        if (c2 >= 'A' && c2 <= 'Z') c2 += 32;
10127
        if (c1 == 'a' && c2 == 'n') {
10128
          *result = 0.0;
10129
          return true;
10130
        }
10131
      }
10132
      if (fc == 'i' && (p + 2 < s_end)) {
10133
        char c1 = p[1], c2 = p[2];
10134
        if (c1 >= 'A' && c1 <= 'Z') c1 += 32;
10135
        if (c2 >= 'A' && c2 <= 'Z') c2 += 32;
10136
        if (c1 == 'n' && c2 == 'f') {
10137
          *result = neg ? std::numeric_limits<double>::lowest()
10138
                        : (std::numeric_limits<double>::max)();
10139
          return true;
10140
        }
10141
      }
10142
    }
10143
  }
10144
10145
  double mantissa = 0.0;
10146
  int exponent = 0;
10147
  char sign = '+';
10148
  char exp_sign = '+';
10149
  const char *curr = s;
10150
  int read = 0;
10151
  bool end_not_reached = false;
10152
  bool has_leading_decimal = false;
10153
10154
  if (*curr == '+' || *curr == '-') {
10155
    sign = *curr;
10156
    curr++;
10157
  }
10158
10159
  if (curr == s_end) return false;
10160
10161
  if (*curr == '.') {
10162
    has_leading_decimal = true;
10163
  } else if (!TINYOBJ_OPT_IS_DIGIT(*curr)) {
10164
    return false;
10165
  }
10166
10167
  end_not_reached = (curr != s_end);
10168
  if (!has_leading_decimal) {
10169
    while (end_not_reached && TINYOBJ_OPT_IS_DIGIT(*curr)) {
10170
      mantissa *= 10;
10171
      mantissa += static_cast<int>(*curr - '0');
10172
      curr++;
10173
      read++;
10174
      end_not_reached = (curr != s_end);
10175
    }
10176
    if (read == 0) return false;
10177
  }
10178
  if (!end_not_reached) goto opt_assemble;
10179
10180
  if (*curr == '.') {
10181
    curr++;
10182
    end_not_reached = (curr != s_end);
10183
    double frac_scale = 0.1;
10184
    while (end_not_reached && TINYOBJ_OPT_IS_DIGIT(*curr)) {
10185
      mantissa += static_cast<int>(*curr - '0') * frac_scale;
10186
      frac_scale *= 0.1;
10187
      read++;
10188
      curr++;
10189
      end_not_reached = (curr != s_end);
10190
    }
10191
    if (has_leading_decimal && read == 0) return false;
10192
  } else if (*curr != 'e' && *curr != 'E') {
10193
    goto opt_assemble;
10194
  }
10195
10196
  if (!end_not_reached) goto opt_assemble;
10197
10198
  if (*curr == 'e' || *curr == 'E') {
10199
    curr++;
10200
    end_not_reached = (curr != s_end);
10201
    if (!end_not_reached) return false;
10202
    if (*curr == '+' || *curr == '-') {
10203
      exp_sign = *curr;
10204
      curr++;
10205
      end_not_reached = (curr != s_end);
10206
    } else if (!TINYOBJ_OPT_IS_DIGIT(*curr)) {
10207
      return false;
10208
    }
10209
    read = 0;
10210
    end_not_reached = (curr != s_end);
10211
    while (end_not_reached && TINYOBJ_OPT_IS_DIGIT(*curr)) {
10212
      // Clamp to avoid signed integer overflow (UB).  |exponent| > 308
10213
      // already exceeds double range, so further digits are irrelevant.
10214
      if (exponent < 0x7FFFFFF) {
10215
        exponent *= 10;
10216
        exponent += static_cast<int>(*curr - '0');
10217
      }
10218
      curr++;
10219
      read++;
10220
      end_not_reached = (curr != s_end);
10221
    }
10222
    exponent *= (exp_sign == '+' ? 1 : -1);
10223
    if (read == 0) return false;
10224
  }
10225
10226
opt_assemble:
10227
  *result = (sign == '+' ? 1.0 : -1.0) *
10228
            (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent)
10229
                      : mantissa);
10230
  return true;
10231
}
10232
#endif  // TINYOBJLOADER_DISABLE_FAST_FLOAT
10233
10234
struct opt_index_t {
10235
  int vertex_index, texcoord_index, normal_index;
10236
  // Sentinel for "field not present" to distinguish from OBJ relative index -1.
10237
  // Using expression form for C++11 static const initializer compatibility.
10238
  static const int kNotPresent = -2147483647 - 1;  // == std::numeric_limits<int>::min()
10239
  opt_index_t()
10240
0
      : vertex_index(kNotPresent),
10241
0
        texcoord_index(kNotPresent),
10242
0
        normal_index(kNotPresent) {}
10243
  opt_index_t(int vi, int ti, int ni)
10244
0
      : vertex_index(vi), texcoord_index(ti), normal_index(ni) {}
10245
};
10246
10247
0
static bool opt_parseRawTriple(const char **token, opt_index_t *ret) {
10248
0
  if (!token || !ret) return false;
10249
10250
0
  opt_index_t vi;
10251
0
  const char *segment_end = opt_find_index_token_end(*token);
10252
0
  if (!opt_tryParseIndexToken(*token, segment_end, &vi.vertex_index)) {
10253
0
    return false;
10254
0
  }
10255
0
  *token = segment_end;
10256
0
  if (**token != '/') {
10257
0
    *ret = vi;
10258
0
    return true;
10259
0
  }
10260
0
  (*token)++;
10261
10262
0
  if (**token == '/') {
10263
0
    (*token)++;
10264
0
    segment_end = opt_find_index_token_end(*token);
10265
0
    if (!opt_tryParseIndexToken(*token, segment_end, &vi.normal_index)) {
10266
0
      return false;
10267
0
    }
10268
0
    *token = segment_end;
10269
0
    *ret = vi;
10270
0
    return true;
10271
0
  }
10272
10273
0
  segment_end = opt_find_index_token_end(*token);
10274
0
  if (!opt_tryParseIndexToken(*token, segment_end, &vi.texcoord_index)) {
10275
0
    return false;
10276
0
  }
10277
0
  *token = segment_end;
10278
0
  if (**token != '/') {
10279
0
    *ret = vi;
10280
0
    return true;
10281
0
  }
10282
0
  (*token)++;
10283
0
  segment_end = opt_find_index_token_end(*token);
10284
0
  if (!opt_tryParseIndexToken(*token, segment_end, &vi.normal_index)) {
10285
0
    return false;
10286
0
  }
10287
0
  *token = segment_end;
10288
0
  *ret = vi;
10289
0
  return true;
10290
0
}
10291
10292
0
static inline int opt_length_until_newline(const char *token, size_t n) {
10293
0
  size_t len = 0;
10294
0
  for (len = 0; len < n; len++) {
10295
0
    if (token[len] == '\n' || token[len] == '\r') break;
10296
0
  }
10297
  // Trim trailing whitespace
10298
0
  while (len > 0 && (token[len - 1] == ' ' || token[len - 1] == '\t')) {
10299
0
    len--;
10300
0
  }
10301
0
  return static_cast<int>(len);
10302
0
}
10303
10304
0
static inline int opt_length_until_token_or_comment(const char *token, size_t n) {
10305
0
  size_t len = 0;
10306
0
  for (len = 0; len < n; len++) {
10307
0
    const char c = token[len];
10308
0
    if (c == '\n' || c == '\r' || c == ' ' || c == '\t') break;
10309
0
  }
10310
0
  return static_cast<int>(len);
10311
0
}
10312
10313
0
static inline std::string opt_parseGroupName(const char *token, size_t n) {
10314
0
  std::string name;
10315
0
  size_t i = 0;
10316
0
  while (i < n) {
10317
0
    while (i < n && (token[i] == ' ' || token[i] == '\t')) {
10318
0
      i++;
10319
0
    }
10320
0
    if (i >= n || token[i] == '\n' || token[i] == '\r' || token[i] == '\0' ||
10321
0
        token[i] == '#') {
10322
0
      break;
10323
0
    }
10324
10325
0
    const size_t start = i;
10326
0
    while (i < n && token[i] != '\n' && token[i] != '\r' &&
10327
0
           token[i] != '\0' && token[i] != ' ' && token[i] != '\t') {
10328
0
      i++;
10329
0
    }
10330
10331
0
    if (!name.empty()) {
10332
0
      name.push_back(' ');
10333
0
    }
10334
0
    name.append(token + start, i - start);
10335
0
  }
10336
0
  return name;
10337
0
}
10338
10339
0
static inline bool opt_tryParseFloatToken(real_t *out, const char **token) {
10340
0
  if (!out || !token) return false;
10341
0
  const char *cursor = *token;
10342
0
  opt_skip_space(&cursor);
10343
0
  if (TINYOBJ_OPT_IS_NEW_LINE(cursor[0]) || cursor[0] == '#' || cursor[0] == '\0') {
10344
0
    return false;
10345
0
  }
10346
0
  const char *end = cursor;
10347
0
  while (!TINYOBJ_OPT_IS_NEW_LINE(end[0]) && end[0] != '#' && end[0] != ' ' &&
10348
0
         end[0] != '\t' && end[0] != '\0') {
10349
0
    end++;
10350
0
  }
10351
0
#ifndef TINYOBJLOADER_DISABLE_FAST_FLOAT
10352
  // Handle nan/inf with OBJ-compatible values before fast_float.
10353
0
  {
10354
0
    const char *q = cursor;
10355
0
    if (q < end && (*q == '+' || *q == '-')) ++q;
10356
0
    if (q < end) {
10357
0
      char fc = *q;
10358
0
      if (fc >= 'A' && fc <= 'Z') fc += 32;
10359
0
      if (fc == 'n' || fc == 'i') {
10360
0
        double special_val;
10361
0
        const char *end_ptr;
10362
0
        if (detail_fp::tryParseNanInf(cursor, end, &special_val, &end_ptr)) {
10363
0
          *out = static_cast<real_t>(special_val);
10364
0
          *token = end;
10365
0
          return true;
10366
0
        }
10367
0
      }
10368
0
    }
10369
0
  }
10370
  // Parse directly to real_t (float or double) via fast_float — avoids
10371
  // the double→float conversion and is ~3-4x faster than the hand-rolled parser.
10372
0
  real_t tmp;
10373
0
  auto r = fast_float::from_chars(cursor, end, tmp,
10374
0
      fast_float::chars_format::general |
10375
0
      fast_float::chars_format::allow_leading_plus);
10376
0
  if (r.ec == tinyobj_ff::ff_errc::ok) {
10377
0
    *out = tmp;
10378
0
    *token = end;
10379
0
    return true;
10380
0
  }
10381
0
  return false;
10382
#else
10383
  double val = 0.0;
10384
  if (!opt_tryParseDouble(cursor, end, &val)) {
10385
    return false;
10386
  }
10387
  *out = static_cast<real_t>(val);
10388
  *token = end;
10389
  return true;
10390
#endif
10391
0
}
10392
10393
0
static inline int opt_count_remaining_scalars(const char *token) {
10394
0
  int count = 0;
10395
0
  const char *cursor = token;
10396
0
  while (true) {
10397
0
    opt_skip_space(&cursor);
10398
0
    if (TINYOBJ_OPT_IS_NEW_LINE(cursor[0]) || cursor[0] == '#' ||
10399
0
        cursor[0] == '\0') {
10400
0
      break;
10401
0
    }
10402
0
    count++;
10403
0
    while (!TINYOBJ_OPT_IS_NEW_LINE(cursor[0]) && cursor[0] != '#' &&
10404
0
           cursor[0] != ' ' && cursor[0] != '\t' && cursor[0] != '\0') {
10405
0
      cursor++;
10406
0
    }
10407
0
  }
10408
0
  return count;
10409
0
}
10410
10411
0
static inline bool opt_is_comment_start(const char *token) {
10412
0
  return token[0] == '#';
10413
0
}
10414
10415
enum OptCommandType {
10416
  OPT_CMD_EMPTY,
10417
  OPT_CMD_V,
10418
  OPT_CMD_VN,
10419
  OPT_CMD_VT,
10420
  OPT_CMD_F,
10421
  OPT_CMD_G,
10422
  OPT_CMD_O,
10423
  OPT_CMD_USEMTL,
10424
  OPT_CMD_MTLLIB,
10425
  OPT_CMD_S
10426
};
10427
10428
struct OptCommand {
10429
  static const unsigned int kInlineIndexCapacity = 24;
10430
10431
  real_t vx, vy, vz, vw;
10432
  real_t vc_r, vc_g, vc_b;
10433
  real_t nx, ny, nz;
10434
  real_t tx, ty, tw;
10435
  bool has_vertex_weight;
10436
  bool has_vertex_color;
10437
  bool has_texcoord_w;
10438
10439
  opt_index_t f_inline[kInlineIndexCapacity];
10440
  unsigned int f_count;
10441
  unsigned int face_vertex_count;
10442
  unsigned int emitted_face_count;
10443
  unsigned int emitted_face_verts;
10444
  std::vector<opt_index_t> f_heap;
10445
10446
  const char *group_name;
10447
  unsigned int group_name_len;
10448
  std::string group_name_storage;
10449
  const char *object_name;
10450
  unsigned int object_name_len;
10451
  const char *material_name;
10452
  unsigned int material_name_len;
10453
  const char *mtllib_name;
10454
  unsigned int mtllib_name_len;
10455
  size_t source_line;
10456
  bool group_name_empty;
10457
  bool degenerate_face;
10458
  int resolved_material_id;
10459
  unsigned int smoothing_group_id;
10460
10461
  OptCommandType type;
10462
10463
  OptCommand()
10464
0
      : vx(0), vy(0), vz(0), vw(1),
10465
0
        vc_r(1), vc_g(1), vc_b(1),
10466
0
        nx(0), ny(0), nz(0),
10467
0
        tx(0), ty(0), tw(0),
10468
0
        has_vertex_weight(false), has_vertex_color(false),
10469
0
        has_texcoord_w(false),
10470
0
        f_count(0), face_vertex_count(0), emitted_face_count(0),
10471
0
        emitted_face_verts(0),
10472
0
        group_name(nullptr), group_name_len(0),
10473
0
        object_name(nullptr), object_name_len(0),
10474
0
        material_name(nullptr), material_name_len(0),
10475
0
        mtllib_name(nullptr), mtllib_name_len(0),
10476
0
        source_line(0), group_name_empty(false),
10477
0
        degenerate_face(false),
10478
0
        resolved_material_id(-1),
10479
0
        smoothing_group_id(0),
10480
0
        type(OPT_CMD_EMPTY) {}
10481
10482
0
  const opt_index_t *face_indices() const {
10483
0
    return f_heap.empty() ? f_inline : f_heap.data();
10484
0
  }
10485
};
10486
10487
struct OptCommandCount {
10488
  size_t num_v, num_vn, num_vt, num_f, num_indices;
10489
0
  OptCommandCount() : num_v(0), num_vn(0), num_vt(0), num_f(0), num_indices(0) {}
10490
};
10491
10492
// Compact face command — replaces OptCommand for 'f' lines
10493
struct OptFaceCmd {
10494
  static const unsigned int kInlineCap = 4;
10495
  opt_index_t f_inline[kInlineCap];  // 48 bytes (covers tri + quad)
10496
  std::vector<opt_index_t> f_heap;   // overflow for >4 vertex faces
10497
  uint32_t face_vertex_count;
10498
  uint32_t emitted_face_count;
10499
  uint32_t emitted_face_verts;
10500
  uint32_t f_count;
10501
  uint32_t source_line;
10502
  uint32_t v_count_before;   // running v count when face was parsed (within thread)
10503
  uint32_t vn_count_before;
10504
  uint32_t vt_count_before;
10505
  bool degenerate;
10506
10507
0
  const opt_index_t *face_indices() const {
10508
0
    return f_heap.empty() ? f_inline : f_heap.data();
10509
0
  }
10510
10511
  OptFaceCmd()
10512
0
      : face_vertex_count(0), emitted_face_count(0), emitted_face_verts(0),
10513
0
        f_count(0), source_line(0), v_count_before(0), vn_count_before(0),
10514
0
        vt_count_before(0), degenerate(false) {}
10515
};
10516
10517
// Meta command — for g, o, usemtl, mtllib, s lines
10518
struct OptMetaCmd {
10519
  OptCommandType type;
10520
  uint32_t source_line;
10521
  const char *str_ptr;
10522
  uint32_t str_len;
10523
  std::string str_storage;
10524
  bool group_name_empty;
10525
  int resolved_material_id;
10526
  uint32_t smoothing_group_id;
10527
10528
  OptMetaCmd()
10529
0
      : type(OPT_CMD_EMPTY), source_line(0), str_ptr(NULL), str_len(0),
10530
0
        group_name_empty(false), resolved_material_id(-1),
10531
0
        smoothing_group_id(0) {}
10532
};
10533
10534
// Sequence entry — ordering of faces and meta commands within a thread
10535
struct OptSeqEntry {
10536
  enum Kind : unsigned char { SEQ_FACE = 0, SEQ_META = 1 };
10537
  Kind kind;
10538
  uint32_t index;
10539
};
10540
10541
// Per-thread parsed data — replaces vector<OptCommand>
10542
struct OptThreadData {
10543
  // Vertex positions: 3 floats per vertex, contiguous
10544
  std::vector<real_t> v_pos;
10545
  // Vertex weights: 1 per vertex, lazily allocated (only if any has weight)
10546
  std::vector<real_t> v_weight;
10547
  // Vertex colors: 3 per vertex, lazily allocated
10548
  std::vector<real_t> v_color;
10549
10550
  // Normal data: 3 floats per normal
10551
  std::vector<real_t> vn_data;
10552
10553
  // Texcoord data: 2 floats per texcoord
10554
  std::vector<real_t> vt_data;
10555
  // Texcoord w: 1 per texcoord, lazily allocated
10556
  std::vector<real_t> vt_w;
10557
10558
  // Non-vertex commands
10559
  std::vector<OptFaceCmd> faces;
10560
  std::vector<OptMetaCmd> metas;
10561
  std::vector<OptSeqEntry> seq;
10562
10563
  // Counts
10564
  size_t num_v, num_vn, num_vt;
10565
  size_t num_f_indices;   // total index_t entries for faces
10566
  size_t num_f_faces;     // total emitted faces
10567
10568
  // Flags
10569
  bool saw_any_color, saw_missing_color;
10570
  bool saw_any_weight;
10571
  bool saw_any_texcoord_w;
10572
  bool saw_any_smoothing;
10573
10574
  // Error tracking
10575
  size_t error_line;
10576
  std::string error_message;
10577
10578
  OptThreadData()
10579
0
      : num_v(0), num_vn(0), num_vt(0), num_f_indices(0), num_f_faces(0),
10580
0
        saw_any_color(false), saw_missing_color(false),
10581
0
        saw_any_weight(false), saw_any_texcoord_w(false),
10582
0
        saw_any_smoothing(false), error_line(0) {}
10583
};
10584
10585
static inline bool opt_is_valid_face_vertex(const std::vector<real_t> &vertices,
10586
0
                                            const index_t &idx) {
10587
0
  if (idx.vertex_index < 0) return false;
10588
0
  const size_t vi = static_cast<size_t>(idx.vertex_index);
10589
0
  return ((3 * vi + 2) < vertices.size());
10590
0
}
10591
10592
static inline size_t opt_triangulate_face(const std::vector<real_t> &vertices,
10593
                                          const index_t *face,
10594
                                          size_t face_count,
10595
0
                                          index_t *dst) {
10596
0
  if (face_count < 3) return 0;
10597
0
  if (face_count == 3) {
10598
0
    dst[0] = face[0];
10599
0
    dst[1] = face[1];
10600
0
    dst[2] = face[2];
10601
0
    return 3;
10602
0
  }
10603
10604
0
  for (size_t i = 0; i < face_count; i++) {
10605
0
    if (!opt_is_valid_face_vertex(vertices, face[i])) {
10606
0
      return 0;
10607
0
    }
10608
0
  }
10609
10610
0
  if (face_count == 4) {
10611
0
    const size_t vi0 = static_cast<size_t>(face[0].vertex_index);
10612
0
    const size_t vi1 = static_cast<size_t>(face[1].vertex_index);
10613
0
    const size_t vi2 = static_cast<size_t>(face[2].vertex_index);
10614
0
    const size_t vi3 = static_cast<size_t>(face[3].vertex_index);
10615
10616
0
    const real_t v0x = vertices[vi0 * 3 + 0];
10617
0
    const real_t v0y = vertices[vi0 * 3 + 1];
10618
0
    const real_t v0z = vertices[vi0 * 3 + 2];
10619
0
    const real_t v1x = vertices[vi1 * 3 + 0];
10620
0
    const real_t v1y = vertices[vi1 * 3 + 1];
10621
0
    const real_t v1z = vertices[vi1 * 3 + 2];
10622
0
    const real_t v2x = vertices[vi2 * 3 + 0];
10623
0
    const real_t v2y = vertices[vi2 * 3 + 1];
10624
0
    const real_t v2z = vertices[vi2 * 3 + 2];
10625
0
    const real_t v3x = vertices[vi3 * 3 + 0];
10626
0
    const real_t v3y = vertices[vi3 * 3 + 1];
10627
0
    const real_t v3z = vertices[vi3 * 3 + 2];
10628
10629
0
    const real_t e02x = v2x - v0x;
10630
0
    const real_t e02y = v2y - v0y;
10631
0
    const real_t e02z = v2z - v0z;
10632
0
    const real_t e13x = v3x - v1x;
10633
0
    const real_t e13y = v3y - v1y;
10634
0
    const real_t e13z = v3z - v1z;
10635
0
    const real_t sqr02 = e02x * e02x + e02y * e02y + e02z * e02z;
10636
0
    const real_t sqr13 = e13x * e13x + e13y * e13y + e13z * e13z;
10637
10638
0
    if (sqr02 < sqr13) {
10639
0
      dst[0] = face[0];
10640
0
      dst[1] = face[1];
10641
0
      dst[2] = face[2];
10642
0
      dst[3] = face[0];
10643
0
      dst[4] = face[2];
10644
0
      dst[5] = face[3];
10645
0
    } else {
10646
0
      dst[0] = face[0];
10647
0
      dst[1] = face[1];
10648
0
      dst[2] = face[3];
10649
0
      dst[3] = face[1];
10650
0
      dst[4] = face[2];
10651
0
      dst[5] = face[3];
10652
0
    }
10653
0
    return 6;
10654
0
  }
10655
10656
0
  std::vector<index_t> remaining(face, face + face_count);
10657
0
  size_t axes[2] = {1, 2};
10658
0
  for (size_t k = 0; k < face_count; ++k) {
10659
0
    const size_t vi0 = static_cast<size_t>(face[(k + 0) % face_count].vertex_index);
10660
0
    const size_t vi1 = static_cast<size_t>(face[(k + 1) % face_count].vertex_index);
10661
0
    const size_t vi2 = static_cast<size_t>(face[(k + 2) % face_count].vertex_index);
10662
0
    const real_t v0x = vertices[vi0 * 3 + 0];
10663
0
    const real_t v0y = vertices[vi0 * 3 + 1];
10664
0
    const real_t v0z = vertices[vi0 * 3 + 2];
10665
0
    const real_t v1x = vertices[vi1 * 3 + 0];
10666
0
    const real_t v1y = vertices[vi1 * 3 + 1];
10667
0
    const real_t v1z = vertices[vi1 * 3 + 2];
10668
0
    const real_t v2x = vertices[vi2 * 3 + 0];
10669
0
    const real_t v2y = vertices[vi2 * 3 + 1];
10670
0
    const real_t v2z = vertices[vi2 * 3 + 2];
10671
0
    const real_t e0x = v1x - v0x;
10672
0
    const real_t e0y = v1y - v0y;
10673
0
    const real_t e0z = v1z - v0z;
10674
0
    const real_t e1x = v2x - v1x;
10675
0
    const real_t e1y = v2y - v1y;
10676
0
    const real_t e1z = v2z - v1z;
10677
0
    const real_t cx = std::fabs(e0y * e1z - e0z * e1y);
10678
0
    const real_t cy = std::fabs(e0z * e1x - e0x * e1z);
10679
0
    const real_t cz = std::fabs(e0x * e1y - e0y * e1x);
10680
0
    const real_t epsilon = std::numeric_limits<real_t>::epsilon();
10681
0
    if (cx > epsilon || cy > epsilon || cz > epsilon) {
10682
0
      if (!(cx > cy && cx > cz)) {
10683
0
        axes[0] = 0;
10684
0
        if (cz > cx && cz > cy) {
10685
0
          axes[1] = 1;
10686
0
        }
10687
0
      }
10688
0
      break;
10689
0
    }
10690
0
  }
10691
10692
0
  size_t out = 0;
10693
0
  size_t guess_vert = 0;
10694
0
  size_t remaining_iterations = remaining.size();
10695
0
  size_t previous_remaining_vertices = remaining.size();
10696
0
  while (remaining.size() > 3 && remaining_iterations > 0) {
10697
0
    const size_t npolys = remaining.size();
10698
0
    if (guess_vert >= npolys) {
10699
0
      guess_vert -= npolys;
10700
0
    }
10701
10702
0
    if (previous_remaining_vertices != npolys) {
10703
0
      previous_remaining_vertices = npolys;
10704
0
      remaining_iterations = npolys;
10705
0
    } else {
10706
0
      remaining_iterations--;
10707
0
    }
10708
10709
0
    index_t ind[3];
10710
0
    real_t vx[3];
10711
0
    real_t vy[3];
10712
0
    for (size_t k = 0; k < 3; k++) {
10713
0
      ind[k] = remaining[(guess_vert + k) % npolys];
10714
0
      const size_t vi = static_cast<size_t>(ind[k].vertex_index);
10715
0
      vx[k] = vertices[vi * 3 + axes[0]];
10716
0
      vy[k] = vertices[vi * 3 + axes[1]];
10717
0
    }
10718
10719
0
    const real_t e0x = vx[1] - vx[0];
10720
0
    const real_t e0y = vy[1] - vy[0];
10721
0
    const real_t e1x = vx[2] - vx[1];
10722
0
    const real_t e1y = vy[2] - vy[1];
10723
0
    const real_t cross_val = e0x * e1y - e0y * e1x;
10724
0
    const real_t area =
10725
0
        (vx[0] * vy[1] - vy[0] * vx[1]) * static_cast<real_t>(0.5);
10726
0
    if (cross_val * area < static_cast<real_t>(0.0)) {
10727
0
      guess_vert += 1;
10728
0
      continue;
10729
0
    }
10730
10731
0
    bool overlap = false;
10732
0
    for (size_t other_vert = 3; other_vert < npolys; ++other_vert) {
10733
0
      const size_t idx = (guess_vert + other_vert) % npolys;
10734
0
      const size_t ovi = static_cast<size_t>(remaining[idx].vertex_index);
10735
0
      const real_t tx = vertices[ovi * 3 + axes[0]];
10736
0
      const real_t ty = vertices[ovi * 3 + axes[1]];
10737
0
      if (pnpoly(3, vx, vy, tx, ty)) {
10738
0
        overlap = true;
10739
0
        break;
10740
0
      }
10741
0
    }
10742
10743
0
    if (overlap) {
10744
0
      guess_vert += 1;
10745
0
      continue;
10746
0
    }
10747
10748
0
    dst[out++] = ind[0];
10749
0
    dst[out++] = ind[1];
10750
0
    dst[out++] = ind[2];
10751
0
    remaining.erase(remaining.begin() +
10752
0
                    static_cast<std::ptrdiff_t>((guess_vert + 1) % npolys));
10753
0
  }
10754
10755
0
  if (remaining.size() == 3) {
10756
0
    dst[out++] = remaining[0];
10757
0
    dst[out++] = remaining[1];
10758
0
    dst[out++] = remaining[2];
10759
0
  }
10760
10761
0
  return out;
10762
0
}
10763
10764
// Pointer+size overloads for TypedArray-based API
10765
static inline bool opt_is_valid_face_vertex(const real_t * /*vertices*/,
10766
                                            size_t vertices_size,
10767
0
                                            const index_t &idx) {
10768
0
  if (idx.vertex_index < 0) return false;
10769
0
  const size_t vi = static_cast<size_t>(idx.vertex_index);
10770
0
  return ((3 * vi + 2) < vertices_size);
10771
0
}
10772
10773
static inline size_t opt_triangulate_face(const real_t *vertices,
10774
                                          size_t vertices_size,
10775
                                          const index_t *face,
10776
                                          size_t face_count,
10777
0
                                          index_t *dst) {
10778
0
  if (face_count < 3) return 0;
10779
0
  if (face_count == 3) {
10780
0
    dst[0] = face[0]; dst[1] = face[1]; dst[2] = face[2];
10781
0
    return 3;
10782
0
  }
10783
10784
0
  for (size_t i = 0; i < face_count; i++) {
10785
0
    if (!opt_is_valid_face_vertex(vertices, vertices_size, face[i])) return 0;
10786
0
  }
10787
10788
0
  if (face_count == 4) {
10789
0
    const size_t vi0 = static_cast<size_t>(face[0].vertex_index);
10790
0
    const size_t vi1 = static_cast<size_t>(face[1].vertex_index);
10791
0
    const size_t vi2 = static_cast<size_t>(face[2].vertex_index);
10792
0
    const size_t vi3 = static_cast<size_t>(face[3].vertex_index);
10793
0
    const real_t e02x = vertices[vi2*3+0] - vertices[vi0*3+0];
10794
0
    const real_t e02y = vertices[vi2*3+1] - vertices[vi0*3+1];
10795
0
    const real_t e02z = vertices[vi2*3+2] - vertices[vi0*3+2];
10796
0
    const real_t e13x = vertices[vi3*3+0] - vertices[vi1*3+0];
10797
0
    const real_t e13y = vertices[vi3*3+1] - vertices[vi1*3+1];
10798
0
    const real_t e13z = vertices[vi3*3+2] - vertices[vi1*3+2];
10799
0
    const real_t sqr02 = e02x*e02x + e02y*e02y + e02z*e02z;
10800
0
    const real_t sqr13 = e13x*e13x + e13y*e13y + e13z*e13z;
10801
0
    if (sqr02 < sqr13) {
10802
0
      dst[0]=face[0]; dst[1]=face[1]; dst[2]=face[2];
10803
0
      dst[3]=face[0]; dst[4]=face[2]; dst[5]=face[3];
10804
0
    } else {
10805
0
      dst[0]=face[0]; dst[1]=face[1]; dst[2]=face[3];
10806
0
      dst[3]=face[1]; dst[4]=face[2]; dst[5]=face[3];
10807
0
    }
10808
0
    return 6;
10809
0
  }
10810
10811
  // General polygon — ear clipping
10812
0
  std::vector<index_t> remaining(face, face + face_count);
10813
0
  size_t axes[2] = {1, 2};
10814
0
  for (size_t k = 0; k < face_count; ++k) {
10815
0
    const size_t vi0 = static_cast<size_t>(face[(k+0)%face_count].vertex_index);
10816
0
    const size_t vi1 = static_cast<size_t>(face[(k+1)%face_count].vertex_index);
10817
0
    const size_t vi2 = static_cast<size_t>(face[(k+2)%face_count].vertex_index);
10818
0
    const real_t e0x = vertices[vi1*3+0]-vertices[vi0*3+0];
10819
0
    const real_t e0y = vertices[vi1*3+1]-vertices[vi0*3+1];
10820
0
    const real_t e0z = vertices[vi1*3+2]-vertices[vi0*3+2];
10821
0
    const real_t e1x = vertices[vi2*3+0]-vertices[vi1*3+0];
10822
0
    const real_t e1y = vertices[vi2*3+1]-vertices[vi1*3+1];
10823
0
    const real_t e1z = vertices[vi2*3+2]-vertices[vi1*3+2];
10824
0
    const real_t cx = std::fabs(e0y*e1z - e0z*e1y);
10825
0
    const real_t cy = std::fabs(e0z*e1x - e0x*e1z);
10826
0
    const real_t cz = std::fabs(e0x*e1y - e0y*e1x);
10827
0
    const real_t epsilon = std::numeric_limits<real_t>::epsilon();
10828
0
    if (cx > epsilon || cy > epsilon || cz > epsilon) {
10829
0
      if (!(cx > cy && cx > cz)) {
10830
0
        axes[0] = 0;
10831
0
        if (cz > cx && cz > cy) axes[1] = 1;
10832
0
      }
10833
0
      break;
10834
0
    }
10835
0
  }
10836
10837
0
  size_t out = 0;
10838
0
  size_t guess_vert = 0;
10839
0
  size_t remaining_iterations = remaining.size();
10840
0
  size_t previous_remaining_vertices = remaining.size();
10841
0
  while (remaining.size() > 3 && remaining_iterations > 0) {
10842
0
    const size_t npolys = remaining.size();
10843
0
    if (guess_vert >= npolys) guess_vert -= npolys;
10844
0
    if (previous_remaining_vertices != npolys) {
10845
0
      previous_remaining_vertices = npolys;
10846
0
      remaining_iterations = npolys;
10847
0
    } else {
10848
0
      remaining_iterations--;
10849
0
    }
10850
0
    index_t ind[3];
10851
0
    real_t vx[3], vy[3];
10852
0
    for (size_t k = 0; k < 3; k++) {
10853
0
      ind[k] = remaining[(guess_vert + k) % npolys];
10854
0
      const size_t vi = static_cast<size_t>(ind[k].vertex_index);
10855
0
      vx[k] = vertices[vi*3+axes[0]];
10856
0
      vy[k] = vertices[vi*3+axes[1]];
10857
0
    }
10858
0
    const real_t e0x_ = vx[1]-vx[0], e0y_ = vy[1]-vy[0];
10859
0
    const real_t e1x_ = vx[2]-vx[1], e1y_ = vy[2]-vy[1];
10860
0
    const real_t cross_val = e0x_*e1y_ - e0y_*e1x_;
10861
0
    const real_t area = (vx[0]*vy[1] - vy[0]*vx[1]) * static_cast<real_t>(0.5);
10862
0
    if (cross_val * area < static_cast<real_t>(0.0)) {
10863
0
      guess_vert += 1; continue;
10864
0
    }
10865
0
    bool overlap = false;
10866
0
    for (size_t other_vert = 3; other_vert < npolys; ++other_vert) {
10867
0
      const size_t idx = (guess_vert + other_vert) % npolys;
10868
0
      const size_t ovi = static_cast<size_t>(remaining[idx].vertex_index);
10869
0
      const real_t ptx = vertices[ovi*3+axes[0]];
10870
0
      const real_t pty = vertices[ovi*3+axes[1]];
10871
0
      if (pnpoly(3, vx, vy, ptx, pty)) { overlap = true; break; }
10872
0
    }
10873
0
    if (overlap) { guess_vert += 1; continue; }
10874
0
    dst[out++] = ind[0]; dst[out++] = ind[1]; dst[out++] = ind[2];
10875
0
    remaining.erase(remaining.begin() +
10876
0
                    static_cast<std::ptrdiff_t>((guess_vert + 1) % npolys));
10877
0
  }
10878
0
  if (remaining.size() == 3) {
10879
0
    dst[out++] = remaining[0]; dst[out++] = remaining[1]; dst[out++] = remaining[2];
10880
0
  }
10881
0
  return out;
10882
0
}
10883
10884
static bool opt_parseLine(OptCommand *command, const char *p, size_t p_len,
10885
                          bool triangulate, bool *parse_error,
10886
0
                          std::string *parse_error_message) {
10887
  // Parse directly from the original buffer without copying.
10888
  // The caller guarantees that p[p_len] is '\n' (or a sentinel),
10889
  // so character-scanning helpers that stop on '\n' are safe.
10890
0
  if (parse_error) *parse_error = false;
10891
0
  if (parse_error_message) parse_error_message->clear();
10892
0
  const char *token = p;
10893
0
  command->type = OPT_CMD_EMPTY;
10894
0
  opt_skip_space(&token);
10895
10896
0
  if (TINYOBJ_OPT_IS_NEW_LINE(token[0]) || token[0] == '#') return false;
10897
10898
  // vertex
10899
0
  if (token[0] == 'v' && TINYOBJ_OPT_IS_SPACE(token[1])) {
10900
0
    token += 2;
10901
0
    real_t x = 0, y = 0, z = 0;
10902
0
    real_t r = real_t(1.0), g = real_t(1.0), b = real_t(1.0);
10903
0
    real_t w = real_t(1.0);
10904
0
    if (!opt_tryParseFloatToken(&x, &token) ||
10905
0
        !opt_tryParseFloatToken(&y, &token) ||
10906
0
        !opt_tryParseFloatToken(&z, &token)) {
10907
0
      if (parse_error) *parse_error = true;
10908
0
      if (parse_error_message) *parse_error_message = "failed to parse `v' line";
10909
0
      return false;
10910
0
    }
10911
0
    const char *extra_token = token;
10912
0
    opt_skip_space(&extra_token);
10913
0
    const int extra_components = opt_count_remaining_scalars(extra_token);
10914
0
    if (extra_components == 1) {
10915
      // v x y z w  (4 total) — weight only
10916
0
      const char *weight_cursor = extra_token;
10917
0
      if (opt_tryParseFloatToken(&w, &weight_cursor)) {
10918
0
        command->has_vertex_weight = true;
10919
0
        command->vw = w;
10920
0
      }
10921
0
    } else if (extra_components == 3) {
10922
      // v x y z r g b  (6 total) — color, weight = r (legacy compat)
10923
0
      real_t cr = r, cg = g, cb = b;
10924
0
      const char *color_cursor = extra_token;
10925
0
      if (opt_tryParseFloatToken(&cr, &color_cursor) &&
10926
0
          opt_tryParseFloatToken(&cg, &color_cursor) &&
10927
0
          opt_tryParseFloatToken(&cb, &color_cursor)) {
10928
0
        command->has_vertex_weight = true;
10929
0
        command->vw = cr;
10930
0
        command->has_vertex_color = true;
10931
0
        command->vc_r = cr;
10932
0
        command->vc_g = cg;
10933
0
        command->vc_b = cb;
10934
0
      }
10935
0
    } else if (extra_components >= 4) {
10936
      // v x y z w r g b  (7+ total) — weight + color.
10937
      // NOTE: 7-component vertices are NOT part of the OBJ spec nor the common
10938
      // vertex-color extension, which define only xyz / xyzw / xyzrgb and treat
10939
      // weight and color as mutually exclusive.  tinyobjloader accepts this as
10940
      // an extension: the 4th value is the weight, the next three are RGB.
10941
      // (The classic LoadObj / LoadObjWithCallback path instead caps at 6 and
10942
      // ignores any extra tokens, so it differs from LoadObjOpt here.)
10943
0
      real_t vw = real_t(1.0), cr = r, cg = g, cb = b;
10944
0
      const char *wc_cursor = extra_token;
10945
0
      if (opt_tryParseFloatToken(&vw, &wc_cursor) &&
10946
0
          opt_tryParseFloatToken(&cr, &wc_cursor) &&
10947
0
          opt_tryParseFloatToken(&cg, &wc_cursor) &&
10948
0
          opt_tryParseFloatToken(&cb, &wc_cursor)) {
10949
0
        command->has_vertex_weight = true;
10950
0
        command->vw = vw;
10951
0
        command->has_vertex_color = true;
10952
0
        command->vc_r = cr;
10953
0
        command->vc_g = cg;
10954
0
        command->vc_b = cb;
10955
0
      }
10956
0
    }
10957
0
    command->vx = x;
10958
0
    command->vy = y;
10959
0
    command->vz = z;
10960
0
    command->type = OPT_CMD_V;
10961
0
    return true;
10962
0
  }
10963
10964
  // normal
10965
0
  if (token[0] == 'v' && token[1] == 'n' && TINYOBJ_OPT_IS_SPACE(token[2])) {
10966
0
    token += 3;
10967
0
    real_t x = 0, y = 0, z = 0;
10968
0
    if (!opt_tryParseFloatToken(&x, &token) ||
10969
0
        !opt_tryParseFloatToken(&y, &token) ||
10970
0
        !opt_tryParseFloatToken(&z, &token)) {
10971
0
      if (parse_error) *parse_error = true;
10972
0
      if (parse_error_message) *parse_error_message = "failed to parse `vn' line";
10973
0
      return false;
10974
0
    }
10975
0
    command->nx = x;
10976
0
    command->ny = y;
10977
0
    command->nz = z;
10978
0
    command->type = OPT_CMD_VN;
10979
0
    return true;
10980
0
  }
10981
10982
  // texcoord
10983
0
  if (token[0] == 'v' && token[1] == 't' && TINYOBJ_OPT_IS_SPACE(token[2])) {
10984
0
    token += 3;
10985
0
    real_t x = 0, y = 0, w = 0;
10986
0
    if (!opt_tryParseFloatToken(&x, &token)) {
10987
0
      if (parse_error) *parse_error = true;
10988
0
      if (parse_error_message) *parse_error_message = "failed to parse `vt' line";
10989
0
      return false;
10990
0
    }
10991
0
    const char *y_token = token;
10992
0
    if (opt_tryParseFloatToken(&y, &y_token)) {
10993
0
      token = y_token;
10994
0
    }
10995
0
    const char *extra_token = token;
10996
0
    opt_skip_space(&extra_token);
10997
0
    if (!TINYOBJ_OPT_IS_NEW_LINE(extra_token[0]) && extra_token[0] != '#' &&
10998
0
        opt_tryParseFloatToken(&w, &extra_token)) {
10999
0
      command->has_texcoord_w = true;
11000
0
      command->tw = w;
11001
0
    }
11002
0
    command->tx = x;
11003
0
    command->ty = y;
11004
0
    command->type = OPT_CMD_VT;
11005
0
    return true;
11006
0
  }
11007
11008
  // face
11009
0
  if (token[0] == 'f' && TINYOBJ_OPT_IS_SPACE(token[1])) {
11010
0
    token += 2;
11011
0
    opt_skip_space(&token);
11012
11013
    // Collect face vertices (use small stack buffer for typical case)
11014
0
    opt_index_t face_buf[8];
11015
0
    std::vector<opt_index_t> face_overflow;
11016
0
    int face_count = 0;
11017
11018
0
    while (!TINYOBJ_OPT_IS_NEW_LINE(token[0]) && !opt_is_comment_start(token)) {
11019
0
      opt_index_t vi;
11020
0
      if (!opt_parseRawTriple(&token, &vi)) {
11021
0
        if (parse_error) *parse_error = true;
11022
0
        if (parse_error_message) {
11023
0
          *parse_error_message = "failed to parse `f' line (invalid vertex index)";
11024
0
        }
11025
0
        return false;
11026
0
      }
11027
0
      opt_skip_space(&token);
11028
0
      if (face_count < 8) {
11029
0
        face_buf[face_count++] = vi;
11030
0
      } else {
11031
0
        if (face_count == 8) {
11032
0
          face_overflow.reserve(16);
11033
0
          for (int k = 0; k < 8; k++) face_overflow.push_back(face_buf[k]);
11034
0
        }
11035
0
        face_overflow.push_back(vi);
11036
0
        face_count++;
11037
0
      }
11038
0
    }
11039
11040
0
      command->type = OPT_CMD_F;
11041
0
      command->face_vertex_count = static_cast<unsigned int>(face_count);
11042
11043
    // Preserve degenerate faces so warnings and EOF shape behavior can match
11044
    // the legacy loader, but do not reserve output slots for them.
11045
0
    if (face_count < 3) {
11046
0
      command->degenerate_face = true;
11047
0
      command->emitted_face_count = 0;
11048
0
      command->emitted_face_verts = 0;
11049
0
      command->f_count = 0;
11050
0
      return true;
11051
0
    }
11052
11053
0
      if (triangulate) {
11054
0
        command->emitted_face_count = static_cast<unsigned int>(face_count - 2);
11055
0
        command->emitted_face_verts = 3;
11056
0
        command->f_count = command->emitted_face_count * 3;
11057
11058
0
      opt_index_t *dst = command->f_inline;
11059
0
      if (command->face_vertex_count > OptCommand::kInlineIndexCapacity) {
11060
0
        command->f_heap.resize(command->face_vertex_count);
11061
0
        dst = command->f_heap.data();
11062
0
      }
11063
0
      if (face_count <= 8) {
11064
0
        for (int k = 0; k < face_count; k++) dst[k] = face_buf[k];
11065
0
      } else {
11066
0
        for (size_t k = 0; k < face_overflow.size(); k++) dst[k] = face_overflow[k];
11067
0
      }
11068
0
    } else {
11069
0
      command->emitted_face_count = 1;
11070
0
      command->emitted_face_verts = static_cast<unsigned int>(face_count);
11071
0
      command->f_count = static_cast<unsigned int>(face_count);
11072
0
      command->face_vertex_count = static_cast<unsigned int>(face_count);
11073
11074
0
      opt_index_t *dst = command->f_inline;
11075
0
      if (command->f_count > OptCommand::kInlineIndexCapacity) {
11076
0
        command->f_heap.resize(command->f_count);
11077
0
        dst = command->f_heap.data();
11078
0
      }
11079
11080
0
      if (face_count <= 8) {
11081
0
        for (int k = 0; k < face_count; k++) dst[k] = face_buf[k];
11082
0
      } else {
11083
0
        for (size_t k = 0; k < face_overflow.size(); k++) {
11084
0
          dst[k] = face_overflow[k];
11085
0
        }
11086
0
      }
11087
0
    }
11088
0
    return true;
11089
0
  }
11090
11091
  // usemtl
11092
0
  if (std::strncmp(token, "usemtl", 6) == 0 &&
11093
0
      TINYOBJ_OPT_IS_SPACE(token[6])) {
11094
0
    token += 7;
11095
0
    opt_skip_space(&token);
11096
0
    command->material_name = token;
11097
0
    command->material_name_len = static_cast<unsigned int>(
11098
0
        opt_length_until_token_or_comment(token,
11099
0
                                p_len - static_cast<size_t>(token - p)));
11100
0
    command->type = OPT_CMD_USEMTL;
11101
0
    return true;
11102
0
  }
11103
11104
  // mtllib
11105
0
  if (std::strncmp(token, "mtllib", 6) == 0 &&
11106
0
      TINYOBJ_OPT_IS_SPACE(token[6])) {
11107
0
    token += 7;
11108
0
    opt_skip_space(&token);
11109
0
    command->mtllib_name = token;
11110
0
    command->mtllib_name_len = static_cast<unsigned int>(
11111
0
        opt_length_until_newline(token,
11112
0
                                p_len - static_cast<size_t>(token - p)));
11113
0
    command->type = OPT_CMD_MTLLIB;
11114
0
    return true;
11115
0
  }
11116
11117
  // group
11118
0
  if (token[0] == 'g' &&
11119
0
      (TINYOBJ_OPT_IS_SPACE(token[1]) || TINYOBJ_OPT_IS_NEW_LINE(token[1]) ||
11120
0
       token[1] == '\0')) {
11121
0
    if (TINYOBJ_OPT_IS_SPACE(token[1])) {
11122
0
      token += 2;
11123
0
    } else {
11124
0
      token += 1;
11125
0
    }
11126
0
    command->group_name_storage = opt_parseGroupName(
11127
0
        token, p_len - static_cast<size_t>(token - p));
11128
0
    command->group_name = nullptr;
11129
0
    command->group_name_len =
11130
0
        static_cast<unsigned int>(command->group_name_storage.size());
11131
0
    command->group_name_empty = command->group_name_storage.empty();
11132
0
    command->type = OPT_CMD_G;
11133
0
    return true;
11134
0
  }
11135
11136
  // object
11137
0
  if (token[0] == 'o' && TINYOBJ_OPT_IS_SPACE(token[1])) {
11138
0
    token += 2;
11139
0
    command->object_name = token;
11140
0
    command->object_name_len = static_cast<unsigned int>(
11141
0
        p_len - static_cast<size_t>(token - p));
11142
0
    command->type = OPT_CMD_O;
11143
0
    return true;
11144
0
  }
11145
11146
  // smoothing group
11147
0
  if (token[0] == 's' && TINYOBJ_OPT_IS_SPACE(token[1])) {
11148
0
    token += 2;
11149
0
    opt_skip_space(&token);
11150
0
    if (TINYOBJ_OPT_IS_NEW_LINE(token[0])) {
11151
0
      command->smoothing_group_id = 0;
11152
0
    } else if (token[0] == 'o' && token[1] == 'f' && token[2] == 'f' &&
11153
0
               (TINYOBJ_OPT_IS_NEW_LINE(token[3]) || token[3] == '\0' ||
11154
0
                token[3] == ' ' || token[3] == '\t')) {
11155
0
      command->smoothing_group_id = 0;
11156
0
    } else {
11157
0
      const int sm = opt_my_atoi(token);
11158
0
      command->smoothing_group_id = (sm > 0) ? static_cast<unsigned int>(sm) : 0;
11159
0
    }
11160
0
    command->type = OPT_CMD_S;
11161
0
    return true;
11162
0
  }
11163
11164
0
  return false;
11165
0
}
11166
11167
// ---- SIMD newline scanning ----
11168
11169
#ifdef TINYOBJLOADER_USE_SIMD
11170
11171
// Include <intrin.h> for _BitScanForward on MSVC. Placed here (not at the top)
11172
// because it's only needed when TINYOBJLOADER_USE_SIMD is enabled.
11173
#if defined(_MSC_VER)
11174
#include <intrin.h>
11175
#endif
11176
11177
// Portable count-trailing-zeros for SIMD bitmask extraction
11178
static inline unsigned int tinyobj_ctz(unsigned int x) {
11179
#if defined(_MSC_VER)
11180
  unsigned long idx;
11181
  _BitScanForward(&idx, x);
11182
  return static_cast<unsigned int>(idx);
11183
#else
11184
  return static_cast<unsigned int>(__builtin_ctz(x));
11185
#endif
11186
}
11187
11188
#if defined(TINYOBJLOADER_SIMD_AVX2)
11189
11190
/// AVX2-accelerated line-ending scanning — finds '\n' and '\r' positions.
11191
static void simd_find_newlines(const char *buf, size_t len,
11192
                               std::vector<size_t> &positions) {
11193
  const __m256i nl = _mm256_set1_epi8('\n');
11194
  const __m256i cr = _mm256_set1_epi8('\r');
11195
  size_t i = 0;
11196
  for (; i + 32 <= len; i += 32) {
11197
    __m256i chunk =
11198
        _mm256_loadu_si256(reinterpret_cast<const __m256i *>(buf + i));
11199
    __m256i cmp_nl = _mm256_cmpeq_epi8(chunk, nl);
11200
    __m256i cmp_cr = _mm256_cmpeq_epi8(chunk, cr);
11201
    __m256i combined = _mm256_or_si256(cmp_nl, cmp_cr);
11202
    unsigned int mask = static_cast<unsigned int>(_mm256_movemask_epi8(combined));
11203
    while (mask) {
11204
      unsigned int bit = tinyobj_ctz(mask);
11205
      positions.push_back(i + bit);
11206
      mask &= mask - 1;
11207
    }
11208
  }
11209
  // Scalar tail
11210
  for (; i < len; i++) {
11211
    if (buf[i] == '\n' || buf[i] == '\r') positions.push_back(i);
11212
  }
11213
}
11214
11215
#elif defined(TINYOBJLOADER_SIMD_SSE2)
11216
11217
/// SSE2-accelerated line-ending scanning — finds '\n' and '\r' positions.
11218
static void simd_find_newlines(const char *buf, size_t len,
11219
                               std::vector<size_t> &positions) {
11220
  const __m128i nl = _mm_set1_epi8('\n');
11221
  const __m128i cr = _mm_set1_epi8('\r');
11222
  size_t i = 0;
11223
  for (; i + 16 <= len; i += 16) {
11224
    __m128i chunk =
11225
        _mm_loadu_si128(reinterpret_cast<const __m128i *>(buf + i));
11226
    __m128i cmp_nl = _mm_cmpeq_epi8(chunk, nl);
11227
    __m128i cmp_cr = _mm_cmpeq_epi8(chunk, cr);
11228
    __m128i combined = _mm_or_si128(cmp_nl, cmp_cr);
11229
    int mask = _mm_movemask_epi8(combined);
11230
    while (mask) {
11231
      int bit = static_cast<int>(tinyobj_ctz(static_cast<unsigned int>(mask)));
11232
      positions.push_back(i + static_cast<size_t>(bit));
11233
      mask &= mask - 1;
11234
    }
11235
  }
11236
  // Scalar tail
11237
  for (; i < len; i++) {
11238
    if (buf[i] == '\n' || buf[i] == '\r') positions.push_back(i);
11239
  }
11240
}
11241
11242
#elif defined(TINYOBJLOADER_SIMD_NEON)
11243
11244
/// NEON-accelerated line-ending scanning — finds '\n' and '\r' positions.
11245
static void simd_find_newlines(const char *buf, size_t len,
11246
                               std::vector<size_t> &positions) {
11247
  const uint8x16_t nl = vdupq_n_u8('\n');
11248
  const uint8x16_t cr = vdupq_n_u8('\r');
11249
  size_t i = 0;
11250
  for (; i + 16 <= len; i += 16) {
11251
    uint8x16_t chunk = vld1q_u8(reinterpret_cast<const uint8_t *>(buf + i));
11252
    uint8x16_t cmp_nl = vceqq_u8(chunk, nl);
11253
    uint8x16_t cmp_cr = vceqq_u8(chunk, cr);
11254
    uint8x16_t combined = vorrq_u8(cmp_nl, cmp_cr);
11255
    for (int j = 0; j < 16; j++) {
11256
      if (vgetq_lane_u8(combined, 0) != 0) {
11257
        positions.push_back(i + static_cast<size_t>(j));
11258
      }
11259
      combined = vextq_u8(combined, combined, 1);
11260
    }
11261
  }
11262
  for (; i < len; i++) {
11263
    if (buf[i] == '\n' || buf[i] == '\r') positions.push_back(i);
11264
  }
11265
}
11266
11267
#endif  // SIMD variant
11268
11269
/// Build LineInfo array from SIMD-detected line-ending positions.
11270
/// The positions array may contain both '\r' and '\n' hits;
11271
/// consecutive \r\n pairs are collapsed into a single line boundary.
11272
static void simd_build_line_infos(const char *buf, size_t len,
11273
                                  const std::vector<size_t> &nl_positions,
11274
                                  std::vector<LineInfo> &out) {
11275
  out.reserve(nl_positions.size() + 1);
11276
  size_t prev = 0;
11277
  for (size_t k = 0; k < nl_positions.size(); k++) {
11278
    size_t pos = nl_positions[k];
11279
    // Skip the '\n' in a '\r\n' pair (the '\r' already created a boundary)
11280
    if (buf[pos] == '\n' && pos > 0 && buf[pos - 1] == '\r') {
11281
      prev = pos + 1;
11282
      continue;
11283
    }
11284
    size_t line_len = pos - prev;
11285
    if (line_len > 0) {
11286
      LineInfo info;
11287
      info.pos = prev;
11288
      info.len = line_len;
11289
      out.push_back(info);
11290
    }
11291
    prev = pos + 1;
11292
  }
11293
  // Handle last line without trailing newline
11294
  if (prev < len) {
11295
    size_t line_len = len - prev;
11296
    if (line_len > 0) {
11297
      LineInfo info;
11298
      info.pos = prev;
11299
      info.len = line_len;
11300
      out.push_back(info);
11301
    }
11302
  }
11303
}
11304
11305
#endif  // TINYOBJLOADER_USE_SIMD
11306
11307
/// Scalar fallback newline scanning — handles \n, \r\n, and bare \r
11308
static void scalar_find_line_infos(const char *buf, size_t start, size_t end,
11309
0
                                   std::vector<LineInfo> &out) {
11310
0
  size_t prev = start;
11311
0
  for (size_t i = start; i < end; i++) {
11312
0
    if (buf[i] == '\n' || buf[i] == '\r') {
11313
0
      size_t line_len = i - prev;
11314
      // Skip \r in \r\n pair
11315
0
      if (buf[i] == '\r' && (i + 1 < end) && buf[i + 1] == '\n') {
11316
0
        if (line_len > 0) {
11317
0
          LineInfo info;
11318
0
          info.pos = prev;
11319
0
          info.len = line_len;
11320
0
          out.push_back(info);
11321
0
        }
11322
0
        i++;  // skip the \n in \r\n
11323
0
      } else {
11324
        // bare \n or bare \r
11325
0
        if (line_len > 0) {
11326
0
          LineInfo info;
11327
0
          info.pos = prev;
11328
0
          info.len = line_len;
11329
0
          out.push_back(info);
11330
0
        }
11331
0
      }
11332
0
      prev = i + 1;
11333
0
    }
11334
0
  }
11335
0
  if (prev < end) {
11336
0
    size_t line_len = end - prev;
11337
0
    if (line_len > 0) {
11338
0
      LineInfo info;
11339
0
      info.pos = prev;
11340
0
      info.len = line_len;
11341
0
      out.push_back(info);
11342
0
    }
11343
0
  }
11344
0
}
11345
11346
template <typename DstVec, typename SrcVec>
11347
0
static void opt_assign_vector(DstVec *dst, const SrcVec &src) {
11348
0
  if (!dst) return;
11349
0
  dst->assign(src.begin(), src.end());
11350
0
}
Unexecuted instantiation: fuzz_ParseFromString.cc:void tinyobj::opt_internal::opt_assign_vector<std::__1::vector<float, std::__1::allocator<float> >, std::__1::vector<float, std::__1::allocator<float> > >(std::__1::vector<float, std::__1::allocator<float> >*, std::__1::vector<float, std::__1::allocator<float> > const&)
Unexecuted instantiation: fuzz_ParseFromString.cc:void tinyobj::opt_internal::opt_assign_vector<std::__1::vector<tinyobj::index_t, std::__1::allocator<tinyobj::index_t> >, std::__1::vector<tinyobj::index_t, std::__1::allocator<tinyobj::index_t> > >(std::__1::vector<tinyobj::index_t, std::__1::allocator<tinyobj::index_t> >*, std::__1::vector<tinyobj::index_t, std::__1::allocator<tinyobj::index_t> > const&)
Unexecuted instantiation: fuzz_ParseFromString.cc:void tinyobj::opt_internal::opt_assign_vector<std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > >(std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >*, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&)
Unexecuted instantiation: fuzz_ParseFromString.cc:void tinyobj::opt_internal::opt_assign_vector<std::__1::vector<int, std::__1::allocator<int> >, std::__1::vector<int, std::__1::allocator<int> > >(std::__1::vector<int, std::__1::allocator<int> >*, std::__1::vector<int, std::__1::allocator<int> > const&)
11351
11352
0
static void ConvertLegacyShapeToBasic(const shape_t &src, basic_shape_t<> *dst) {
11353
0
  if (!dst) return;
11354
0
  dst->name = src.name;
11355
0
  opt_assign_vector(&dst->mesh.indices, src.mesh.indices);
11356
0
  opt_assign_vector(&dst->mesh.num_face_vertices, src.mesh.num_face_vertices);
11357
0
  opt_assign_vector(&dst->mesh.material_ids, src.mesh.material_ids);
11358
0
  opt_assign_vector(&dst->mesh.smoothing_group_ids,
11359
0
                    src.mesh.smoothing_group_ids);
11360
0
  dst->mesh.tags = src.mesh.tags;
11361
0
  opt_assign_vector(&dst->lines.indices, src.lines.indices);
11362
0
  opt_assign_vector(&dst->lines.num_line_vertices, src.lines.num_line_vertices);
11363
0
  opt_assign_vector(&dst->points.indices, src.points.indices);
11364
0
}
11365
11366
static void ConvertLegacyResultToBasic(
11367
    const attrib_t &src_attrib, const std::vector<shape_t> &src_shapes,
11368
    const std::vector<material_t> &src_materials, basic_attrib_t<> *dst_attrib,
11369
    std::vector<basic_shape_t<> > *dst_shapes,
11370
0
    std::vector<material_t> *dst_materials) {
11371
0
  if (dst_attrib) {
11372
0
    opt_assign_vector(&dst_attrib->vertices, src_attrib.vertices);
11373
0
    opt_assign_vector(&dst_attrib->vertex_weights, src_attrib.vertex_weights);
11374
0
    opt_assign_vector(&dst_attrib->normals, src_attrib.normals);
11375
0
    opt_assign_vector(&dst_attrib->texcoords, src_attrib.texcoords);
11376
0
    opt_assign_vector(&dst_attrib->texcoord_ws, src_attrib.texcoord_ws);
11377
0
    opt_assign_vector(&dst_attrib->colors, src_attrib.colors);
11378
0
    dst_attrib->skin_weights = src_attrib.skin_weights;
11379
0
    dst_attrib->indices.clear();
11380
0
    dst_attrib->face_num_verts.clear();
11381
0
    dst_attrib->material_ids.clear();
11382
0
    for (size_t si = 0; si < src_shapes.size(); si++) {
11383
0
      const mesh_t &mesh = src_shapes[si].mesh;
11384
0
      dst_attrib->indices.insert(dst_attrib->indices.end(), mesh.indices.begin(),
11385
0
                                 mesh.indices.end());
11386
0
      dst_attrib->face_num_verts.insert(dst_attrib->face_num_verts.end(),
11387
0
                                        mesh.num_face_vertices.begin(),
11388
0
                                        mesh.num_face_vertices.end());
11389
0
      dst_attrib->material_ids.insert(dst_attrib->material_ids.end(),
11390
0
                                      mesh.material_ids.begin(),
11391
0
                                      mesh.material_ids.end());
11392
0
    }
11393
0
  }
11394
11395
0
  if (dst_shapes) {
11396
0
    dst_shapes->clear();
11397
0
    dst_shapes->resize(src_shapes.size());
11398
0
    for (size_t i = 0; i < src_shapes.size(); i++) {
11399
0
      ConvertLegacyShapeToBasic(src_shapes[i], &(*dst_shapes)[i]);
11400
0
    }
11401
0
  }
11402
11403
0
  if (dst_materials) {
11404
0
    (*dst_materials) = src_materials;
11405
0
  }
11406
0
}
11407
11408
/// Check if the first non-whitespace token on a line (starting at p, length
11409
/// rem) requires the legacy parser.
11410
0
static inline bool opt_line_start_is_legacy(const char *p, size_t rem) {
11411
0
  while (rem > 0 && (*p == ' ' || *p == '\t')) { p++; rem--; }
11412
0
  if (rem >= 3 && p[0] == 'v' && p[1] == 'w' &&
11413
0
      (p[2] == ' ' || p[2] == '\t'))
11414
0
    return true;
11415
0
  if (rem >= 2 &&
11416
0
      ((p[0] == 'l' || p[0] == 'p' || p[0] == 't') &&
11417
0
       (p[1] == ' ' || p[1] == '\t')))
11418
0
    return true;
11419
0
  return false;
11420
0
}
11421
11422
/// Fast whole-buffer scan for legacy-only tokens (vw, l, p, t at line start).
11423
/// Uses memchr for SIMD-accelerated newline finding.  Handles both \n and
11424
/// bare \r (old Mac) line endings.
11425
0
static bool opt_buffer_requires_legacy_fallback(const char *buf, size_t len) {
11426
0
  if (!buf || len == 0) return false;
11427
  // Check at start of buffer
11428
0
  if (opt_line_start_is_legacy(buf, len)) return true;
11429
  // Scan for line breaks (\n first, then bare \r)
11430
0
  for (int pass = 0; pass < 2; pass++) {
11431
0
    const char needle = (pass == 0) ? '\n' : '\r';
11432
0
    const char *p = buf;
11433
0
    size_t remaining = len;
11434
0
    for (;;) {
11435
0
      const char *nl = static_cast<const char *>(
11436
0
          std::memchr(p, needle, remaining));
11437
0
      if (!nl) break;
11438
0
      size_t offset = static_cast<size_t>(nl - buf) + 1;
11439
0
      if (offset >= len) break;
11440
      // Skip \n in \r\n pair on the \r pass to avoid double-checking
11441
0
      if (needle == '\r' && offset < len && buf[offset] == '\n') {
11442
0
        p = buf + offset + 1;
11443
0
        remaining = len - offset - 1;
11444
0
        continue;
11445
0
      }
11446
0
      if (opt_line_start_is_legacy(buf + offset, len - offset)) return true;
11447
0
      p = buf + offset;
11448
0
      remaining = len - offset;
11449
0
    }
11450
0
  }
11451
0
  return false;
11452
0
}
11453
11454
///
11455
/// Trie-based string → float cache for repeated OBJ coordinate values.
11456
/// Caches short float tokens (up to kMaxKeyLen chars) in a compact trie.
11457
/// Alphabet: '0'-'9', '+', '-', '.', 'e', 'E' (15 chars).
11458
/// Thread-local — each thread gets its own cache instance.
11459
///
11460
class OptFloatCache {
11461
 public:
11462
  static const int kFp32MaxKeyLen =
11463
      std::numeric_limits<float>::digits10 + 2;  // 8
11464
  static const int kRealMaxKeyLen =
11465
      std::numeric_limits<real_t>::digits10 + 2;
11466
  static const int kAlphabetSize = 15;
11467
11468
  explicit OptFloatCache(int max_nodes = 1024, bool fp32_keys = true)
11469
0
      : max_nodes_(max_nodes < 2 ? 2 : (max_nodes > 65535 ? 65535 : max_nodes)),
11470
0
        max_key_len_(fp32_keys ? kFp32MaxKeyLen : kRealMaxKeyLen) {
11471
0
    nodes_.reserve(static_cast<size_t>(max_nodes_));
11472
0
    nodes_.resize(1);
11473
0
    std::memset(&nodes_[0], 0, sizeof(Node));
11474
0
  }
11475
11476
0
  static inline int char_index(char c) {
11477
0
    if (c >= '0' && c <= '9') return c - '0';
11478
0
    switch (c) {
11479
0
      case '+': return 10;
11480
0
      case '-': return 11;
11481
0
      case '.': return 12;
11482
0
      case 'e': return 13;
11483
0
      case 'E': return 14;
11484
0
    }
11485
0
    return -1;
11486
0
  }
11487
11488
  /// Try cache lookup.  Returns true + sets *out on hit.
11489
0
  inline bool lookup(const char *key, int key_len, real_t *out) const {
11490
0
    if (key_len <= 0 || key_len > max_key_len_) return false;
11491
0
    int node = 0;
11492
0
    for (int i = 0; i < key_len; i++) {
11493
0
      int ci = char_index(key[i]);
11494
0
      if (ci < 0) return false;
11495
0
      int child = nodes_[static_cast<size_t>(node)]
11496
0
                      .children[static_cast<size_t>(ci)];
11497
0
      if (child == 0) return false;
11498
0
      node = child;
11499
0
    }
11500
0
    const Node &n = nodes_[static_cast<size_t>(node)];
11501
0
    if (!n.has_value) return false;
11502
0
    *out = n.value;
11503
0
    return true;
11504
0
  }
11505
11506
  /// Insert parsed value.  Returns false if full or key too long.
11507
0
  inline bool insert(const char *key, int key_len, real_t value) {
11508
0
    if (key_len <= 0 || key_len > max_key_len_) return false;
11509
0
    int node = 0;
11510
0
    for (int i = 0; i < key_len; i++) {
11511
0
      int ci = char_index(key[i]);
11512
0
      if (ci < 0) return false;
11513
0
      uint16_t child = nodes_[static_cast<size_t>(node)]
11514
0
                           .children[static_cast<size_t>(ci)];
11515
0
      if (child == 0) {
11516
0
        if (static_cast<int>(nodes_.size()) >= max_nodes_) return false;
11517
0
        child = static_cast<uint16_t>(nodes_.size());
11518
        // Write back BEFORE push_back (push_back may realloc)
11519
0
        nodes_[static_cast<size_t>(node)]
11520
0
            .children[static_cast<size_t>(ci)] = child;
11521
0
        Node new_node;
11522
0
        std::memset(&new_node, 0, sizeof(Node));
11523
0
        nodes_.push_back(new_node);
11524
0
      }
11525
0
      node = static_cast<int>(child);
11526
0
    }
11527
0
    Node &n = nodes_[static_cast<size_t>(node)];
11528
0
    n.value = value;
11529
0
    n.has_value = true;
11530
0
    return true;
11531
0
  }
11532
11533
0
  int max_key_len() const { return max_key_len_; }
11534
11535
 private:
11536
  struct Node {
11537
    uint16_t children[kAlphabetSize];  // 30 bytes
11538
    real_t value;                      // 4 or 8 bytes
11539
    bool has_value;                    // 1 byte
11540
  };
11541
  std::vector<Node> nodes_;
11542
  int max_nodes_;
11543
  int max_key_len_;
11544
};
11545
11546
// Fast inline float parse: skip whitespace, call fast_float with line_end,
11547
// advance cursor.  No token-end pre-scan.  Returns false if no float found.
11548
// When cache is non-null, checks/populates the trie cache for short tokens.
11549
// Handles nan/inf keywords with OBJ-compatible replacement values (matching
11550
// opt_tryParseDouble behavior).
11551
#ifndef TINYOBJLOADER_DISABLE_FAST_FLOAT
11552
static inline bool opt_fast_parse_float(real_t *out, const char **cursor,
11553
                                        const char *line_end,
11554
0
                                        OptFloatCache *cache = nullptr) {
11555
0
  const char *p = *cursor;
11556
0
  while (p < line_end && (*p == ' ' || *p == '\t')) p++;
11557
0
  if (p >= line_end || *p == '\n' || *p == '\r' || *p == '#') return false;
11558
11559
  // Check for nan/inf keywords before calling fast_float (which doesn't
11560
  // handle them).  Map to OBJ-compatible values: nan→0, +inf→max, -inf→lowest.
11561
0
  {
11562
0
    const char *q = p;
11563
0
    if (q < line_end && (*q == '+' || *q == '-')) ++q;
11564
0
    if (q < line_end) {
11565
0
      char fc = *q;
11566
0
      if (fc >= 'A' && fc <= 'Z') fc += 32;
11567
0
      if (fc == 'n' || fc == 'i') {
11568
0
        double special_val;
11569
0
        const char *end_ptr;
11570
0
        if (detail_fp::tryParseNanInf(p, line_end, &special_val, &end_ptr)) {
11571
0
          *out = static_cast<real_t>(special_val);
11572
0
          *cursor = end_ptr;
11573
0
          return true;
11574
0
        }
11575
0
      }
11576
0
    }
11577
0
  }
11578
11579
  // --- Cache fast path: scan up to max_key_len chars for trie lookup ---
11580
0
  if (cache) {
11581
0
    const char *tok_start = p;
11582
0
    const char *kp = p;
11583
0
    int klen = 0;
11584
0
    int mkl = cache->max_key_len();
11585
0
    while (klen < mkl && kp < line_end) {
11586
0
      char c = *kp;
11587
0
      if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '#' ||
11588
0
          c == '\0')
11589
0
        break;
11590
0
      if (OptFloatCache::char_index(c) < 0) break;
11591
0
      klen++;
11592
0
      kp++;
11593
0
    }
11594
    // Token is cacheable if it ended at a delimiter (not mid-token)
11595
0
    bool at_delim = (kp >= line_end || *kp == ' ' || *kp == '\t' ||
11596
0
                     *kp == '\n' || *kp == '\r' || *kp == '#' || *kp == '\0');
11597
0
    if (klen > 0 && at_delim) {
11598
0
      real_t cached;
11599
0
      if (cache->lookup(tok_start, klen, &cached)) {
11600
0
        *out = cached;
11601
0
        *cursor = kp;
11602
0
        return true;
11603
0
      }
11604
      // Cache miss — parse normally, then insert
11605
0
      real_t tmp;
11606
0
      auto r = fast_float::from_chars(tok_start, line_end, tmp,
11607
0
          fast_float::chars_format::general |
11608
0
          fast_float::chars_format::allow_leading_plus);
11609
0
      if (r.ec != tinyobj_ff::ff_errc::ok) return false;
11610
0
      *out = tmp;
11611
0
      *cursor = r.ptr;
11612
0
      cache->insert(tok_start, klen, tmp);
11613
0
      return true;
11614
0
    }
11615
    // Token too long or has non-float chars — fall through to uncached parse
11616
0
  }
11617
11618
  // --- Uncached parse ---
11619
0
  real_t tmp;
11620
0
  auto r = fast_float::from_chars(p, line_end, tmp,
11621
0
      fast_float::chars_format::general |
11622
0
      fast_float::chars_format::allow_leading_plus);
11623
0
  if (r.ec != tinyobj_ff::ff_errc::ok) return false;
11624
0
  *out = tmp;
11625
0
  *cursor = r.ptr;
11626
0
  return true;
11627
0
}
11628
#endif
11629
11630
// Parse a single line and store into compact per-type thread data.
11631
// Fast path: v/vn/vt lines are parsed directly using fast_float with line_end,
11632
// bypassing OptCommand entirely.  Face/meta lines fall back to opt_parseLine.
11633
static void opt_parseLineToThreadData(
11634
    OptThreadData &td, const char *line_ptr, size_t line_len,
11635
    bool triangulate, size_t line_number,
11636
0
    OptFloatCache *cache = nullptr) {
11637
0
  const char *token = line_ptr;
11638
0
  while (*token == ' ' || *token == '\t') token++;
11639
0
  if (*token == '\n' || *token == '\r' || *token == '#' || *token == '\0')
11640
0
    return;
11641
11642
#ifdef TINYOBJLOADER_DISABLE_FAST_FLOAT
11643
  (void)cache;  // only consumed by the fast_float fast path below
11644
#else
11645
0
  const char *line_end = line_ptr + line_len;
11646
11647
  // ---- Fast path: vertex position (v x y z [w] [r g b]) ----
11648
0
  if (token[0] == 'v' && (token[1] == ' ' || token[1] == '\t')) {
11649
0
    const char *cur = token + 2;
11650
0
    real_t x, y, z;
11651
0
    if (!opt_fast_parse_float(&x, &cur, line_end, cache) ||
11652
0
        !opt_fast_parse_float(&y, &cur, line_end, cache) ||
11653
0
        !opt_fast_parse_float(&z, &cur, line_end, cache)) {
11654
0
      td.error_line = line_number;
11655
0
      td.error_message = "failed to parse `v' line";
11656
0
      return;
11657
0
    }
11658
    // Write xyz (batch: resize + direct write)
11659
0
    size_t off = td.v_pos.size();
11660
0
    td.v_pos.resize(off + 3);
11661
0
    td.v_pos[off] = x; td.v_pos[off+1] = y; td.v_pos[off+2] = z;
11662
11663
    // Check for extra components (weight / color)
11664
0
    real_t extra[4];
11665
0
    int nextra = 0;
11666
0
    while (nextra < 4 && opt_fast_parse_float(&extra[nextra], &cur, line_end, cache))
11667
0
      nextra++;
11668
11669
0
    if (nextra == 1) {
11670
      // v x y z w  (4 total) — weight only, no color
11671
0
      if (!td.saw_any_weight) {
11672
0
        td.saw_any_weight = true;
11673
0
        td.v_weight.resize(td.num_v, real_t(1.0));
11674
0
      }
11675
0
      td.v_weight.push_back(extra[0]);
11676
0
      td.saw_missing_color = true;
11677
0
      if (td.saw_any_color) {
11678
0
        size_t coff = td.v_color.size();
11679
0
        td.v_color.resize(coff + 3);
11680
0
        td.v_color[coff] = real_t(1.0);
11681
0
        td.v_color[coff+1] = real_t(1.0);
11682
0
        td.v_color[coff+2] = real_t(1.0);
11683
0
      }
11684
0
    } else if (nextra == 3) {
11685
      // v x y z r g b  (6 total) — color, weight = r (legacy compat)
11686
0
      if (!td.saw_any_weight) {
11687
0
        td.saw_any_weight = true;
11688
0
        td.v_weight.resize(td.num_v, real_t(1.0));
11689
0
      }
11690
0
      td.v_weight.push_back(extra[0]);
11691
0
      if (!td.saw_any_color) {
11692
0
        td.saw_any_color = true;
11693
0
        td.v_color.resize(td.num_v * 3, real_t(1.0));
11694
0
      }
11695
0
      size_t coff = td.v_color.size();
11696
0
      td.v_color.resize(coff + 3);
11697
0
      td.v_color[coff] = extra[0];
11698
0
      td.v_color[coff+1] = extra[1];
11699
0
      td.v_color[coff+2] = extra[2];
11700
0
    } else if (nextra >= 4) {
11701
      // v x y z w r g b  (7+ total) — weight + color.
11702
      // NOTE: 7-component vertices are NOT part of the OBJ spec nor the common
11703
      // vertex-color extension, which define only xyz / xyzw / xyzrgb and treat
11704
      // weight and color as mutually exclusive.  tinyobjloader accepts this as
11705
      // an extension: the 4th value is the weight, the next three are RGB.
11706
      // (The classic LoadObj / LoadObjWithCallback path instead caps at 6 and
11707
      // ignores any extra tokens, so it differs from LoadObjOpt here.)
11708
0
      if (!td.saw_any_weight) {
11709
0
        td.saw_any_weight = true;
11710
0
        td.v_weight.resize(td.num_v, real_t(1.0));
11711
0
      }
11712
0
      td.v_weight.push_back(extra[0]);
11713
0
      if (!td.saw_any_color) {
11714
0
        td.saw_any_color = true;
11715
0
        td.v_color.resize(td.num_v * 3, real_t(1.0));
11716
0
      }
11717
0
      size_t coff = td.v_color.size();
11718
0
      td.v_color.resize(coff + 3);
11719
0
      td.v_color[coff] = extra[1];
11720
0
      td.v_color[coff+1] = extra[2];
11721
0
      td.v_color[coff+2] = extra[3];
11722
0
    } else {
11723
      // nextra == 0 or 2 — no color
11724
0
      if (td.saw_any_weight) td.v_weight.push_back(real_t(1.0));
11725
0
      td.saw_missing_color = true;
11726
0
      if (td.saw_any_color) {
11727
0
        size_t coff = td.v_color.size();
11728
0
        td.v_color.resize(coff + 3);
11729
0
        td.v_color[coff] = real_t(1.0);
11730
0
        td.v_color[coff+1] = real_t(1.0);
11731
0
        td.v_color[coff+2] = real_t(1.0);
11732
0
      }
11733
0
    }
11734
0
    td.num_v++;
11735
0
    return;
11736
0
  }
11737
11738
  // ---- Fast path: normal (vn x y z) ----
11739
0
  if (token[0] == 'v' && token[1] == 'n' &&
11740
0
      (token[2] == ' ' || token[2] == '\t')) {
11741
0
    const char *cur = token + 3;
11742
0
    real_t x, y, z;
11743
0
    if (!opt_fast_parse_float(&x, &cur, line_end, cache) ||
11744
0
        !opt_fast_parse_float(&y, &cur, line_end, cache) ||
11745
0
        !opt_fast_parse_float(&z, &cur, line_end, cache)) {
11746
0
      td.error_line = line_number;
11747
0
      td.error_message = "failed to parse `vn' line";
11748
0
      return;
11749
0
    }
11750
0
    size_t off = td.vn_data.size();
11751
0
    td.vn_data.resize(off + 3);
11752
0
    td.vn_data[off] = x; td.vn_data[off+1] = y; td.vn_data[off+2] = z;
11753
0
    td.num_vn++;
11754
0
    return;
11755
0
  }
11756
11757
  // ---- Fast path: texcoord (vt u [v [w]]) ----
11758
0
  if (token[0] == 'v' && token[1] == 't' &&
11759
0
      (token[2] == ' ' || token[2] == '\t')) {
11760
0
    const char *cur = token + 3;
11761
0
    real_t u = 0, v = 0;
11762
0
    if (!opt_fast_parse_float(&u, &cur, line_end, cache)) {
11763
0
      td.error_line = line_number;
11764
0
      td.error_message = "failed to parse `vt' line";
11765
0
      return;
11766
0
    }
11767
0
    opt_fast_parse_float(&v, &cur, line_end, cache);  // v is optional
11768
0
    real_t w = 0;
11769
0
    bool has_w = opt_fast_parse_float(&w, &cur, line_end, cache);
11770
11771
0
    size_t off = td.vt_data.size();
11772
0
    td.vt_data.resize(off + 2);
11773
0
    td.vt_data[off] = u; td.vt_data[off+1] = v;
11774
0
    if (has_w) {
11775
0
      if (!td.saw_any_texcoord_w) {
11776
0
        td.saw_any_texcoord_w = true;
11777
0
        td.vt_w.resize(td.num_vt, real_t(0.0));
11778
0
      }
11779
0
      td.vt_w.push_back(w);
11780
0
    } else if (td.saw_any_texcoord_w) {
11781
0
      td.vt_w.push_back(real_t(0.0));
11782
0
    }
11783
0
    td.num_vt++;
11784
0
    return;
11785
0
  }
11786
0
#endif  // TINYOBJLOADER_DISABLE_FAST_FLOAT
11787
11788
  // ---- Slow path: face / meta commands via opt_parseLine ----
11789
0
  OptCommand cmd;
11790
0
  bool parse_error = false;
11791
0
  bool ok = opt_parseLine(&cmd, line_ptr, line_len, triangulate,
11792
0
                           &parse_error, nullptr);
11793
0
  if (parse_error) {
11794
0
    td.error_line = line_number;
11795
0
    std::string msg;
11796
0
    opt_parseLine(&cmd, line_ptr, line_len, triangulate, nullptr, &msg);
11797
0
    td.error_message = msg;
11798
0
    return;
11799
0
  }
11800
0
  if (!ok) return;
11801
11802
0
  switch (cmd.type) {
11803
#ifdef TINYOBJLOADER_DISABLE_FAST_FLOAT
11804
    // When fast_float is disabled, v/vn/vt fall through to here
11805
    case OPT_CMD_V: {
11806
      size_t off = td.v_pos.size();
11807
      td.v_pos.resize(off + 3);
11808
      td.v_pos[off] = cmd.vx; td.v_pos[off+1] = cmd.vy; td.v_pos[off+2] = cmd.vz;
11809
      if (cmd.has_vertex_weight) {
11810
        if (!td.saw_any_weight) {
11811
          td.saw_any_weight = true;
11812
          td.v_weight.resize(td.num_v, real_t(1.0));
11813
        }
11814
        td.v_weight.push_back(cmd.vw);
11815
      } else if (td.saw_any_weight) {
11816
        td.v_weight.push_back(real_t(1.0));
11817
      }
11818
      if (cmd.has_vertex_color) {
11819
        if (!td.saw_any_color) {
11820
          td.saw_any_color = true;
11821
          td.v_color.resize(td.num_v * 3, real_t(1.0));
11822
        }
11823
        td.v_color.push_back(cmd.vc_r);
11824
        td.v_color.push_back(cmd.vc_g);
11825
        td.v_color.push_back(cmd.vc_b);
11826
      } else {
11827
        td.saw_missing_color = true;
11828
        if (td.saw_any_color) {
11829
          td.v_color.push_back(real_t(1.0));
11830
          td.v_color.push_back(real_t(1.0));
11831
          td.v_color.push_back(real_t(1.0));
11832
        }
11833
      }
11834
      td.num_v++;
11835
      break;
11836
    }
11837
    case OPT_CMD_VN: {
11838
      size_t off = td.vn_data.size();
11839
      td.vn_data.resize(off + 3);
11840
      td.vn_data[off] = cmd.nx; td.vn_data[off+1] = cmd.ny; td.vn_data[off+2] = cmd.nz;
11841
      td.num_vn++;
11842
      break;
11843
    }
11844
    case OPT_CMD_VT: {
11845
      size_t off = td.vt_data.size();
11846
      td.vt_data.resize(off + 2);
11847
      td.vt_data[off] = cmd.tx; td.vt_data[off+1] = cmd.ty;
11848
      if (cmd.has_texcoord_w) {
11849
        if (!td.saw_any_texcoord_w) {
11850
          td.saw_any_texcoord_w = true;
11851
          td.vt_w.resize(td.num_vt, real_t(0.0));
11852
        }
11853
        td.vt_w.push_back(cmd.tw);
11854
      } else if (td.saw_any_texcoord_w) {
11855
        td.vt_w.push_back(real_t(0.0));
11856
      }
11857
      td.num_vt++;
11858
      break;
11859
    }
11860
#endif  // TINYOBJLOADER_DISABLE_FAST_FLOAT
11861
0
    case OPT_CMD_F: {
11862
0
      OptFaceCmd fc;
11863
0
      fc.face_vertex_count = cmd.face_vertex_count;
11864
0
      fc.emitted_face_count = cmd.emitted_face_count;
11865
0
      fc.emitted_face_verts = cmd.emitted_face_verts;
11866
0
      fc.f_count = cmd.f_count;
11867
0
      fc.degenerate = cmd.degenerate_face;
11868
0
      fc.source_line = static_cast<uint32_t>(line_number);
11869
0
      fc.v_count_before = static_cast<uint32_t>(td.num_v);
11870
0
      fc.vn_count_before = static_cast<uint32_t>(td.num_vn);
11871
0
      fc.vt_count_before = static_cast<uint32_t>(td.num_vt);
11872
0
      if (!cmd.f_heap.empty()) {
11873
0
        fc.f_heap = std::move(cmd.f_heap);
11874
0
      } else if (cmd.face_vertex_count <= OptFaceCmd::kInlineCap) {
11875
0
        for (uint32_t k = 0; k < cmd.face_vertex_count; k++)
11876
0
          fc.f_inline[k] = cmd.f_inline[k];
11877
0
      } else {
11878
0
        fc.f_heap.assign(cmd.f_inline, cmd.f_inline + cmd.face_vertex_count);
11879
0
      }
11880
0
      td.num_f_indices += fc.f_count;
11881
0
      td.num_f_faces += fc.emitted_face_count;
11882
0
      OptSeqEntry se;
11883
0
      se.kind = OptSeqEntry::SEQ_FACE;
11884
0
      se.index = static_cast<uint32_t>(td.faces.size());
11885
0
      td.seq.push_back(se);
11886
0
      td.faces.push_back(std::move(fc));
11887
0
      break;
11888
0
    }
11889
0
    case OPT_CMD_G:
11890
0
    case OPT_CMD_O:
11891
0
    case OPT_CMD_USEMTL:
11892
0
    case OPT_CMD_MTLLIB:
11893
0
    case OPT_CMD_S: {
11894
0
      OptMetaCmd mc;
11895
0
      mc.type = cmd.type;
11896
0
      mc.source_line = static_cast<uint32_t>(line_number);
11897
0
      if (cmd.type == OPT_CMD_G) {
11898
0
        mc.str_storage = cmd.group_name_storage;
11899
0
        mc.str_ptr = cmd.group_name;
11900
0
        mc.str_len = cmd.group_name_len;
11901
0
        mc.group_name_empty = cmd.group_name_empty;
11902
0
      } else if (cmd.type == OPT_CMD_O) {
11903
0
        mc.str_ptr = cmd.object_name;
11904
0
        mc.str_len = cmd.object_name_len;
11905
0
      } else if (cmd.type == OPT_CMD_USEMTL) {
11906
0
        mc.str_ptr = cmd.material_name;
11907
0
        mc.str_len = cmd.material_name_len;
11908
0
      } else if (cmd.type == OPT_CMD_MTLLIB) {
11909
0
        mc.str_ptr = cmd.mtllib_name;
11910
0
        mc.str_len = cmd.mtllib_name_len;
11911
0
      } else if (cmd.type == OPT_CMD_S) {
11912
0
        mc.smoothing_group_id = cmd.smoothing_group_id;
11913
0
        td.saw_any_smoothing = true;
11914
0
      }
11915
0
      OptSeqEntry se;
11916
0
      se.kind = OptSeqEntry::SEQ_META;
11917
0
      se.index = static_cast<uint32_t>(td.metas.size());
11918
0
      td.seq.push_back(se);
11919
0
      td.metas.push_back(std::move(mc));
11920
0
      break;
11921
0
    }
11922
0
    default:
11923
0
      break;
11924
0
  }
11925
0
}
11926
11927
}  // namespace opt_internal
11928
11929
// Internal implementation with optional basedir for material path resolution
11930
static bool LoadObjOpt_internal(basic_attrib_t<> *attrib,
11931
                                std::vector<basic_shape_t<>> *shapes,
11932
                                std::vector<material_t> *materials,
11933
                                std::string *warn, std::string *err,
11934
                                const char *buf, size_t buf_len,
11935
                                const std::string &mtl_basedir,
11936
                                const std::string &source_name,
11937
                                bool enable_mtllib_loading,
11938
0
                                const OptLoadConfig &config) {
11939
0
  using namespace opt_internal;
11940
11941
0
  if (!attrib || !shapes) {
11942
0
    if (err) *err = "attrib and shapes must not be null.";
11943
0
    return false;
11944
0
  }
11945
11946
0
  attrib->vertices.clear();
11947
0
  attrib->vertex_weights.clear();
11948
0
  attrib->normals.clear();
11949
0
  attrib->texcoords.clear();
11950
0
  attrib->texcoord_ws.clear();
11951
0
  attrib->colors.clear();
11952
0
  attrib->skin_weights.clear();
11953
0
  attrib->indices.clear();
11954
0
  attrib->face_num_verts.clear();
11955
0
  attrib->material_ids.clear();
11956
0
  shapes->clear();
11957
0
  if (materials) materials->clear();
11958
11959
0
  if (buf_len < 1) return true;  // empty buffer is not an error
11960
11961
0
  if (!buf) {
11962
0
    if (err) *err = "buf must not be null when buf_len > 0.";
11963
0
    return false;
11964
0
  }
11965
11966
  // Ensure buffer ends with a newline for safe tokenization (avoids
11967
  // one-byte over-read on the last line when parsing directly from the
11968
  // buffer without per-line copies).
11969
0
  const char *work_buf = buf;
11970
0
  size_t work_len = buf_len;
11971
0
  std::vector<char> buf_with_sentinel;
11972
0
  if (buf[buf_len - 1] != '\n') {
11973
0
    buf_with_sentinel.assign(buf, buf + buf_len);
11974
0
    buf_with_sentinel.push_back('\n');
11975
0
    work_buf = buf_with_sentinel.data();
11976
0
    work_len = buf_with_sentinel.size();
11977
0
  }
11978
11979
  // Determine thread count
11980
0
  int num_threads = 1;
11981
#ifdef TINYOBJLOADER_USE_MULTITHREADING
11982
  if (config.num_threads < 0) {
11983
    num_threads = static_cast<int>(std::thread::hardware_concurrency());
11984
    if (num_threads < 1) num_threads = 1;
11985
  } else if (config.num_threads > 1) {
11986
    num_threads = config.num_threads;
11987
  }
11988
  if (num_threads > kOptMaxThreads) num_threads = kOptMaxThreads;
11989
#else
11990
0
#endif
11991
11992
  // ---- Phase 1: find line boundaries ----
11993
0
  std::vector<LineInfo> all_line_infos;
11994
11995
#if defined(TINYOBJLOADER_USE_SIMD) && \
11996
    (defined(TINYOBJLOADER_SIMD_SSE2) || defined(TINYOBJLOADER_SIMD_AVX2) || \
11997
     defined(TINYOBJLOADER_SIMD_NEON))
11998
  {
11999
    std::vector<size_t> nl_positions;
12000
    nl_positions.reserve(work_len / 64);
12001
    simd_find_newlines(work_buf, work_len, nl_positions);
12002
    simd_build_line_infos(work_buf, work_len, nl_positions, all_line_infos);
12003
  }
12004
#else
12005
0
  {
12006
0
    all_line_infos.reserve(work_len / 64);
12007
0
    scalar_find_line_infos(work_buf, 0, work_len, all_line_infos);
12008
0
  }
12009
0
#endif
12010
12011
0
  const size_t total_lines = all_line_infos.size();
12012
0
  if (total_lines == 0) return true;
12013
12014
  // Fast buffer-level check for legacy-only tokens (replaces per-line scan)
12015
0
  if (opt_buffer_requires_legacy_fallback(work_buf, work_len)) {
12016
0
    std::string input(buf, buf + buf_len);
12017
0
    std::istringstream iss(input);
12018
0
    attrib_t legacy_attrib;
12019
0
    std::vector<shape_t> legacy_shapes;
12020
0
    std::vector<material_t> legacy_materials;
12021
0
    MaterialFileReader mat_reader(mtl_basedir);
12022
0
    MaterialReader *reader =
12023
0
        enable_mtllib_loading ? static_cast<MaterialReader *>(&mat_reader) : NULL;
12024
0
    const bool ok = LoadObj(&legacy_attrib, &legacy_shapes, &legacy_materials,
12025
0
                            warn, err, &iss, reader, config.triangulate, false);
12026
0
    if (!ok) {
12027
0
      return false;
12028
0
    }
12029
0
    ConvertLegacyResultToBasic(legacy_attrib, legacy_shapes, legacy_materials,
12030
0
                               attrib, shapes, materials);
12031
0
    return true;
12032
0
  }
12033
12034
  // ---- Phase 2: parse lines (compact storage) ----
12035
  //   Single-threaded or multi-threaded depending on compile option.
12036
12037
0
  const size_t num_t = static_cast<size_t>(num_threads);
12038
0
  std::vector<OptThreadData> thread_data(num_t);
12039
12040
#ifdef TINYOBJLOADER_USE_MULTITHREADING
12041
  // Multi-threaded path
12042
  {
12043
    size_t lines_per_thread = total_lines / num_t;
12044
    std::vector<std::thread> workers;
12045
    workers.reserve(num_t);
12046
12047
    for (int t = 0; t < num_threads; t++) {
12048
      size_t start = static_cast<size_t>(t) * lines_per_thread;
12049
      size_t end = (t == num_threads - 1)
12050
                       ? total_lines
12051
                       : (static_cast<size_t>(t) + 1) * lines_per_thread;
12052
12053
      workers.emplace_back([&, t, start, end]() {
12054
        OptThreadData &td = thread_data[static_cast<size_t>(t)];
12055
        size_t est_lines = end - start;
12056
        td.v_pos.reserve(est_lines * 2);
12057
        td.faces.reserve(est_lines / 3);
12058
        td.seq.reserve(est_lines / 3);
12059
        OptFloatCache *tc = nullptr;
12060
#ifndef TINYOBJLOADER_DISABLE_FAST_FLOAT
12061
        OptFloatCache thread_cache(config.float_cache_max_nodes,
12062
                                   config.fp32_cache);
12063
        if (config.float_cache) tc = &thread_cache;
12064
#endif
12065
        for (size_t i = start; i < end; i++) {
12066
          opt_parseLineToThreadData(td, &work_buf[all_line_infos[i].pos],
12067
                                     all_line_infos[i].len, config.triangulate,
12068
                                     i + 1, tc);
12069
          if (td.error_line != 0) break;
12070
        }
12071
      });
12072
    }
12073
    for (auto &w : workers) w.join();
12074
  }
12075
12076
#else
12077
  // Single-threaded path
12078
0
  {
12079
0
    OptThreadData &td = thread_data[0];
12080
0
    td.v_pos.reserve(total_lines * 2);
12081
0
    td.faces.reserve(total_lines / 3);
12082
0
    td.seq.reserve(total_lines / 3);
12083
0
    OptFloatCache *tc = nullptr;
12084
0
#ifndef TINYOBJLOADER_DISABLE_FAST_FLOAT
12085
0
    OptFloatCache thread_cache(config.float_cache_max_nodes,
12086
0
                               config.fp32_cache);
12087
0
    if (config.float_cache) tc = &thread_cache;
12088
0
#endif
12089
0
    for (size_t i = 0; i < total_lines; i++) {
12090
0
      opt_parseLineToThreadData(td, &work_buf[all_line_infos[i].pos],
12091
0
                                 all_line_infos[i].len, config.triangulate,
12092
0
                                 i + 1, tc);
12093
0
      if (td.error_line != 0) break;
12094
0
    }
12095
0
  }
12096
0
#endif
12097
12098
0
  size_t first_error_line = 0;
12099
0
  std::string first_error_message;
12100
0
  for (size_t t = 0; t < num_t; t++) {
12101
0
    if (thread_data[t].error_line == 0) continue;
12102
0
    if (first_error_line == 0 || thread_data[t].error_line < first_error_line) {
12103
0
      first_error_line = thread_data[t].error_line;
12104
0
      first_error_message = thread_data[t].error_message;
12105
0
    }
12106
0
  }
12107
12108
  // ---- Phase 3: process sequential material state ----
12109
  // Compute v/vn/vt prefix sums across threads
12110
0
  std::vector<size_t> v_prefix(num_t), vn_prefix(num_t), vt_prefix(num_t);
12111
0
  v_prefix[0] = vn_prefix[0] = vt_prefix[0] = 0;
12112
0
  for (size_t t = 1; t < num_t; t++) {
12113
0
    v_prefix[t] = v_prefix[t - 1] + thread_data[t - 1].num_v;
12114
0
    vn_prefix[t] = vn_prefix[t - 1] + thread_data[t - 1].num_vn;
12115
0
    vt_prefix[t] = vt_prefix[t - 1] + thread_data[t - 1].num_vt;
12116
0
  }
12117
12118
0
  std::map<std::string, int> material_map;
12119
0
  std::vector<int> initial_material_id(num_t, -1);
12120
0
  size_t eof_pending_degenerate_faces = 0;
12121
0
  size_t phase3_error_line = 0;
12122
0
  std::string phase3_error_message;
12123
0
  {
12124
0
    MaterialFileReader mat_file_reader(mtl_basedir);
12125
0
    std::set<std::string> material_filenames;
12126
0
    std::vector<material_t> temp_materials;
12127
0
    std::vector<material_t> *material_dst = materials ? materials : &temp_materials;
12128
0
    int running_material_id = -1;
12129
0
    size_t pending_degenerate_faces = 0;
12130
0
    for (size_t t = 0; t < num_t; t++) {
12131
0
      if (phase3_error_line != 0) {
12132
0
        break;
12133
0
      }
12134
12135
0
      initial_material_id[t] = running_material_id;
12136
0
      const OptThreadData &td = thread_data[t];
12137
12138
0
      for (size_t si = 0; si < td.seq.size(); si++) {
12139
0
        const OptSeqEntry &entry = td.seq[si];
12140
12141
0
        if (entry.kind == OptSeqEntry::SEQ_FACE) {
12142
0
          const OptFaceCmd &face = td.faces[entry.index];
12143
12144
          // Compute running counts from prefix sums + per-face stored counts
12145
0
          int rv = static_cast<int>(v_prefix[t] + face.v_count_before);
12146
0
          int rvn = static_cast<int>(vn_prefix[t] + face.vn_count_before);
12147
0
          int rvt = static_cast<int>(vt_prefix[t] + face.vt_count_before);
12148
12149
0
          if (first_error_line != 0 && face.source_line >= first_error_line) {
12150
0
            continue;
12151
0
          }
12152
12153
0
          if (face.degenerate) {
12154
0
            pending_degenerate_faces++;
12155
0
            continue;
12156
0
          }
12157
12158
0
          for (size_t k = 0; k < face.face_vertex_count; k++) {
12159
0
            const opt_index_t &raw = face.face_indices()[k];
12160
0
            int resolved_idx = -1;
12161
12162
0
            if (!opt_validateAndResolveFaceIndexLikeLegacy(
12163
0
                    raw.vertex_index, rv, false, source_name,
12164
0
                    face.source_line, warn, &resolved_idx)) {
12165
0
              phase3_error_line = face.source_line;
12166
0
              phase3_error_message =
12167
0
                  "failed to parse `f' line (invalid vertex index)";
12168
0
              break;
12169
0
            }
12170
12171
0
            if (raw.texcoord_index != opt_index_t::kNotPresent) {
12172
0
              if (!opt_validateAndResolveFaceIndexLikeLegacy(
12173
0
                      raw.texcoord_index, rvt, true, source_name,
12174
0
                      face.source_line, warn, &resolved_idx)) {
12175
0
                phase3_error_line = face.source_line;
12176
0
                phase3_error_message =
12177
0
                    "failed to parse `f' line (invalid vertex index)";
12178
0
                break;
12179
0
              }
12180
0
            }
12181
12182
0
            if (raw.normal_index != opt_index_t::kNotPresent) {
12183
0
              if (!opt_validateAndResolveFaceIndexLikeLegacy(
12184
0
                      raw.normal_index, rvn, true, source_name,
12185
0
                      face.source_line, warn, &resolved_idx)) {
12186
0
                phase3_error_line = face.source_line;
12187
0
                phase3_error_message =
12188
0
                    "failed to parse `f' line (invalid vertex index)";
12189
0
                break;
12190
0
              }
12191
0
            }
12192
0
          }
12193
12194
0
          if (phase3_error_line != 0) {
12195
0
            break;
12196
0
          }
12197
12198
0
          continue;
12199
0
        }
12200
12201
        // SEQ_META
12202
0
        OptMetaCmd &meta = thread_data[t].metas[entry.index];
12203
12204
0
        if (first_error_line != 0 && meta.source_line >= first_error_line) {
12205
0
          continue;
12206
0
        }
12207
12208
0
        if (meta.type == OPT_CMD_G || meta.type == OPT_CMD_O) {
12209
0
          for (size_t deg = 0; deg < pending_degenerate_faces; deg++) {
12210
0
            if (warn) {
12211
0
              (*warn) += "Degenerated face found\n.";
12212
0
            }
12213
0
          }
12214
0
          pending_degenerate_faces = 0;
12215
0
        }
12216
12217
0
        if (meta.type == OPT_CMD_G && meta.group_name_empty) {
12218
0
          if (warn) {
12219
0
            std::stringstream ss;
12220
0
            ss << "Empty group name. line: " << meta.source_line << "\n";
12221
0
            (*warn) += ss.str();
12222
0
          }
12223
0
          continue;
12224
0
        }
12225
12226
0
        if (meta.type == OPT_CMD_MTLLIB) {
12227
0
          if (!enable_mtllib_loading) {
12228
0
            continue;
12229
0
          }
12230
12231
0
          std::string line_rest;
12232
0
          if (meta.str_ptr && meta.str_len > 0) {
12233
0
            line_rest.assign(meta.str_ptr, meta.str_len);
12234
0
          }
12235
12236
0
          std::vector<std::string> filenames;
12237
0
          SplitString(line_rest, ' ', '\\', filenames);
12238
0
          RemoveEmptyTokens(&filenames);
12239
12240
0
          if (filenames.empty()) {
12241
0
            if (warn) {
12242
0
              std::stringstream ss;
12243
0
              ss << "Looks like empty filename for mtllib. Use default "
12244
0
                    "material (line "
12245
0
                 << meta.source_line << ".)\n";
12246
0
              (*warn) += ss.str();
12247
0
            }
12248
0
            continue;
12249
0
          }
12250
12251
0
          bool found = false;
12252
0
          for (size_t s = 0; s < filenames.size(); s++) {
12253
0
            if (material_filenames.count(filenames[s]) > 0) {
12254
0
              found = true;
12255
0
              continue;
12256
0
            }
12257
12258
0
            std::string warn_mtl;
12259
0
            std::string err_mtl;
12260
0
            bool ok = mat_file_reader(filenames[s], material_dst, &material_map,
12261
0
                                      &warn_mtl, &err_mtl);
12262
12263
0
            if (warn && !warn_mtl.empty()) {
12264
0
              (*warn) += warn_mtl;
12265
0
            }
12266
12267
0
            if (err && !err_mtl.empty()) {
12268
0
              (*err) += err_mtl;
12269
0
            }
12270
12271
0
            if (ok) {
12272
0
              found = true;
12273
0
              material_filenames.insert(filenames[s]);
12274
0
              break;
12275
0
            }
12276
0
          }
12277
12278
0
          if (!found) {
12279
0
            if (warn) {
12280
0
              (*warn) +=
12281
0
                  "Failed to load material file(s). Use default material.\n";
12282
0
            }
12283
0
          }
12284
0
          continue;
12285
0
        }
12286
12287
0
        if (meta.type == OPT_CMD_USEMTL) {
12288
0
          std::string mat_name;
12289
0
          if (meta.str_ptr && meta.str_len > 0) {
12290
0
            mat_name.assign(meta.str_ptr, meta.str_len);
12291
0
          }
12292
0
          while (!mat_name.empty() &&
12293
0
                 (mat_name.back() == '\r' || mat_name.back() == '\n')) {
12294
0
            mat_name.pop_back();
12295
0
          }
12296
12297
0
          std::map<std::string, int>::const_iterator it = material_map.find(mat_name);
12298
0
          if (it != material_map.end()) {
12299
0
            meta.resolved_material_id = it->second;
12300
0
          } else {
12301
0
            meta.resolved_material_id = -1;
12302
0
            if (warn) {
12303
0
              (*warn) += "material [ '" + mat_name + "' ] not found in .mtl\n";
12304
0
            }
12305
0
          }
12306
0
          running_material_id = meta.resolved_material_id;
12307
0
          continue;
12308
0
        }
12309
0
      }
12310
12311
0
    }
12312
12313
0
    if (first_error_line == 0) {
12314
0
      eof_pending_degenerate_faces = pending_degenerate_faces;
12315
0
    }
12316
0
  }
12317
12318
0
  if (phase3_error_line != 0) {
12319
0
    if (err) {
12320
0
      std::stringstream ss;
12321
0
      ss << "Failed parse line(line " << phase3_error_line << "). "
12322
0
         << phase3_error_message << "\n";
12323
0
      if (!err->empty()) {
12324
0
        (*err) += ss.str();
12325
0
      } else {
12326
0
        (*err) = ss.str();
12327
0
      }
12328
0
    }
12329
0
    return false;
12330
0
  }
12331
12332
0
  if (first_error_line != 0) {
12333
0
    if (err) {
12334
0
      std::stringstream ss;
12335
0
      ss << "Failed parse line(line " << first_error_line << "). "
12336
0
         << first_error_message << "\n";
12337
0
      if (!err->empty()) {
12338
0
        (*err) += ss.str();
12339
0
      } else {
12340
0
        (*err) = ss.str();
12341
0
      }
12342
0
    }
12343
0
    return false;
12344
0
  }
12345
12346
  // ---- Phase 4: merge results ----
12347
0
  size_t num_v = 0, num_vn = 0, num_vt = 0;
12348
0
  size_t total_idx = 0;   // total index_t entries across all faces
12349
0
  size_t total_faces = 0; // total emitted faces
12350
0
  for (size_t t = 0; t < num_t; t++) {
12351
0
    num_v += thread_data[t].num_v;
12352
0
    num_vn += thread_data[t].num_vn;
12353
0
    num_vt += thread_data[t].num_vt;
12354
0
    total_idx += thread_data[t].num_f_indices;
12355
0
    total_faces += thread_data[t].num_f_faces;
12356
0
  }
12357
12358
  // Guard against size_t overflow in vertex/normal/texcoord allocation
12359
0
  if (num_v > SIZE_MAX / 3 || num_vn > SIZE_MAX / 3 || num_vt > SIZE_MAX / 2) {
12360
0
    if (err) {
12361
0
      (*err) += "Integer overflow in vertex/normal/texcoord count.\n";
12362
0
    }
12363
0
    return false;
12364
0
  }
12365
12366
0
  attrib->vertices.resize(num_v * 3);
12367
0
  attrib->vertex_weights.resize(num_v, real_t(1.0));
12368
0
  attrib->normals.resize(num_vn * 3);
12369
0
  attrib->texcoords.resize(num_vt * 2);
12370
0
  attrib->texcoord_ws.resize(num_vt, real_t(0.0));
12371
0
  std::vector<unsigned int> all_smoothing_group_ids(
12372
0
      static_cast<size_t>(total_faces), 0);
12373
0
  attrib->indices.resize(total_idx);
12374
0
  attrib->face_num_verts.resize(static_cast<size_t>(total_faces));
12375
0
  attrib->material_ids.resize(static_cast<size_t>(total_faces), -1);
12376
0
  std::vector<unsigned char> thread_saw_any_vertex_color(num_t, 0);
12377
0
  std::vector<unsigned char> thread_missing_vertex_color(num_t, 0);
12378
0
  std::vector<real_t> all_colors(num_v * 3, real_t(1.0));
12379
12380
  // Compute per-thread offsets
12381
0
  std::vector<size_t> v_off(num_t), n_off(num_t), t_off(num_t), f_off(num_t),
12382
0
      face_off(num_t);
12383
0
  v_off[0] = n_off[0] = t_off[0] = f_off[0] = face_off[0] = 0;
12384
0
  for (size_t t = 1; t < num_t; t++) {
12385
0
    v_off[t] = v_off[t - 1] + thread_data[t - 1].num_v;
12386
0
    n_off[t] = n_off[t - 1] + thread_data[t - 1].num_vn;
12387
0
    t_off[t] = t_off[t - 1] + thread_data[t - 1].num_vt;
12388
0
    f_off[t] = f_off[t - 1] + thread_data[t - 1].num_f_indices;
12389
0
    face_off[t] = face_off[t - 1] + thread_data[t - 1].num_f_faces;
12390
0
  }
12391
12392
  // Carry parser state that persists across lines, such as smoothing groups,
12393
  // across thread chunk boundaries before merging in parallel.
12394
0
  std::vector<unsigned int> initial_smoothing_group_id(num_t, 0);
12395
0
  unsigned int running_smoothing_group_id = 0;
12396
0
  for (size_t t = 0; t < num_t; t++) {
12397
0
    initial_smoothing_group_id[t] = running_smoothing_group_id;
12398
0
    for (size_t mi = 0; mi < thread_data[t].metas.size(); mi++) {
12399
0
      if (thread_data[t].metas[mi].type == OPT_CMD_S) {
12400
0
        running_smoothing_group_id = thread_data[t].metas[mi].smoothing_group_id;
12401
0
      }
12402
0
    }
12403
0
  }
12404
12405
  // Merge parsed data into final arrays
12406
0
  std::vector<std::vector<unsigned int> > command_written_faces(num_t);
12407
0
  for (size_t t = 0; t < num_t; t++) {
12408
0
    command_written_faces[t].assign(thread_data[t].faces.size(), 0);
12409
0
  }
12410
0
  std::vector<size_t> written_index_counts(num_t, 0);
12411
0
  std::vector<size_t> written_face_counts(num_t, 0);
12412
0
  std::vector<size_t> merge_error_lines(num_t, 0);
12413
0
  std::vector<std::string> merge_error_messages(num_t);
12414
0
  std::vector<int> thread_greatest_v_idx(num_t, -1);
12415
0
  std::vector<int> thread_greatest_vn_idx(num_t, -1);
12416
0
  std::vector<int> thread_greatest_vt_idx(num_t, -1);
12417
0
  auto merge_vertex_thread = [&](size_t t) {
12418
0
    const OptThreadData &td = thread_data[t];
12419
    // Bulk copy positions
12420
0
    if (!td.v_pos.empty()) {
12421
0
      std::memcpy(&attrib->vertices[v_off[t] * 3], td.v_pos.data(),
12422
0
                  td.v_pos.size() * sizeof(real_t));
12423
0
    }
12424
    // Copy weights
12425
0
    if (td.saw_any_weight) {
12426
0
      for (size_t i = 0; i < td.num_v; i++)
12427
0
        attrib->vertex_weights[v_off[t] + i] = td.v_weight[i];
12428
0
    }
12429
    // Copy colors
12430
0
    if (td.saw_any_color) {
12431
0
      std::memcpy(&all_colors[v_off[t] * 3], td.v_color.data(),
12432
0
                  td.v_color.size() * sizeof(real_t));
12433
0
      thread_saw_any_vertex_color[t] = 1;
12434
0
      if (td.saw_missing_color) thread_missing_vertex_color[t] = 1;
12435
0
    } else if (td.saw_missing_color) {
12436
0
      thread_missing_vertex_color[t] = 1;
12437
0
    }
12438
    // Bulk copy normals
12439
0
    if (!td.vn_data.empty()) {
12440
0
      std::memcpy(&attrib->normals[n_off[t] * 3], td.vn_data.data(),
12441
0
                  td.vn_data.size() * sizeof(real_t));
12442
0
    }
12443
    // Bulk copy texcoords
12444
0
    if (!td.vt_data.empty()) {
12445
0
      std::memcpy(&attrib->texcoords[t_off[t] * 2], td.vt_data.data(),
12446
0
                  td.vt_data.size() * sizeof(real_t));
12447
0
    }
12448
0
    if (td.saw_any_texcoord_w) {
12449
0
      for (size_t i = 0; i < td.num_vt; i++)
12450
0
        attrib->texcoord_ws[t_off[t] + i] = td.vt_w[i];
12451
0
    }
12452
0
  };
12453
0
  auto merge_thread = [&](size_t t) {
12454
0
    const OptThreadData &td = thread_data[t];
12455
0
    size_t fc = f_off[t], fcc = face_off[t];
12456
0
    int current_mat_id = initial_material_id[t];
12457
0
    unsigned int current_smoothing_id = initial_smoothing_group_id[t];
12458
0
    int greatest_v_idx = -1;
12459
0
    int greatest_vn_idx = -1;
12460
0
    int greatest_vt_idx = -1;
12461
12462
0
    for (size_t si = 0; si < td.seq.size(); si++) {
12463
0
      const OptSeqEntry &entry = td.seq[si];
12464
0
      if (entry.kind == OptSeqEntry::SEQ_META) {
12465
0
        const OptMetaCmd &meta = td.metas[entry.index];
12466
0
        if (meta.type == OPT_CMD_USEMTL)
12467
0
          current_mat_id = meta.resolved_material_id;
12468
0
        else if (meta.type == OPT_CMD_S)
12469
0
          current_smoothing_id = meta.smoothing_group_id;
12470
0
        continue;
12471
0
      }
12472
      // SEQ_FACE
12473
0
      const OptFaceCmd &face = td.faces[entry.index];
12474
0
      if (face.degenerate) {
12475
0
        command_written_faces[t][entry.index] = 0;
12476
0
        continue;
12477
0
      }
12478
      // Compute running counts for index resolution
12479
0
      size_t vc = v_off[t] + face.v_count_before;
12480
0
      size_t nc = n_off[t] + face.vn_count_before;
12481
0
      size_t tc = t_off[t] + face.vt_count_before;
12482
12483
0
      std::vector<index_t> resolved_face(face.face_vertex_count);
12484
0
      for (size_t k = 0; k < face.face_vertex_count; k++) {
12485
0
        const opt_index_t &vi = face.face_indices()[k];
12486
0
        index_t idx;
12487
0
        if (!opt_resolveIndexLikeLegacy(vi.vertex_index,
12488
0
                                        static_cast<int>(vc),
12489
0
                                        &idx.vertex_index, false)) {
12490
0
          merge_error_lines[t] = face.source_line;
12491
0
          merge_error_messages[t] =
12492
0
              "failed to parse `f' line (invalid vertex index)";
12493
0
          return;
12494
0
        }
12495
0
        opt_updateGreatestIndex(idx.vertex_index, &greatest_v_idx);
12496
0
        if (vi.texcoord_index == opt_index_t::kNotPresent) {
12497
0
          idx.texcoord_index = -1;
12498
0
        } else {
12499
0
          if (!opt_resolveIndexLikeLegacy(vi.texcoord_index,
12500
0
                                          static_cast<int>(tc),
12501
0
                                          &idx.texcoord_index, true)) {
12502
0
            merge_error_lines[t] = face.source_line;
12503
0
            merge_error_messages[t] =
12504
0
                "failed to parse `f' line (invalid vertex index)";
12505
0
            return;
12506
0
          }
12507
0
          if (idx.texcoord_index >= 0) {
12508
0
            opt_updateGreatestIndex(idx.texcoord_index, &greatest_vt_idx);
12509
0
          }
12510
0
        }
12511
0
        if (vi.normal_index == opt_index_t::kNotPresent) {
12512
0
          idx.normal_index = -1;
12513
0
        } else {
12514
0
          if (!opt_resolveIndexLikeLegacy(vi.normal_index,
12515
0
                                          static_cast<int>(nc),
12516
0
                                          &idx.normal_index, true)) {
12517
0
            merge_error_lines[t] = face.source_line;
12518
0
            merge_error_messages[t] =
12519
0
                "failed to parse `f' line (invalid vertex index)";
12520
0
            return;
12521
0
          }
12522
0
          if (idx.normal_index >= 0) {
12523
0
            opt_updateGreatestIndex(idx.normal_index, &greatest_vn_idx);
12524
0
          }
12525
0
        }
12526
0
        resolved_face[k] = idx;
12527
0
      }
12528
0
      size_t written_index_count = 0;
12529
0
      size_t written_face_count = 0;
12530
0
      if (config.triangulate) {
12531
0
        written_index_count = opt_triangulate_face(
12532
0
            attrib->vertices, resolved_face.data(), resolved_face.size(),
12533
0
            &attrib->indices[fc]);
12534
0
        written_face_count = written_index_count / 3;
12535
0
      } else {
12536
0
        written_index_count = resolved_face.size();
12537
0
        written_face_count = 1;
12538
0
        for (size_t k = 0; k < resolved_face.size(); k++) {
12539
0
          attrib->indices[fc + k] = resolved_face[k];
12540
0
        }
12541
0
      }
12542
0
      for (size_t k = 0; k < written_face_count; k++) {
12543
0
        attrib->face_num_verts[fcc + k] = config.triangulate
12544
0
                                              ? 3
12545
0
                                              : static_cast<int>(face.emitted_face_verts);
12546
0
        attrib->material_ids[fcc + k] = current_mat_id;
12547
0
        all_smoothing_group_ids[fcc + k] = current_smoothing_id;
12548
0
      }
12549
0
      command_written_faces[t][entry.index] =
12550
0
          static_cast<unsigned int>(written_face_count);
12551
0
      fc += written_index_count;
12552
0
      fcc += written_face_count;
12553
0
    }
12554
0
    written_index_counts[t] = fc - f_off[t];
12555
0
    written_face_counts[t] = fcc - face_off[t];
12556
0
    thread_greatest_v_idx[t] = greatest_v_idx;
12557
0
    thread_greatest_vn_idx[t] = greatest_vn_idx;
12558
0
    thread_greatest_vt_idx[t] = greatest_vt_idx;
12559
0
  };
12560
12561
#ifdef TINYOBJLOADER_USE_MULTITHREADING
12562
  if (num_threads > 1) {
12563
    std::vector<std::thread> workers;
12564
    workers.reserve(num_t);
12565
    for (size_t t = 0; t < num_t; t++) {
12566
      workers.emplace_back([&, t]() { merge_vertex_thread(t); });
12567
    }
12568
    for (auto &w : workers) w.join();
12569
    workers.clear();
12570
    for (size_t t = 0; t < num_t; t++) {
12571
      workers.emplace_back([&, t]() { merge_thread(t); });
12572
    }
12573
    for (auto &w : workers) w.join();
12574
  } else {
12575
    for (size_t t = 0; t < num_t; t++) merge_vertex_thread(t);
12576
    for (size_t t = 0; t < num_t; t++) merge_thread(t);
12577
  }
12578
#else
12579
0
  for (size_t t = 0; t < num_t; t++) merge_vertex_thread(t);
12580
0
  for (size_t t = 0; t < num_t; t++) merge_thread(t);
12581
0
#endif
12582
12583
0
  size_t first_merge_error_line = 0;
12584
0
  std::string first_merge_error_message;
12585
0
  for (size_t t = 0; t < merge_error_lines.size(); t++) {
12586
0
    if (merge_error_lines[t] == 0) continue;
12587
0
    if (first_merge_error_line == 0 ||
12588
0
        merge_error_lines[t] < first_merge_error_line) {
12589
0
      first_merge_error_line = merge_error_lines[t];
12590
0
      first_merge_error_message = merge_error_messages[t];
12591
0
    }
12592
0
  }
12593
12594
0
  if (first_merge_error_line != 0) {
12595
0
    attrib->vertices.clear();
12596
0
    attrib->vertex_weights.clear();
12597
0
    attrib->normals.clear();
12598
0
    attrib->texcoords.clear();
12599
0
    attrib->texcoord_ws.clear();
12600
0
    attrib->colors.clear();
12601
0
    attrib->skin_weights.clear();
12602
0
    attrib->indices.clear();
12603
0
    attrib->face_num_verts.clear();
12604
0
    attrib->material_ids.clear();
12605
0
    shapes->clear();
12606
0
    if (materials) materials->clear();
12607
12608
0
    if (err) {
12609
0
      std::stringstream ss;
12610
0
      ss << "Failed parse line(line " << first_merge_error_line << "). "
12611
0
         << first_merge_error_message << "\n";
12612
0
      if (!err->empty()) {
12613
0
        (*err) += ss.str();
12614
0
      } else {
12615
0
        (*err) = ss.str();
12616
0
      }
12617
0
    }
12618
0
    return false;
12619
0
  }
12620
12621
0
  bool saw_any_vertex_color = false;
12622
0
  bool saw_missing_vertex_color = false;
12623
0
  for (size_t t = 0; t < num_t; t++) {
12624
0
    saw_any_vertex_color =
12625
0
        saw_any_vertex_color || (thread_saw_any_vertex_color[t] != 0);
12626
0
    saw_missing_vertex_color =
12627
0
        saw_missing_vertex_color || (thread_missing_vertex_color[t] != 0);
12628
0
  }
12629
12630
0
  if (saw_any_vertex_color && !saw_missing_vertex_color) {
12631
0
    attrib->colors.swap(all_colors);
12632
0
  } else {
12633
0
    attrib->colors.clear();
12634
0
  }
12635
12636
0
  size_t actual_num_indices = 0;
12637
0
  size_t actual_num_faces = 0;
12638
0
  int greatest_v_idx = -1;
12639
0
  int greatest_vn_idx = -1;
12640
0
  int greatest_vt_idx = -1;
12641
0
  for (size_t t = 0; t < num_t; t++) {
12642
0
    actual_num_indices += written_index_counts[t];
12643
0
    actual_num_faces += written_face_counts[t];
12644
0
    opt_updateGreatestIndex(thread_greatest_v_idx[t], &greatest_v_idx);
12645
0
    opt_updateGreatestIndex(thread_greatest_vn_idx[t], &greatest_vn_idx);
12646
0
    opt_updateGreatestIndex(thread_greatest_vt_idx[t], &greatest_vt_idx);
12647
0
  }
12648
12649
  // Compact face arrays to remove gaps left by threads that wrote fewer
12650
  // indices/faces than pre-allocated (e.g. triangulation returning 0 for
12651
  // faces with out-of-bounds vertex indices).
12652
0
  if (num_t > 1) {
12653
0
    size_t dst_idx = 0;
12654
0
    size_t dst_face = 0;
12655
0
    for (size_t t = 0; t < num_t; t++) {
12656
0
      size_t src_idx = f_off[t];
12657
0
      size_t src_face = face_off[t];
12658
0
      size_t idx_count = written_index_counts[t];
12659
0
      size_t fc_count = written_face_counts[t];
12660
0
      if (dst_idx != src_idx && idx_count > 0) {
12661
0
        std::memmove(&attrib->indices[dst_idx], &attrib->indices[src_idx],
12662
0
                     idx_count * sizeof(index_t));
12663
0
      }
12664
0
      if (dst_face != src_face && fc_count > 0) {
12665
0
        std::memmove(&attrib->face_num_verts[dst_face],
12666
0
                     &attrib->face_num_verts[src_face],
12667
0
                     fc_count * sizeof(int));
12668
0
        std::memmove(&attrib->material_ids[dst_face],
12669
0
                     &attrib->material_ids[src_face],
12670
0
                     fc_count * sizeof(int));
12671
0
        std::memmove(&all_smoothing_group_ids[dst_face],
12672
0
                     &all_smoothing_group_ids[src_face],
12673
0
                     fc_count * sizeof(unsigned int));
12674
0
      }
12675
0
      dst_idx += idx_count;
12676
0
      dst_face += fc_count;
12677
0
    }
12678
0
  }
12679
12680
0
  attrib->indices.resize(actual_num_indices);
12681
0
  attrib->face_num_verts.resize(actual_num_faces);
12682
0
  attrib->material_ids.resize(actual_num_faces);
12683
0
  all_smoothing_group_ids.resize(actual_num_faces);
12684
12685
0
  opt_appendOutOfBoundsWarnings(
12686
0
      warn, greatest_v_idx, greatest_vn_idx, greatest_vt_idx,
12687
0
      static_cast<int>(attrib->vertices.size() / 3),
12688
0
      static_cast<int>(attrib->normals.size() / 3),
12689
0
      static_cast<int>(attrib->texcoords.size() / 2),
12690
0
      all_line_infos.size() + 1);
12691
0
  for (size_t deg = 0; deg < eof_pending_degenerate_faces; deg++) {
12692
0
    if (warn) {
12693
0
      (*warn) += "Degenerated face found\n.";
12694
0
    }
12695
0
  }
12696
12697
  // ---- Phase 5: construct shapes ----
12698
0
  {
12699
    // Precompute prefix-sum of index offsets for O(1) slicing
12700
0
    const size_t total_faces = attrib->face_num_verts.size();
12701
0
    std::vector<size_t> idx_prefix(total_faces + 1);
12702
0
    idx_prefix[0] = 0;
12703
0
    for (size_t fi = 0; fi < total_faces; fi++) {
12704
0
      idx_prefix[fi + 1] =
12705
0
          idx_prefix[fi] + static_cast<size_t>(attrib->face_num_verts[fi]);
12706
0
    }
12707
12708
0
    size_t face_count = 0;
12709
0
    basic_shape_t<> shape;
12710
0
    size_t face_prev_offset = 0;
12711
0
    bool shape_has_face_record = false;
12712
0
    bool have_active_shape_name = false;
12713
12714
0
    for (size_t t = 0; t < num_t; t++) {
12715
0
      const OptThreadData &td = thread_data[t];
12716
0
      for (size_t si = 0; si < td.seq.size(); si++) {
12717
0
        const OptSeqEntry &entry = td.seq[si];
12718
0
        if (entry.kind == OptSeqEntry::SEQ_META) {
12719
0
          const OptMetaCmd &meta = td.metas[entry.index];
12720
0
          if (meta.type == OPT_CMD_O || meta.type == OPT_CMD_G) {
12721
0
            std::string name;
12722
0
            if (meta.type == OPT_CMD_O && meta.str_ptr) {
12723
0
              name.assign(meta.str_ptr, meta.str_len);
12724
0
            } else if (!meta.str_storage.empty()) {
12725
0
              name = meta.str_storage;
12726
0
            } else if (meta.str_ptr) {
12727
0
              name.assign(meta.str_ptr, meta.str_len);
12728
0
            }
12729
0
            while (!name.empty() &&
12730
0
                   (name.back() == '\r' || name.back() == '\n'))
12731
0
              name.pop_back();
12732
12733
0
            if (face_count == 0) {
12734
0
              shape.name = name;
12735
0
              face_prev_offset = 0;
12736
0
              have_active_shape_name = true;
12737
0
            } else {
12738
0
              if (!have_active_shape_name) {
12739
                // faces before first group/object
12740
0
                basic_shape_t<> prev_shape;
12741
0
                prev_shape.mesh.num_face_vertices.assign(
12742
0
                    attrib->face_num_verts.begin(),
12743
0
                    attrib->face_num_verts.begin() +
12744
0
                        static_cast<std::ptrdiff_t>(face_count));
12745
0
                prev_shape.mesh.indices.assign(
12746
0
                    attrib->indices.begin(),
12747
0
                    attrib->indices.begin() +
12748
0
                        static_cast<std::ptrdiff_t>(idx_prefix[face_count]));
12749
0
                prev_shape.mesh.material_ids.assign(
12750
0
                    attrib->material_ids.begin(),
12751
0
                    attrib->material_ids.begin() +
12752
0
                        static_cast<std::ptrdiff_t>(face_count));
12753
0
                prev_shape.mesh.smoothing_group_ids.assign(
12754
0
                    all_smoothing_group_ids.begin(),
12755
0
                    all_smoothing_group_ids.begin() +
12756
0
                        static_cast<std::ptrdiff_t>(face_count));
12757
0
                shapes->push_back(std::move(prev_shape));
12758
0
              } else if (face_count > face_prev_offset) {
12759
                // push previous shape
12760
0
                basic_shape_t<> prev_shape;
12761
0
                prev_shape.name = shape.name;
12762
0
                prev_shape.mesh.num_face_vertices.assign(
12763
0
                    attrib->face_num_verts.begin() +
12764
0
                        static_cast<std::ptrdiff_t>(face_prev_offset),
12765
0
                    attrib->face_num_verts.begin() +
12766
0
                        static_cast<std::ptrdiff_t>(face_count));
12767
0
                prev_shape.mesh.indices.assign(
12768
0
                    attrib->indices.begin() +
12769
0
                        static_cast<std::ptrdiff_t>(idx_prefix[face_prev_offset]),
12770
0
                    attrib->indices.begin() +
12771
0
                        static_cast<std::ptrdiff_t>(idx_prefix[face_count]));
12772
0
                prev_shape.mesh.material_ids.assign(
12773
0
                    attrib->material_ids.begin() +
12774
0
                        static_cast<std::ptrdiff_t>(face_prev_offset),
12775
0
                    attrib->material_ids.begin() +
12776
0
                        static_cast<std::ptrdiff_t>(face_count));
12777
0
                prev_shape.mesh.smoothing_group_ids.assign(
12778
0
                    all_smoothing_group_ids.begin() +
12779
0
                        static_cast<std::ptrdiff_t>(face_prev_offset),
12780
0
                    all_smoothing_group_ids.begin() +
12781
0
                        static_cast<std::ptrdiff_t>(face_count));
12782
0
                shapes->push_back(std::move(prev_shape));
12783
0
              }
12784
0
              shape.name = name;
12785
0
              face_prev_offset = face_count;
12786
0
              have_active_shape_name = true;
12787
0
            }
12788
0
            shape_has_face_record = false;
12789
0
          }
12790
0
        } else {
12791
          // SEQ_FACE
12792
0
          shape_has_face_record = true;
12793
0
          face_count += command_written_faces[t][entry.index];
12794
0
        }
12795
0
      }
12796
0
    }
12797
12798
    // Final shape
12799
0
    if (face_count > face_prev_offset || shape_has_face_record) {
12800
0
      basic_shape_t<> final_shape;
12801
0
      final_shape.name = shape.name;
12802
0
      final_shape.mesh.num_face_vertices.assign(
12803
0
          attrib->face_num_verts.begin() +
12804
0
              static_cast<std::ptrdiff_t>(face_prev_offset),
12805
0
          attrib->face_num_verts.begin() +
12806
0
              static_cast<std::ptrdiff_t>(face_count));
12807
0
      final_shape.mesh.indices.assign(
12808
0
          attrib->indices.begin() +
12809
0
              static_cast<std::ptrdiff_t>(idx_prefix[face_prev_offset]),
12810
0
          attrib->indices.begin() +
12811
0
              static_cast<std::ptrdiff_t>(idx_prefix[face_count]));
12812
0
      final_shape.mesh.material_ids.assign(
12813
0
          attrib->material_ids.begin() +
12814
0
              static_cast<std::ptrdiff_t>(face_prev_offset),
12815
0
          attrib->material_ids.begin() +
12816
0
              static_cast<std::ptrdiff_t>(face_count));
12817
0
      final_shape.mesh.smoothing_group_ids.assign(
12818
0
          all_smoothing_group_ids.begin() +
12819
0
              static_cast<std::ptrdiff_t>(face_prev_offset),
12820
0
          all_smoothing_group_ids.begin() +
12821
0
              static_cast<std::ptrdiff_t>(face_count));
12822
0
      shapes->push_back(std::move(final_shape));
12823
0
    }
12824
0
  }
12825
12826
0
  return true;
12827
0
}
12828
12829
// ---- LoadObjOpt (buffer version, public API) ----
12830
12831
bool LoadObjOpt(basic_attrib_t<> *attrib,
12832
                std::vector<basic_shape_t<>> *shapes,
12833
                std::vector<material_t> *materials,
12834
                std::string *warn, std::string *err,
12835
                const char *buf, size_t buf_len,
12836
0
                const OptLoadConfig &config) {
12837
0
  return LoadObjOpt_internal(attrib, shapes, materials, warn, err,
12838
0
                             buf, buf_len, std::string(), "<stream>", false,
12839
0
                             config);
12840
0
}
12841
12842
// ---- LoadObjOpt (file version) ----
12843
12844
bool LoadObjOpt(basic_attrib_t<> *attrib,
12845
                std::vector<basic_shape_t<>> *shapes,
12846
                std::vector<material_t> *materials,
12847
                std::string *warn, std::string *err,
12848
                const char *filename,
12849
                const char *mtl_basedir,
12850
0
                const OptLoadConfig &config) {
12851
0
  if (!filename) {
12852
0
    if (err) *err = "filename is null.";
12853
0
    return false;
12854
0
  }
12855
12856
0
  std::string filepath(filename);
12857
12858
  // Resolve material base directory
12859
0
  std::string baseDir;
12860
0
  if (mtl_basedir) {
12861
0
    baseDir = mtl_basedir;
12862
0
  } else {
12863
    // Extract directory from filename
12864
0
    size_t pos = filepath.find_last_of("/\\");
12865
0
    if (pos != std::string::npos) {
12866
0
      baseDir = filepath.substr(0, pos + 1);
12867
0
    }
12868
0
  }
12869
0
  if (!baseDir.empty()) {
12870
0
#ifndef _WIN32
12871
0
    const char dirsep = '/';
12872
#else
12873
    const char dirsep = '\\';
12874
#endif
12875
0
    if (baseDir[baseDir.length() - 1] != dirsep) baseDir += dirsep;
12876
0
  }
12877
12878
#ifdef TINYOBJLOADER_USE_MMAP
12879
  {
12880
    MappedFile mf;
12881
    if (mf.open(filepath.c_str())) {
12882
      return LoadObjOpt_internal(attrib, shapes, materials, warn, err,
12883
                                 mf.data, mf.size, baseDir, filepath, true,
12884
                                 config);
12885
    }
12886
  }
12887
#endif
12888
12889
#ifdef _WIN32
12890
  std::ifstream ifs(LongPathW(UTF8ToWchar(filepath)).c_str(),
12891
                    std::ios::binary | std::ios::ate);
12892
#else
12893
0
  std::ifstream ifs(filepath.c_str(), std::ios::binary | std::ios::ate);
12894
0
#endif
12895
0
  if (!ifs.is_open()) {
12896
0
    if (err) *err = "Cannot open file: " + filepath;
12897
0
    return false;
12898
0
  }
12899
12900
0
  std::streamsize fsize = ifs.tellg();
12901
0
  ifs.seekg(0, std::ios::beg);
12902
12903
0
  if (fsize <= 0) {
12904
0
    return LoadObjOpt_internal(attrib, shapes, materials, warn, err,
12905
0
                               "", static_cast<size_t>(0), baseDir, filepath,
12906
0
                               true, config);
12907
0
  }
12908
12909
0
  std::vector<char> buf(static_cast<size_t>(fsize));
12910
0
  if (!ifs.read(buf.data(), fsize)) {
12911
0
    if (err) *err = "Failed to read file: " + filepath;
12912
0
    return false;
12913
0
  }
12914
0
  ifs.close();
12915
12916
  // Parse the in-memory file buffer with baseDir for mtllib resolution.
12917
0
  return LoadObjOpt_internal(attrib, shapes, materials, warn, err,
12918
0
                             buf.data(), static_cast<size_t>(fsize), baseDir,
12919
0
                             filepath, true, config);
12920
0
}
12921
12922
// ---- LoadObjOptTyped internals ----
12923
12924
static bool LoadObjOptTyped_internal(OptResult *result,
12925
                                     std::string *warn, std::string *err,
12926
                                     const char *buf, size_t buf_len,
12927
                                     const std::string &mtl_basedir,
12928
                                     const std::string &source_name,
12929
                                     bool enable_mtllib_loading,
12930
0
                                     const OptLoadConfig &config) {
12931
0
  using namespace opt_internal;
12932
12933
0
  ArenaAllocator &arena = result->arena;
12934
0
  OptAttrib &attrib = result->attrib;
12935
0
  result->valid = false;
12936
12937
0
  if (buf_len < 1) {
12938
0
    result->valid = true;
12939
0
    return true;
12940
0
  }
12941
0
  if (!buf) {
12942
0
    if (err) *err = "buf must not be null when buf_len > 0.";
12943
0
    return false;
12944
0
  }
12945
12946
0
  const char *work_buf = buf;
12947
0
  size_t work_len = buf_len;
12948
0
  std::vector<char> buf_with_sentinel;
12949
0
  if (buf[buf_len - 1] != '\n') {
12950
0
    buf_with_sentinel.assign(buf, buf + buf_len);
12951
0
    buf_with_sentinel.push_back('\n');
12952
0
    work_buf = buf_with_sentinel.data();
12953
0
    work_len = buf_with_sentinel.size();
12954
0
  }
12955
12956
0
  int num_threads = 1;
12957
#ifdef TINYOBJLOADER_USE_MULTITHREADING
12958
  if (config.num_threads < 0) {
12959
    num_threads = static_cast<int>(std::thread::hardware_concurrency());
12960
    if (num_threads < 1) num_threads = 1;
12961
  } else if (config.num_threads > 1) {
12962
    num_threads = config.num_threads;
12963
  }
12964
  if (num_threads > kOptMaxThreads) num_threads = kOptMaxThreads;
12965
#else
12966
0
#endif
12967
12968
  // ---- Phase 1: find line boundaries ----
12969
0
  std::vector<LineInfo> all_line_infos;
12970
12971
#if defined(TINYOBJLOADER_USE_SIMD) && \
12972
    (defined(TINYOBJLOADER_SIMD_SSE2) || defined(TINYOBJLOADER_SIMD_AVX2) || \
12973
     defined(TINYOBJLOADER_SIMD_NEON))
12974
  {
12975
    std::vector<size_t> nl_positions;
12976
    nl_positions.reserve(work_len / 64);
12977
    simd_find_newlines(work_buf, work_len, nl_positions);
12978
    simd_build_line_infos(work_buf, work_len, nl_positions, all_line_infos);
12979
  }
12980
#else
12981
0
  {
12982
0
    all_line_infos.reserve(work_len / 64);
12983
0
    scalar_find_line_infos(work_buf, 0, work_len, all_line_infos);
12984
0
  }
12985
0
#endif
12986
12987
0
  const size_t total_lines = all_line_infos.size();
12988
0
  if (total_lines == 0) {
12989
0
    result->valid = true;
12990
0
    return true;
12991
0
  }
12992
12993
  // Fast buffer-level check for legacy-only tokens (replaces per-line scan)
12994
0
  if (opt_buffer_requires_legacy_fallback(work_buf, work_len)) {
12995
0
    basic_attrib_t<> tmp_attrib;
12996
0
    std::vector<basic_shape_t<>> tmp_shapes;
12997
0
    std::vector<material_t> tmp_materials;
12998
0
    std::string input(buf, buf + buf_len);
12999
0
    std::istringstream iss(input);
13000
0
    attrib_t legacy_attrib;
13001
0
    std::vector<shape_t> legacy_shapes;
13002
0
    std::vector<material_t> legacy_materials;
13003
0
    MaterialFileReader mat_reader(mtl_basedir);
13004
0
    MaterialReader *reader =
13005
0
        enable_mtllib_loading ? static_cast<MaterialReader *>(&mat_reader) : NULL;
13006
0
    const bool ok = LoadObj(&legacy_attrib, &legacy_shapes, &legacy_materials,
13007
0
                            warn, err, &iss, reader, config.triangulate, false);
13008
0
    if (!ok) return false;
13009
0
    ConvertLegacyResultToBasic(legacy_attrib, legacy_shapes, legacy_materials,
13010
0
                               &tmp_attrib, &tmp_shapes, &tmp_materials);
13011
0
#define TINYOBJ_COPY_VEC_TO_ARENA_(dst, src)        \
13012
0
    do {                                             \
13013
0
      if (!(src).empty()) {                          \
13014
0
        (dst).allocate(arena, (src).size());          \
13015
0
        std::memcpy((dst).data(), (src).data(),       \
13016
0
                    (src).size() * sizeof((src)[0])); \
13017
0
      }                                              \
13018
0
    } while (0)
13019
0
    TINYOBJ_COPY_VEC_TO_ARENA_(attrib.vertices, tmp_attrib.vertices);
13020
0
    TINYOBJ_COPY_VEC_TO_ARENA_(attrib.vertex_weights, tmp_attrib.vertex_weights);
13021
0
    TINYOBJ_COPY_VEC_TO_ARENA_(attrib.normals, tmp_attrib.normals);
13022
0
    TINYOBJ_COPY_VEC_TO_ARENA_(attrib.texcoords, tmp_attrib.texcoords);
13023
0
    TINYOBJ_COPY_VEC_TO_ARENA_(attrib.texcoord_ws, tmp_attrib.texcoord_ws);
13024
0
    TINYOBJ_COPY_VEC_TO_ARENA_(attrib.colors, tmp_attrib.colors);
13025
0
    TINYOBJ_COPY_VEC_TO_ARENA_(attrib.indices, tmp_attrib.indices);
13026
0
    TINYOBJ_COPY_VEC_TO_ARENA_(attrib.face_num_verts, tmp_attrib.face_num_verts);
13027
0
    TINYOBJ_COPY_VEC_TO_ARENA_(attrib.material_ids, tmp_attrib.material_ids);
13028
0
#undef TINYOBJ_COPY_VEC_TO_ARENA_
13029
    // Flatten per-shape smoothing_group_ids into attrib
13030
0
    {
13031
0
      size_t total_sg = 0;
13032
0
      for (size_t si = 0; si < tmp_shapes.size(); si++)
13033
0
        total_sg += tmp_shapes[si].mesh.smoothing_group_ids.size();
13034
0
      if (total_sg > 0) {
13035
0
        attrib.smoothing_group_ids.allocate(arena, total_sg);
13036
0
        size_t off = 0;
13037
0
        for (size_t si = 0; si < tmp_shapes.size(); si++) {
13038
0
          const auto &sg = tmp_shapes[si].mesh.smoothing_group_ids;
13039
0
          if (!sg.empty()) {
13040
0
            std::memcpy(&attrib.smoothing_group_ids[off], sg.data(),
13041
0
                        sg.size() * sizeof(sg[0]));
13042
0
            off += sg.size();
13043
0
          }
13044
0
        }
13045
0
      }
13046
0
    }
13047
0
    result->shapes.clear();
13048
0
    size_t idx_off = 0, face_off = 0;
13049
0
    for (size_t si = 0; si < tmp_shapes.size(); si++) {
13050
0
      OptShapeRange sr;
13051
0
      sr.name = tmp_shapes[si].name;
13052
0
      sr.face_offset = face_off;
13053
0
      sr.face_count = tmp_shapes[si].mesh.num_face_vertices.size();
13054
0
      sr.index_offset = idx_off;
13055
0
      sr.index_count = tmp_shapes[si].mesh.indices.size();
13056
0
      face_off += sr.face_count;
13057
0
      idx_off += sr.index_count;
13058
0
      result->shapes.push_back(std::move(sr));
13059
0
    }
13060
0
    result->materials = tmp_materials;
13061
0
    result->valid = true;
13062
0
    return true;
13063
0
  }
13064
13065
  // ---- Phase 2: parse lines (compact storage) ----
13066
0
  const size_t num_t = static_cast<size_t>(num_threads);
13067
0
  std::vector<OptThreadData> thread_data(num_t);
13068
13069
#ifdef TINYOBJLOADER_USE_MULTITHREADING
13070
  {
13071
    size_t lines_per_thread = total_lines / num_t;
13072
    std::vector<std::thread> workers;
13073
    workers.reserve(num_t);
13074
13075
    for (int t = 0; t < num_threads; t++) {
13076
      size_t start = static_cast<size_t>(t) * lines_per_thread;
13077
      size_t end = (t == num_threads - 1)
13078
                       ? total_lines
13079
                       : (static_cast<size_t>(t) + 1) * lines_per_thread;
13080
13081
      workers.emplace_back([&, t, start, end]() {
13082
        OptThreadData &td = thread_data[static_cast<size_t>(t)];
13083
        size_t est_lines = end - start;
13084
        td.v_pos.reserve(est_lines * 2);
13085
        td.faces.reserve(est_lines / 3);
13086
        td.seq.reserve(est_lines / 3);
13087
        OptFloatCache *tc = nullptr;
13088
#ifndef TINYOBJLOADER_DISABLE_FAST_FLOAT
13089
        OptFloatCache thread_cache(config.float_cache_max_nodes,
13090
                                   config.fp32_cache);
13091
        if (config.float_cache) tc = &thread_cache;
13092
#endif
13093
        for (size_t i = start; i < end; i++) {
13094
          opt_parseLineToThreadData(td, &work_buf[all_line_infos[i].pos],
13095
                                     all_line_infos[i].len, config.triangulate,
13096
                                     i + 1, tc);
13097
          if (td.error_line != 0) break;
13098
        }
13099
      });
13100
    }
13101
    for (auto &w : workers) w.join();
13102
  }
13103
#else
13104
0
  {
13105
0
    OptThreadData &td = thread_data[0];
13106
0
    td.v_pos.reserve(total_lines * 2);
13107
0
    td.faces.reserve(total_lines / 3);
13108
0
    td.seq.reserve(total_lines / 3);
13109
0
    OptFloatCache *tc = nullptr;
13110
0
#ifndef TINYOBJLOADER_DISABLE_FAST_FLOAT
13111
0
    OptFloatCache thread_cache(config.float_cache_max_nodes,
13112
0
                               config.fp32_cache);
13113
0
    if (config.float_cache) tc = &thread_cache;
13114
0
#endif
13115
0
    for (size_t i = 0; i < total_lines; i++) {
13116
0
      opt_parseLineToThreadData(td, &work_buf[all_line_infos[i].pos],
13117
0
                                 all_line_infos[i].len, config.triangulate,
13118
0
                                 i + 1, tc);
13119
0
      if (td.error_line != 0) break;
13120
0
    }
13121
0
  }
13122
0
#endif
13123
13124
  // Check for parse errors
13125
0
  size_t first_error_line = 0;
13126
0
  std::string first_error_message;
13127
0
  for (size_t t = 0; t < num_t; t++) {
13128
0
    if (thread_data[t].error_line == 0) continue;
13129
0
    if (first_error_line == 0 || thread_data[t].error_line < first_error_line) {
13130
0
      first_error_line = thread_data[t].error_line;
13131
0
      first_error_message = thread_data[t].error_message;
13132
0
    }
13133
0
  }
13134
13135
  // ---- Phase 3: process sequential material state ----
13136
  // Compute v/vn/vt prefix sums across threads
13137
0
  std::vector<size_t> v_prefix(num_t), vn_prefix(num_t), vt_prefix(num_t);
13138
0
  v_prefix[0] = vn_prefix[0] = vt_prefix[0] = 0;
13139
0
  for (size_t t = 1; t < num_t; t++) {
13140
0
    v_prefix[t] = v_prefix[t - 1] + thread_data[t - 1].num_v;
13141
0
    vn_prefix[t] = vn_prefix[t - 1] + thread_data[t - 1].num_vn;
13142
0
    vt_prefix[t] = vt_prefix[t - 1] + thread_data[t - 1].num_vt;
13143
0
  }
13144
13145
0
  std::map<std::string, int> material_map;
13146
0
  std::vector<int> initial_material_id(num_t, -1);
13147
0
  size_t eof_pending_degenerate_faces = 0;
13148
0
  size_t phase3_error_line = 0;
13149
0
  std::string phase3_error_message;
13150
0
  {
13151
0
    MaterialFileReader mat_file_reader(mtl_basedir);
13152
0
    std::set<std::string> material_filenames;
13153
0
    std::vector<material_t> *material_dst = &result->materials;
13154
0
    int running_material_id = -1;
13155
0
    size_t pending_degenerate_faces = 0;
13156
13157
0
    for (size_t t = 0; t < num_t; t++) {
13158
0
      if (phase3_error_line != 0) break;
13159
0
      initial_material_id[t] = running_material_id;
13160
0
      const OptThreadData &td = thread_data[t];
13161
13162
0
      for (size_t si = 0; si < td.seq.size(); si++) {
13163
0
        const OptSeqEntry &entry = td.seq[si];
13164
13165
0
        if (entry.kind == OptSeqEntry::SEQ_FACE) {
13166
0
          const OptFaceCmd &face = td.faces[entry.index];
13167
0
          int rv = static_cast<int>(v_prefix[t] + face.v_count_before);
13168
0
          int rvn = static_cast<int>(vn_prefix[t] + face.vn_count_before);
13169
0
          int rvt = static_cast<int>(vt_prefix[t] + face.vt_count_before);
13170
13171
0
          if (first_error_line != 0 && face.source_line >= first_error_line) continue;
13172
13173
0
          if (face.degenerate) {
13174
0
            pending_degenerate_faces++;
13175
0
            continue;
13176
0
          }
13177
13178
0
          for (size_t k = 0; k < face.face_vertex_count; k++) {
13179
0
            const opt_index_t &raw = face.face_indices()[k];
13180
0
            int resolved_idx = -1;
13181
0
            if (!opt_validateAndResolveFaceIndexLikeLegacy(
13182
0
                    raw.vertex_index, rv, false, source_name,
13183
0
                    face.source_line, warn, &resolved_idx)) {
13184
0
              phase3_error_line = face.source_line;
13185
0
              phase3_error_message = "failed to parse `f' line (invalid vertex index)";
13186
0
              break;
13187
0
            }
13188
0
            if (raw.texcoord_index != opt_index_t::kNotPresent) {
13189
0
              if (!opt_validateAndResolveFaceIndexLikeLegacy(
13190
0
                      raw.texcoord_index, rvt, true, source_name,
13191
0
                      face.source_line, warn, &resolved_idx)) {
13192
0
                phase3_error_line = face.source_line;
13193
0
                phase3_error_message = "failed to parse `f' line (invalid vertex index)";
13194
0
                break;
13195
0
              }
13196
0
            }
13197
0
            if (raw.normal_index != opt_index_t::kNotPresent) {
13198
0
              if (!opt_validateAndResolveFaceIndexLikeLegacy(
13199
0
                      raw.normal_index, rvn, true, source_name,
13200
0
                      face.source_line, warn, &resolved_idx)) {
13201
0
                phase3_error_line = face.source_line;
13202
0
                phase3_error_message = "failed to parse `f' line (invalid vertex index)";
13203
0
                break;
13204
0
              }
13205
0
            }
13206
0
          }
13207
0
          if (phase3_error_line != 0) break;
13208
0
          continue;
13209
0
        }
13210
13211
        // SEQ_META
13212
0
        OptMetaCmd &meta = thread_data[t].metas[entry.index];
13213
13214
0
        if (first_error_line != 0 && meta.source_line >= first_error_line) continue;
13215
13216
0
        if (meta.type == OPT_CMD_G || meta.type == OPT_CMD_O) {
13217
0
          for (size_t deg = 0; deg < pending_degenerate_faces; deg++) {
13218
0
            if (warn) (*warn) += "Degenerated face found\n.";
13219
0
          }
13220
0
          pending_degenerate_faces = 0;
13221
0
        }
13222
13223
0
        if (meta.type == OPT_CMD_G && meta.group_name_empty) {
13224
0
          if (warn) {
13225
0
            (*warn) += "Empty group name. line: " +
13226
0
                       std::to_string(meta.source_line) + "\n";
13227
0
          }
13228
0
          continue;
13229
0
        }
13230
13231
0
        if (meta.type == OPT_CMD_MTLLIB) {
13232
0
          if (!enable_mtllib_loading) continue;
13233
0
          std::string line_rest;
13234
0
          if (meta.str_ptr && meta.str_len > 0) {
13235
0
            line_rest.assign(meta.str_ptr, meta.str_len);
13236
0
          }
13237
0
          std::vector<std::string> filenames;
13238
0
          SplitString(line_rest, ' ', '\\', filenames);
13239
0
          RemoveEmptyTokens(&filenames);
13240
0
          if (filenames.empty()) {
13241
0
            if (warn) {
13242
0
              (*warn) += "Looks like empty filename for mtllib. Use default "
13243
0
                         "material (line " +
13244
0
                         std::to_string(meta.source_line) + ".)\n";
13245
0
            }
13246
0
            continue;
13247
0
          }
13248
0
          bool found = false;
13249
0
          for (size_t s = 0; s < filenames.size(); s++) {
13250
0
            if (material_filenames.count(filenames[s]) > 0) {
13251
0
              found = true;
13252
0
              continue;
13253
0
            }
13254
0
            std::string warn_mtl, err_mtl;
13255
0
            bool ok = mat_file_reader(filenames[s], material_dst, &material_map,
13256
0
                                      &warn_mtl, &err_mtl);
13257
0
            if (warn && !warn_mtl.empty()) (*warn) += warn_mtl;
13258
0
            if (err && !err_mtl.empty()) (*err) += err_mtl;
13259
0
            if (ok) {
13260
0
              found = true;
13261
0
              material_filenames.insert(filenames[s]);
13262
0
              break;
13263
0
            }
13264
0
          }
13265
0
          if (!found && warn) {
13266
0
            (*warn) += "Failed to load material file(s). Use default material.\n";
13267
0
          }
13268
0
          continue;
13269
0
        }
13270
13271
0
        if (meta.type == OPT_CMD_USEMTL) {
13272
0
          std::string mat_name;
13273
0
          if (meta.str_ptr && meta.str_len > 0) {
13274
0
            mat_name.assign(meta.str_ptr, meta.str_len);
13275
0
          }
13276
0
          while (!mat_name.empty() &&
13277
0
                 (mat_name.back() == '\r' || mat_name.back() == '\n')) {
13278
0
            mat_name.pop_back();
13279
0
          }
13280
0
          std::map<std::string, int>::const_iterator it = material_map.find(mat_name);
13281
0
          if (it != material_map.end()) {
13282
0
            meta.resolved_material_id = it->second;
13283
0
          } else {
13284
0
            meta.resolved_material_id = -1;
13285
0
            if (warn) (*warn) += "material [ '" + mat_name + "' ] not found in .mtl\n";
13286
0
          }
13287
0
          running_material_id = meta.resolved_material_id;
13288
0
          continue;
13289
0
        }
13290
0
      }
13291
13292
0
    }
13293
0
    if (first_error_line == 0) {
13294
0
      eof_pending_degenerate_faces = pending_degenerate_faces;
13295
0
    }
13296
0
  }
13297
13298
0
  if (phase3_error_line != 0) {
13299
0
    if (err) {
13300
0
      (*err) += "Failed parse line(line " +
13301
0
                std::to_string(phase3_error_line) + "). " +
13302
0
                phase3_error_message + "\n";
13303
0
    }
13304
0
    return false;
13305
0
  }
13306
13307
0
  if (first_error_line != 0) {
13308
0
    if (err) {
13309
0
      (*err) += "Failed parse line(line " +
13310
0
                std::to_string(first_error_line) + "). " +
13311
0
                first_error_message + "\n";
13312
0
    }
13313
0
    return false;
13314
0
  }
13315
13316
  // ---- Phase 4: allocate arena arrays and merge ----
13317
0
  size_t num_v = 0, num_vn = 0, num_vt = 0;
13318
0
  size_t total_idx = 0;   // total index_t entries across all faces
13319
0
  size_t total_faces = 0; // total emitted faces
13320
0
  for (size_t t = 0; t < num_t; t++) {
13321
0
    num_v += thread_data[t].num_v;
13322
0
    num_vn += thread_data[t].num_vn;
13323
0
    num_vt += thread_data[t].num_vt;
13324
0
    total_idx += thread_data[t].num_f_indices;
13325
0
    total_faces += thread_data[t].num_f_faces;
13326
0
  }
13327
13328
  // Guard against size_t overflow in vertex/normal/texcoord allocation
13329
0
  if (num_v > SIZE_MAX / 3 || num_vn > SIZE_MAX / 3 || num_vt > SIZE_MAX / 2) {
13330
0
    if (err) {
13331
0
      (*err) += "Integer overflow in vertex/normal/texcoord count.\n";
13332
0
    }
13333
0
    return false;
13334
0
  }
13335
13336
  // Determine which optional arrays are needed
13337
0
  bool any_color = false;
13338
0
  bool any_weight = false, any_texcoord_w = false, any_smoothing = false;
13339
0
  for (size_t t = 0; t < num_t; t++) {
13340
0
    if (thread_data[t].saw_any_color) any_color = true;
13341
0
    if (thread_data[t].saw_any_weight) any_weight = true;
13342
0
    if (thread_data[t].saw_any_texcoord_w) any_texcoord_w = true;
13343
0
    if (thread_data[t].saw_any_smoothing) any_smoothing = true;
13344
0
  }
13345
13346
  // Allocate from arena
13347
0
  attrib.vertices.allocate(arena, num_v * 3);
13348
0
  if (any_weight) {
13349
0
    attrib.vertex_weights.allocate(arena, num_v);
13350
0
    for (size_t i = 0; i < num_v; i++)
13351
0
      attrib.vertex_weights[i] = real_t(1.0);
13352
0
  }
13353
0
  attrib.normals.allocate(arena, num_vn * 3);
13354
0
  attrib.texcoords.allocate(arena, num_vt * 2);
13355
0
  if (any_texcoord_w) {
13356
0
    attrib.texcoord_ws.allocate(arena, num_vt);
13357
0
  }
13358
0
  attrib.indices.allocate(arena, total_idx);
13359
0
  attrib.face_num_verts.allocate(arena, total_faces);
13360
0
  attrib.material_ids.allocate(arena, total_faces);
13361
0
  for (size_t i = 0; i < total_faces; i++)
13362
0
    attrib.material_ids[i] = -1;
13363
13364
  // Smoothing group ids — only if any smoothing commands seen
13365
0
  TypedArray<unsigned int> all_smoothing_group_ids;
13366
0
  if (any_smoothing) {
13367
0
    all_smoothing_group_ids.allocate(arena, total_faces);
13368
0
  }
13369
13370
  // Colors — allocate temp only if any vertex has color
13371
0
  TypedArray<real_t> all_colors;
13372
0
  if (any_color) {
13373
0
    all_colors.allocate(arena, num_v * 3);
13374
0
    for (size_t i = 0; i < num_v * 3; i++)
13375
0
      all_colors[i] = real_t(1.0);
13376
0
  }
13377
13378
  // Compute per-thread offsets
13379
0
  std::vector<size_t> v_off(num_t), n_off(num_t), t_off(num_t), f_off(num_t),
13380
0
      face_off(num_t);
13381
0
  v_off[0] = n_off[0] = t_off[0] = f_off[0] = face_off[0] = 0;
13382
0
  for (size_t t = 1; t < num_t; t++) {
13383
0
    v_off[t] = v_off[t - 1] + thread_data[t - 1].num_v;
13384
0
    n_off[t] = n_off[t - 1] + thread_data[t - 1].num_vn;
13385
0
    t_off[t] = t_off[t - 1] + thread_data[t - 1].num_vt;
13386
0
    f_off[t] = f_off[t - 1] + thread_data[t - 1].num_f_indices;
13387
0
    face_off[t] = face_off[t - 1] + thread_data[t - 1].num_f_faces;
13388
0
  }
13389
13390
  // Carry smoothing group state across thread boundaries
13391
0
  std::vector<unsigned int> initial_smoothing_group_id(num_t, 0);
13392
0
  unsigned int running_smoothing_group_id = 0;
13393
0
  for (size_t t = 0; t < num_t; t++) {
13394
0
    initial_smoothing_group_id[t] = running_smoothing_group_id;
13395
0
    for (size_t mi = 0; mi < thread_data[t].metas.size(); mi++) {
13396
0
      if (thread_data[t].metas[mi].type == OPT_CMD_S) {
13397
0
        running_smoothing_group_id = thread_data[t].metas[mi].smoothing_group_id;
13398
0
      }
13399
0
    }
13400
0
  }
13401
13402
  // Per-thread face tracking for shape construction
13403
0
  std::vector<std::vector<unsigned int>> command_written_faces(num_t);
13404
0
  for (size_t t = 0; t < num_t; t++) {
13405
0
    command_written_faces[t].assign(thread_data[t].faces.size(), 0);
13406
0
  }
13407
0
  std::vector<size_t> written_index_counts(num_t, 0);
13408
0
  std::vector<size_t> written_face_counts(num_t, 0);
13409
0
  std::vector<size_t> merge_error_lines(num_t, 0);
13410
0
  std::vector<std::string> merge_error_messages(num_t);
13411
0
  std::vector<int> thread_greatest_v_idx(num_t, -1);
13412
0
  std::vector<int> thread_greatest_vn_idx(num_t, -1);
13413
0
  std::vector<int> thread_greatest_vt_idx(num_t, -1);
13414
0
  std::vector<unsigned char> thread_saw_any_vertex_color(num_t, 0);
13415
0
  std::vector<unsigned char> thread_missing_vertex_color(num_t, 0);
13416
13417
0
  auto merge_vertex_thread = [&](size_t t) {
13418
0
    const OptThreadData &td = thread_data[t];
13419
    // Bulk copy positions
13420
0
    if (!td.v_pos.empty()) {
13421
0
      std::memcpy(&attrib.vertices[v_off[t] * 3], td.v_pos.data(),
13422
0
                  td.v_pos.size() * sizeof(real_t));
13423
0
    }
13424
    // Copy weights
13425
0
    if (any_weight && td.saw_any_weight) {
13426
0
      for (size_t i = 0; i < td.num_v; i++)
13427
0
        attrib.vertex_weights[v_off[t] + i] = td.v_weight[i];
13428
0
    }
13429
    // Copy colors
13430
0
    if (any_color) {
13431
0
      if (td.saw_any_color) {
13432
0
        std::memcpy(&all_colors[v_off[t] * 3], td.v_color.data(),
13433
0
                    td.v_color.size() * sizeof(real_t));
13434
0
        thread_saw_any_vertex_color[t] = 1;
13435
0
        if (td.saw_missing_color) thread_missing_vertex_color[t] = 1;
13436
0
      } else if (td.saw_missing_color) {
13437
0
        thread_missing_vertex_color[t] = 1;
13438
0
      }
13439
0
    }
13440
    // Bulk copy normals
13441
0
    if (!td.vn_data.empty()) {
13442
0
      std::memcpy(&attrib.normals[n_off[t] * 3], td.vn_data.data(),
13443
0
                  td.vn_data.size() * sizeof(real_t));
13444
0
    }
13445
    // Bulk copy texcoords
13446
0
    if (!td.vt_data.empty()) {
13447
0
      std::memcpy(&attrib.texcoords[t_off[t] * 2], td.vt_data.data(),
13448
0
                  td.vt_data.size() * sizeof(real_t));
13449
0
    }
13450
0
    if (any_texcoord_w && td.saw_any_texcoord_w) {
13451
0
      for (size_t i = 0; i < td.num_vt; i++)
13452
0
        attrib.texcoord_ws[t_off[t] + i] = td.vt_w[i];
13453
0
    }
13454
0
  };
13455
13456
0
  auto merge_thread = [&](size_t t) {
13457
0
    const OptThreadData &td = thread_data[t];
13458
0
    size_t fc = f_off[t], fcc = face_off[t];
13459
0
    int current_mat_id = initial_material_id[t];
13460
0
    unsigned int current_smoothing_id = initial_smoothing_group_id[t];
13461
0
    int greatest_v_idx = -1;
13462
0
    int greatest_vn_idx = -1;
13463
0
    int greatest_vt_idx = -1;
13464
13465
    // Reusable scratch buffer for resolved face indices
13466
0
    index_t resolved_inline[8];
13467
0
    std::vector<index_t> resolved_heap;
13468
13469
0
    for (size_t si = 0; si < td.seq.size(); si++) {
13470
0
      const OptSeqEntry &entry = td.seq[si];
13471
0
      if (entry.kind == OptSeqEntry::SEQ_META) {
13472
0
        const OptMetaCmd &meta = td.metas[entry.index];
13473
0
        if (meta.type == OPT_CMD_USEMTL)
13474
0
          current_mat_id = meta.resolved_material_id;
13475
0
        else if (meta.type == OPT_CMD_S)
13476
0
          current_smoothing_id = meta.smoothing_group_id;
13477
0
        continue;
13478
0
      }
13479
      // SEQ_FACE
13480
0
      const OptFaceCmd &face = td.faces[entry.index];
13481
0
      if (face.degenerate) {
13482
0
        command_written_faces[t][entry.index] = 0;
13483
0
        continue;
13484
0
      }
13485
      // Compute running counts for index resolution
13486
0
      size_t vc = v_off[t] + face.v_count_before;
13487
0
      size_t nc = n_off[t] + face.vn_count_before;
13488
0
      size_t tc = t_off[t] + face.vt_count_before;
13489
13490
      // Use stack buffer for typical faces, heap only for large polygons
13491
0
      index_t *resolved_face;
13492
0
      if (face.face_vertex_count <= 8) {
13493
0
        resolved_face = resolved_inline;
13494
0
      } else {
13495
0
        resolved_heap.resize(face.face_vertex_count);
13496
0
        resolved_face = resolved_heap.data();
13497
0
      }
13498
0
      for (size_t k = 0; k < face.face_vertex_count; k++) {
13499
0
        const opt_index_t &vi = face.face_indices()[k];
13500
0
        index_t idx;
13501
0
        if (!opt_resolveIndexLikeLegacy(vi.vertex_index,
13502
0
                                        static_cast<int>(vc),
13503
0
                                        &idx.vertex_index, false)) {
13504
0
          merge_error_lines[t] = face.source_line;
13505
0
          merge_error_messages[t] =
13506
0
              "failed to parse `f' line (invalid vertex index)";
13507
0
          return;
13508
0
        }
13509
0
        opt_updateGreatestIndex(idx.vertex_index, &greatest_v_idx);
13510
0
        if (vi.texcoord_index == opt_index_t::kNotPresent) {
13511
0
          idx.texcoord_index = -1;
13512
0
        } else {
13513
0
          if (!opt_resolveIndexLikeLegacy(vi.texcoord_index,
13514
0
                                          static_cast<int>(tc),
13515
0
                                          &idx.texcoord_index, true)) {
13516
0
            merge_error_lines[t] = face.source_line;
13517
0
            merge_error_messages[t] =
13518
0
                "failed to parse `f' line (invalid vertex index)";
13519
0
            return;
13520
0
          }
13521
0
          if (idx.texcoord_index >= 0)
13522
0
            opt_updateGreatestIndex(idx.texcoord_index, &greatest_vt_idx);
13523
0
        }
13524
0
        if (vi.normal_index == opt_index_t::kNotPresent) {
13525
0
          idx.normal_index = -1;
13526
0
        } else {
13527
0
          if (!opt_resolveIndexLikeLegacy(vi.normal_index,
13528
0
                                          static_cast<int>(nc),
13529
0
                                          &idx.normal_index, true)) {
13530
0
            merge_error_lines[t] = face.source_line;
13531
0
            merge_error_messages[t] =
13532
0
                "failed to parse `f' line (invalid vertex index)";
13533
0
            return;
13534
0
          }
13535
0
          if (idx.normal_index >= 0)
13536
0
            opt_updateGreatestIndex(idx.normal_index, &greatest_vn_idx);
13537
0
        }
13538
0
        resolved_face[k] = idx;
13539
0
      }
13540
0
      size_t written_index_count = 0;
13541
0
      size_t written_face_count = 0;
13542
0
      if (config.triangulate) {
13543
0
        written_index_count = opt_triangulate_face(
13544
0
            attrib.vertices.data(), attrib.vertices.size(),
13545
0
            resolved_face, face.face_vertex_count,
13546
0
            &attrib.indices[fc]);
13547
0
        written_face_count = written_index_count / 3;
13548
0
      } else {
13549
0
        written_index_count = face.face_vertex_count;
13550
0
        written_face_count = 1;
13551
0
        for (size_t k = 0; k < face.face_vertex_count; k++) {
13552
0
          attrib.indices[fc + k] = resolved_face[k];
13553
0
        }
13554
0
      }
13555
0
      for (size_t k = 0; k < written_face_count; k++) {
13556
0
        attrib.face_num_verts[fcc + k] = config.triangulate
13557
0
                                            ? 3
13558
0
                                            : static_cast<int>(face.emitted_face_verts);
13559
0
        attrib.material_ids[fcc + k] = current_mat_id;
13560
0
        if (any_smoothing) {
13561
0
          all_smoothing_group_ids[fcc + k] = current_smoothing_id;
13562
0
        }
13563
0
      }
13564
0
      command_written_faces[t][entry.index] =
13565
0
          static_cast<unsigned int>(written_face_count);
13566
0
      fc += written_index_count;
13567
0
      fcc += written_face_count;
13568
0
    }
13569
0
    written_index_counts[t] = fc - f_off[t];
13570
0
    written_face_counts[t] = fcc - face_off[t];
13571
0
    thread_greatest_v_idx[t] = greatest_v_idx;
13572
0
    thread_greatest_vn_idx[t] = greatest_vn_idx;
13573
0
    thread_greatest_vt_idx[t] = greatest_vt_idx;
13574
0
  };
13575
13576
#ifdef TINYOBJLOADER_USE_MULTITHREADING
13577
  if (num_threads > 1) {
13578
    std::vector<std::thread> workers;
13579
    workers.reserve(num_t);
13580
    for (size_t t = 0; t < num_t; t++) {
13581
      workers.emplace_back([&, t]() { merge_vertex_thread(t); });
13582
    }
13583
    for (auto &w : workers) w.join();
13584
    workers.clear();
13585
    for (size_t t = 0; t < num_t; t++) {
13586
      workers.emplace_back([&, t]() { merge_thread(t); });
13587
    }
13588
    for (auto &w : workers) w.join();
13589
  } else {
13590
    for (size_t t = 0; t < num_t; t++) merge_vertex_thread(t);
13591
    for (size_t t = 0; t < num_t; t++) merge_thread(t);
13592
  }
13593
#else
13594
0
  for (size_t t = 0; t < num_t; t++) merge_vertex_thread(t);
13595
0
  for (size_t t = 0; t < num_t; t++) merge_thread(t);
13596
0
#endif
13597
13598
  // Check merge errors
13599
0
  size_t first_merge_error_line = 0;
13600
0
  std::string first_merge_error_message;
13601
0
  for (size_t t = 0; t < merge_error_lines.size(); t++) {
13602
0
    if (merge_error_lines[t] == 0) continue;
13603
0
    if (first_merge_error_line == 0 ||
13604
0
        merge_error_lines[t] < first_merge_error_line) {
13605
0
      first_merge_error_line = merge_error_lines[t];
13606
0
      first_merge_error_message = merge_error_messages[t];
13607
0
    }
13608
0
  }
13609
13610
0
  if (first_merge_error_line != 0) {
13611
0
    if (err) {
13612
0
      (*err) += "Failed parse line(line " +
13613
0
                std::to_string(first_merge_error_line) + "). " +
13614
0
                first_merge_error_message + "\n";
13615
0
    }
13616
0
    return false;
13617
0
  }
13618
13619
  // Handle vertex colors: only keep if all vertices have color
13620
0
  {
13621
0
    bool saw_any = false, saw_missing = false;
13622
0
    for (size_t t = 0; t < num_t; t++) {
13623
0
      saw_any = saw_any || (thread_saw_any_vertex_color[t] != 0);
13624
0
      saw_missing = saw_missing || (thread_missing_vertex_color[t] != 0);
13625
0
    }
13626
0
    if (saw_any && !saw_missing) {
13627
0
      attrib.colors = all_colors;  // share arena pointer
13628
0
    }
13629
    // else attrib.colors stays empty (default)
13630
0
  }
13631
13632
  // Compute actual sizes and compact/truncate
13633
0
  size_t actual_num_indices = 0;
13634
0
  size_t actual_num_faces = 0;
13635
0
  int greatest_v_idx = -1, greatest_vn_idx = -1, greatest_vt_idx = -1;
13636
0
  for (size_t t = 0; t < num_t; t++) {
13637
0
    actual_num_indices += written_index_counts[t];
13638
0
    actual_num_faces += written_face_counts[t];
13639
0
    opt_updateGreatestIndex(thread_greatest_v_idx[t], &greatest_v_idx);
13640
0
    opt_updateGreatestIndex(thread_greatest_vn_idx[t], &greatest_vn_idx);
13641
0
    opt_updateGreatestIndex(thread_greatest_vt_idx[t], &greatest_vt_idx);
13642
0
  }
13643
13644
  // Compact face arrays to remove gaps left by threads that wrote fewer
13645
  // indices/faces than pre-allocated (e.g. triangulation returning 0 for
13646
  // faces with out-of-bounds vertex indices).
13647
0
  if (num_t > 1) {
13648
0
    size_t dst_idx = 0;
13649
0
    size_t dst_face = 0;
13650
0
    for (size_t t = 0; t < num_t; t++) {
13651
0
      size_t src_idx = f_off[t];
13652
0
      size_t src_face = face_off[t];
13653
0
      size_t idx_count = written_index_counts[t];
13654
0
      size_t fc_count = written_face_counts[t];
13655
0
      if (dst_idx != src_idx && idx_count > 0) {
13656
0
        std::memmove(&attrib.indices[dst_idx], &attrib.indices[src_idx],
13657
0
                     idx_count * sizeof(index_t));
13658
0
      }
13659
0
      if (dst_face != src_face && fc_count > 0) {
13660
0
        std::memmove(&attrib.face_num_verts[dst_face],
13661
0
                     &attrib.face_num_verts[src_face],
13662
0
                     fc_count * sizeof(int));
13663
0
        std::memmove(&attrib.material_ids[dst_face],
13664
0
                     &attrib.material_ids[src_face],
13665
0
                     fc_count * sizeof(int));
13666
0
        if (any_smoothing) {
13667
0
          std::memmove(&all_smoothing_group_ids[dst_face],
13668
0
                       &all_smoothing_group_ids[src_face],
13669
0
                       fc_count * sizeof(unsigned int));
13670
0
        }
13671
0
      }
13672
0
      dst_idx += idx_count;
13673
0
      dst_face += fc_count;
13674
0
    }
13675
0
  }
13676
13677
0
  attrib.indices.truncate(actual_num_indices);
13678
0
  attrib.face_num_verts.truncate(actual_num_faces);
13679
0
  attrib.material_ids.truncate(actual_num_faces);
13680
0
  if (any_smoothing) {
13681
0
    all_smoothing_group_ids.truncate(actual_num_faces);
13682
0
    attrib.smoothing_group_ids = all_smoothing_group_ids;
13683
0
  }
13684
13685
0
  opt_appendOutOfBoundsWarnings(
13686
0
      warn, greatest_v_idx, greatest_vn_idx, greatest_vt_idx,
13687
0
      static_cast<int>(attrib.vertices.size() / 3),
13688
0
      static_cast<int>(attrib.normals.size() / 3),
13689
0
      static_cast<int>(attrib.texcoords.size() / 2),
13690
0
      all_line_infos.size() + 1);
13691
0
  for (size_t deg = 0; deg < eof_pending_degenerate_faces; deg++) {
13692
0
    if (warn) (*warn) += "Degenerated face found\n.";
13693
0
  }
13694
13695
  // ---- Phase 5: construct shape ranges (views, no copies) ----
13696
0
  {
13697
0
    const size_t total_faces = attrib.face_num_verts.size();
13698
    // Build prefix-sum for index offsets
13699
0
    TypedArray<size_t> idx_prefix;
13700
0
    idx_prefix.allocate(arena, total_faces + 1);
13701
0
    idx_prefix[0] = 0;
13702
0
    for (size_t fi = 0; fi < total_faces; fi++) {
13703
0
      idx_prefix[fi + 1] =
13704
0
          idx_prefix[fi] + static_cast<size_t>(attrib.face_num_verts[fi]);
13705
0
    }
13706
13707
0
    size_t face_count = 0;
13708
0
    std::string current_name;
13709
0
    size_t face_prev_offset = 0;
13710
0
    bool have_active_shape_name = false;
13711
13712
0
    auto emit_shape = [&](const std::string &name, size_t f_start, size_t f_end) {
13713
0
      if (f_end <= f_start) return;
13714
0
      OptShapeRange sr;
13715
0
      sr.name = name;
13716
0
      sr.face_offset = f_start;
13717
0
      sr.face_count = f_end - f_start;
13718
0
      sr.index_offset = idx_prefix[f_start];
13719
0
      sr.index_count = idx_prefix[f_end] - idx_prefix[f_start];
13720
0
      result->shapes.push_back(std::move(sr));
13721
0
    };
13722
13723
0
    for (size_t t = 0; t < num_t; t++) {
13724
0
      const OptThreadData &td = thread_data[t];
13725
0
      for (size_t si = 0; si < td.seq.size(); si++) {
13726
0
        const OptSeqEntry &entry = td.seq[si];
13727
0
        if (entry.kind == OptSeqEntry::SEQ_META) {
13728
0
          const OptMetaCmd &meta = td.metas[entry.index];
13729
0
          if (meta.type == OPT_CMD_O || meta.type == OPT_CMD_G) {
13730
0
            std::string name;
13731
0
            if (meta.type == OPT_CMD_O && meta.str_ptr) {
13732
0
              name.assign(meta.str_ptr, meta.str_len);
13733
0
            } else if (!meta.str_storage.empty()) {
13734
0
              name = meta.str_storage;
13735
0
            } else if (meta.str_ptr) {
13736
0
              name.assign(meta.str_ptr, meta.str_len);
13737
0
            }
13738
0
            while (!name.empty() &&
13739
0
                   (name.back() == '\r' || name.back() == '\n'))
13740
0
              name.pop_back();
13741
13742
0
            if (face_count == 0) {
13743
0
              current_name = name;
13744
0
              face_prev_offset = 0;
13745
0
              have_active_shape_name = true;
13746
0
            } else {
13747
0
              if (!have_active_shape_name) {
13748
0
                emit_shape(std::string(), 0, face_count);
13749
0
              } else if (face_count > face_prev_offset) {
13750
0
                emit_shape(current_name, face_prev_offset, face_count);
13751
0
              }
13752
0
              current_name = name;
13753
0
              face_prev_offset = face_count;
13754
0
              have_active_shape_name = true;
13755
0
            }
13756
0
          }
13757
0
        } else {
13758
          // SEQ_FACE
13759
0
          face_count += command_written_faces[t][entry.index];
13760
0
        }
13761
0
      }
13762
0
    }
13763
13764
    // Final shape
13765
0
    if (face_count > face_prev_offset || face_count == 0) {
13766
0
      emit_shape(current_name, face_prev_offset, face_count);
13767
0
    }
13768
0
  }
13769
13770
0
  result->valid = true;
13771
0
  return true;
13772
0
}
13773
13774
// ---- LoadObjOptTyped (buffer version, public API) ----
13775
13776
OptResult LoadObjOptTyped(const char *buf, size_t buf_len,
13777
                          std::string *warn, std::string *err,
13778
0
                          const OptLoadConfig &config) {
13779
0
  OptResult result;
13780
0
  LoadObjOptTyped_internal(&result, warn, err, buf, buf_len,
13781
0
                           std::string(), "<stream>", false, config);
13782
0
  return result;
13783
0
}
13784
13785
// ---- LoadObjOptTyped (file version) ----
13786
13787
OptResult LoadObjOptTyped(const char *filename,
13788
                          std::string *warn, std::string *err,
13789
                          const char *mtl_basedir,
13790
0
                          const OptLoadConfig &config) {
13791
0
  OptResult result;
13792
0
  if (!filename) {
13793
0
    if (err) *err = "filename is null.";
13794
0
    return result;
13795
0
  }
13796
13797
0
  std::string filepath(filename);
13798
0
  std::string baseDir;
13799
0
  if (mtl_basedir) {
13800
0
    baseDir = mtl_basedir;
13801
0
  } else {
13802
0
    size_t pos = filepath.find_last_of("/\\");
13803
0
    if (pos != std::string::npos) {
13804
0
      baseDir = filepath.substr(0, pos + 1);
13805
0
    }
13806
0
  }
13807
0
  if (!baseDir.empty()) {
13808
0
#ifndef _WIN32
13809
0
    const char dirsep = '/';
13810
#else
13811
    const char dirsep = '\\';
13812
#endif
13813
0
    if (baseDir[baseDir.length() - 1] != dirsep) baseDir += dirsep;
13814
0
  }
13815
13816
#ifdef TINYOBJLOADER_USE_MMAP
13817
  {
13818
    MappedFile mf;
13819
    if (mf.open(filepath.c_str())) {
13820
      LoadObjOptTyped_internal(&result, warn, err, mf.data, mf.size,
13821
                               baseDir, filepath, true, config);
13822
      return result;
13823
    }
13824
  }
13825
#endif
13826
13827
#ifdef _WIN32
13828
  std::ifstream ifs(LongPathW(UTF8ToWchar(filepath)).c_str(),
13829
                    std::ios::binary | std::ios::ate);
13830
#else
13831
0
  std::ifstream ifs(filepath.c_str(), std::ios::binary | std::ios::ate);
13832
0
#endif
13833
0
  if (!ifs.is_open()) {
13834
0
    if (err) *err = "Cannot open file: " + filepath;
13835
0
    return result;
13836
0
  }
13837
13838
0
  std::streamsize fsize = ifs.tellg();
13839
0
  ifs.seekg(0, std::ios::beg);
13840
13841
0
  if (fsize <= 0) {
13842
0
    LoadObjOptTyped_internal(&result, warn, err, "", 0,
13843
0
                             baseDir, filepath, true, config);
13844
0
    return result;
13845
0
  }
13846
13847
0
  std::vector<char> file_buf(static_cast<size_t>(fsize));
13848
0
  if (!ifs.read(file_buf.data(), fsize)) {
13849
0
    if (err) *err = "Failed to read file: " + filepath;
13850
0
    return result;
13851
0
  }
13852
0
  ifs.close();
13853
13854
0
  LoadObjOptTyped_internal(&result, warn, err, file_buf.data(),
13855
0
                           static_cast<size_t>(fsize), baseDir, filepath,
13856
0
                           true, config);
13857
0
  return result;
13858
0
}
13859
13860
#ifdef __clang__
13861
#pragma clang diagnostic pop
13862
#endif
13863
}  // namespace tinyobj
13864
13865
#endif