Coverage Report

Created: 2026-08-13 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tomlplusplus/include/toml++/impl/parser.inl
Line
Count
Source
1
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
2
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
3
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
4
// SPDX-License-Identifier: MIT
5
#pragma once
6
7
#include "preprocessor.hpp"
8
//# {{
9
#if !TOML_IMPLEMENTATION
10
#error This is an implementation-only header.
11
#endif
12
//# }}
13
#if TOML_ENABLE_PARSER
14
15
#include "parser.hpp"
16
#include "std_optional.hpp"
17
#include "source_region.hpp"
18
#include "parse_error.hpp"
19
#include "date_time.hpp"
20
#include "value.hpp"
21
#include "array.hpp"
22
#include "table.hpp"
23
#include "unicode.hpp"
24
TOML_DISABLE_WARNINGS;
25
#include <istream>
26
#include <fstream>
27
#if TOML_INT_CHARCONV || TOML_FLOAT_CHARCONV
28
#include <charconv>
29
#endif
30
#if !TOML_INT_CHARCONV || !TOML_FLOAT_CHARCONV
31
#include <sstream>
32
#endif
33
#if !TOML_INT_CHARCONV
34
#include <iomanip>
35
#endif
36
TOML_ENABLE_WARNINGS;
37
#include "header_start.hpp"
38
39
//#---------------------------------------------------------------------------------------------------------------------
40
//# UTF8 STREAMS
41
//#---------------------------------------------------------------------------------------------------------------------
42
43
TOML_ANON_NAMESPACE_START
44
{
45
  template <typename T>
46
  class utf8_byte_stream;
47
48
  TOML_INTERNAL_LINKAGE
49
  constexpr auto utf8_byte_order_mark = "\xEF\xBB\xBF"sv;
50
51
  template <typename Char>
52
  class utf8_byte_stream<std::basic_string_view<Char>>
53
  {
54
    static_assert(sizeof(Char) == 1);
55
56
    private:
57
    std::basic_string_view<Char> source_;
58
    size_t position_ = {};
59
60
    public:
61
    TOML_NODISCARD_CTOR
62
    explicit constexpr utf8_byte_stream(std::basic_string_view<Char> sv) noexcept //
63
7.88k
      : source_{ sv }
64
7.88k
    {
65
      // skip bom
66
7.88k
      if (source_.length() >= 3u && memcmp(utf8_byte_order_mark.data(), source_.data(), 3u) == 0)
67
44
        position_ += 3u;
68
7.88k
    }
69
70
    TOML_CONST_INLINE_GETTER
71
    constexpr bool error() const noexcept
72
1.68M
    {
73
1.68M
      return false;
74
1.68M
    }
75
76
    TOML_PURE_INLINE_GETTER
77
    constexpr bool eof() const noexcept
78
3.37M
    {
79
3.37M
      return position_ >= source_.length();
80
3.37M
    }
81
82
    TOML_PURE_INLINE_GETTER
83
    explicit constexpr operator bool() const noexcept
84
1.68M
    {
85
1.68M
      return !eof();
86
1.68M
    }
87
88
    TOML_PURE_INLINE_GETTER
89
    constexpr bool peek_eof() const noexcept
90
7.88k
    {
91
7.88k
      return eof();
92
7.88k
    }
93
94
    TOML_NODISCARD
95
    TOML_ATTR(nonnull)
96
    size_t operator()(void* dest, size_t num) noexcept
97
1.68M
    {
98
1.68M
      TOML_ASSERT_ASSUME(!eof());
99
100
1.68M
      num = impl::min(position_ + num, source_.length()) - position_;
101
1.68M
      std::memcpy(dest, source_.data() + position_, num);
102
1.68M
      position_ += num;
103
1.68M
      return num;
104
1.68M
    }
105
  };
106
107
  template <>
108
  class utf8_byte_stream<std::istream>
109
  {
110
    private:
111
    std::istream* source_;
112
113
    public:
114
    TOML_NODISCARD_CTOR
115
    explicit utf8_byte_stream(std::istream& stream) noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) //
116
      : source_{ &stream }
117
0
    {
118
0
      if (!*this) // eof, bad
119
0
        return;
120
0
121
0
      const auto initial_pos = source_->tellg();
122
0
      char bom[3];
123
0
      source_->read(bom, 3);
124
0
      if (source_->bad() || (source_->gcount() == 3 && memcmp(utf8_byte_order_mark.data(), bom, 3u) == 0))
125
0
        return;
126
0
127
0
      source_->clear();
128
0
      source_->seekg(initial_pos, std::istream::beg);
129
0
    }
130
131
    TOML_PURE_INLINE_GETTER
132
    bool error() const noexcept
133
0
    {
134
0
      return !!(source_->rdstate() & std::istream::badbit);
135
0
    }
136
137
    TOML_PURE_INLINE_GETTER
138
    bool eof() const noexcept
139
0
    {
140
0
      return !!(source_->rdstate() & std::istream::eofbit);
141
0
    }
142
143
    TOML_PURE_INLINE_GETTER
144
    explicit operator bool() const noexcept
145
0
    {
146
0
      return !(source_->rdstate() & (std::istream::badbit | std::istream::eofbit));
147
0
    }
148
149
    TOML_NODISCARD
150
    bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS)
151
0
    {
152
0
      return eof() || source_->peek() == std::istream::traits_type::eof();
153
0
    }
154
155
    TOML_NODISCARD
156
    TOML_ATTR(nonnull)
157
    size_t operator()(void* dest, size_t num) noexcept(!TOML_COMPILER_HAS_EXCEPTIONS)
158
0
    {
159
0
      TOML_ASSERT(*this);
160
0
161
0
      source_->read(static_cast<char*>(dest), static_cast<std::streamsize>(num));
162
0
      return static_cast<size_t>(source_->gcount());
163
0
    }
164
  };
165
166
  struct utf8_codepoint
167
  {
168
    char32_t value;
169
    char bytes[4];
170
    size_t count;
171
    source_position position;
172
173
    TOML_PURE_INLINE_GETTER
174
    constexpr operator const char32_t&() const noexcept
175
181M
    {
176
181M
      return value;
177
181M
    }
178
179
    TOML_PURE_INLINE_GETTER
180
    constexpr const char32_t& operator*() const noexcept
181
1.12M
    {
182
1.12M
      return value;
183
1.12M
    }
184
  };
185
  static_assert(std::is_trivially_default_constructible_v<utf8_codepoint> && std::is_trivially_copyable_v<utf8_codepoint>);
186
  static_assert(std::is_standard_layout_v<utf8_codepoint>);
187
188
  struct TOML_ABSTRACT_INTERFACE utf8_reader_interface
189
  {
190
    TOML_NODISCARD
191
    virtual const source_path_ptr& source_path() const noexcept = 0;
192
193
    TOML_NODISCARD
194
    virtual const utf8_codepoint* read_next() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) = 0;
195
196
    TOML_NODISCARD
197
    virtual bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) = 0;
198
199
#if !TOML_EXCEPTIONS
200
201
    TOML_NODISCARD
202
    virtual optional<parse_error>&& error() noexcept = 0;
203
204
#endif
205
206
7.88k
    virtual ~utf8_reader_interface() noexcept = default;
207
  };
208
209
#if TOML_EXCEPTIONS
210
126
#define utf8_reader_error(...)        throw parse_error(__VA_ARGS__)
211
0
#define utf8_reader_return_after_error(...) static_assert(true)
212
53.6M
#define utf8_reader_error_check(...)    static_assert(true)
213
#else
214
#define utf8_reader_error(...)        err_.emplace(__VA_ARGS__)
215
#define utf8_reader_return_after_error(...) return __VA_ARGS__
216
#define utf8_reader_error_check(...)                                                                                   \
217
  do                                                                                                                 \
218
  {                                                                                                                  \
219
    if TOML_UNLIKELY(err_)                                                                                         \
220
      return __VA_ARGS__;                                                                                        \
221
  }                                                                                                                  \
222
  while (false)
223
224
#endif
225
226
#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__)
227
#define TOML_OVERALIGNED
228
#else
229
1.68M
#define TOML_OVERALIGNED alignas(32)
230
#endif
231
232
  template <typename T>
233
  class TOML_EMPTY_BASES utf8_reader final : public utf8_reader_interface
234
  {
235
    private:
236
    static constexpr size_t block_capacity = 32;
237
    utf8_byte_stream<T> stream_;
238
    source_position next_pos_ = { 1, 1 };
239
240
    impl::utf8_decoder decoder_;
241
    struct currently_decoding_t
242
    {
243
      char bytes[4];
244
      size_t count;
245
    } currently_decoding_;
246
247
    struct codepoints_t
248
    {
249
      TOML_OVERALIGNED utf8_codepoint buffer[block_capacity];
250
      size_t current;
251
      size_t count;
252
    } codepoints_;
253
254
    source_path_ptr source_path_;
255
256
#if !TOML_EXCEPTIONS
257
    optional<parse_error> err_;
258
#endif
259
260
    bool read_next_block() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS)
261
1.68M
    {
262
1.68M
      TOML_ASSERT(stream_);
263
264
1.68M
      TOML_OVERALIGNED char raw_bytes[block_capacity];
265
1.68M
      size_t raw_bytes_read;
266
267
      // read the next raw (encoded) block in from the stream
268
      if constexpr (noexcept(stream_(raw_bytes, block_capacity)) || !TOML_EXCEPTIONS)
269
1.68M
      {
270
1.68M
        raw_bytes_read = stream_(raw_bytes, block_capacity);
271
      }
272
#if TOML_EXCEPTIONS
273
      else
274
      {
275
        try
276
        {
277
          raw_bytes_read = stream_(raw_bytes, block_capacity);
278
        }
279
        catch (const std::exception& exc)
280
        {
281
          throw parse_error{ exc.what(), next_pos_, source_path_ };
282
        }
283
        catch (...)
284
        {
285
          throw parse_error{ "An unspecified error occurred", next_pos_, source_path_ };
286
        }
287
      }
288
1.68M
#endif // TOML_EXCEPTIONS
289
290
      // handle a zero-byte read
291
1.68M
      if TOML_UNLIKELY(!raw_bytes_read)
292
0
      {
293
0
        if (stream_.eof())
294
0
        {
295
          // EOF only sets the error state if the decoder wants more input, otherwise
296
          // a zero-byte read might have just caused the underlying stream to realize it's exhaused and set
297
          // the EOF flag, and that's totally fine
298
0
          if (decoder_.needs_more_input())
299
0
            utf8_reader_error("Encountered EOF during incomplete utf-8 code point sequence",
300
0
                      next_pos_,
301
0
                      source_path_);
302
0
        }
303
0
        else
304
0
        {
305
0
          utf8_reader_error("Reading from the underlying stream failed - zero bytes read",
306
0
                    next_pos_,
307
0
                    source_path_);
308
0
        }
309
0
        return false;
310
0
      }
311
312
1.68M
      TOML_ASSERT_ASSUME(raw_bytes_read);
313
1.68M
      std::memset(&codepoints_, 0, sizeof(codepoints_));
314
315
      // helper for calculating decoded codepoint line+cols
316
1.68M
      const auto calc_positions = [&]() noexcept
317
1.68M
      {
318
55.2M
        for (size_t i = 0; i < codepoints_.count; i++)
319
53.6M
        {
320
53.6M
          auto& cp  = codepoints_.buffer[i];
321
53.6M
          cp.position = next_pos_;
322
323
53.6M
          if (cp == U'\n')
324
1.64M
          {
325
1.64M
            next_pos_.line++;
326
1.64M
            next_pos_.column = source_index{ 1 };
327
1.64M
          }
328
51.9M
          else
329
51.9M
            next_pos_.column++;
330
53.6M
        }
331
1.68M
      };
332
333
      // decide whether we need to use the UTF-8 decoder or if we can treat this block as plain ASCII
334
1.68M
      const auto ascii_fast_path = !decoder_.needs_more_input() && impl::is_ascii(raw_bytes, raw_bytes_read);
335
336
      // ASCII fast-path
337
1.68M
      if (ascii_fast_path)
338
1.66M
      {
339
1.66M
        decoder_.reset();
340
1.66M
        currently_decoding_.count = {};
341
342
1.66M
        codepoints_.count = raw_bytes_read;
343
54.9M
        for (size_t i = 0; i < codepoints_.count; i++)
344
53.2M
        {
345
53.2M
          auto& cp  = codepoints_.buffer[i];
346
53.2M
          cp.value  = static_cast<char32_t>(raw_bytes[i]);
347
53.2M
          cp.bytes[0] = raw_bytes[i];
348
53.2M
          cp.count  = 1u;
349
53.2M
        }
350
1.66M
      }
351
352
      // UTF-8 slow-path
353
13.2k
      else
354
13.2k
      {
355
        // helper for getting precise error location
356
13.2k
        const auto error_pos = [&]() noexcept -> const source_position&
357
13.2k
        { //
358
126
          return codepoints_.count ? codepoints_.buffer[codepoints_.count - 1u].position : next_pos_;
359
126
        };
360
361
395k
        for (size_t i = 0; i < raw_bytes_read; i++)
362
382k
        {
363
382k
          decoder_(static_cast<uint8_t>(raw_bytes[i]));
364
382k
          if TOML_UNLIKELY(decoder_.error())
365
90
          {
366
90
            calc_positions();
367
90
            utf8_reader_error("Encountered invalid utf-8 sequence", error_pos(), source_path_);
368
0
            utf8_reader_return_after_error(false);
369
0
          }
370
371
382k
          currently_decoding_.bytes[currently_decoding_.count++] = raw_bytes[i];
372
373
382k
          if (decoder_.has_code_point())
374
339k
          {
375
339k
            auto& cp = codepoints_.buffer[codepoints_.count++];
376
377
339k
            cp.value = decoder_.codepoint;
378
339k
            cp.count = currently_decoding_.count;
379
339k
            std::memcpy(cp.bytes, currently_decoding_.bytes, currently_decoding_.count);
380
339k
            currently_decoding_.count = {};
381
339k
          }
382
42.6k
          else if TOML_UNLIKELY(currently_decoding_.count == 4u)
383
0
          {
384
0
            calc_positions();
385
0
            utf8_reader_error("Encountered overlong utf-8 sequence", error_pos(), source_path_);
386
0
            utf8_reader_return_after_error(false);
387
0
          }
388
382k
        }
389
13.2k
        if TOML_UNLIKELY(decoder_.needs_more_input() && stream_.eof())
390
36
        {
391
36
          calc_positions();
392
36
          utf8_reader_error("Encountered EOF during incomplete utf-8 code point sequence",
393
36
                    error_pos(),
394
36
                    source_path_);
395
0
          utf8_reader_return_after_error(false);
396
0
        }
397
13.2k
      }
398
399
1.68M
      TOML_ASSERT_ASSUME(codepoints_.count);
400
1.68M
      calc_positions();
401
402
      // handle general I/O errors
403
      // (down here so the next_pos_ benefits from calc_positions())
404
1.68M
      if TOML_UNLIKELY(stream_.error())
405
0
      {
406
0
        utf8_reader_error("An I/O error occurred while reading from the underlying stream",
407
0
                  next_pos_,
408
0
                  source_path_);
409
0
        utf8_reader_return_after_error(false);
410
0
      }
411
412
1.68M
      return true;
413
1.68M
    }
toml::v3::impl::utf8_reader<std::__1::basic_string_view<char, std::__1::char_traits<char> > >::read_next_block()
Line
Count
Source
261
1.68M
    {
262
1.68M
      TOML_ASSERT(stream_);
263
264
1.68M
      TOML_OVERALIGNED char raw_bytes[block_capacity];
265
1.68M
      size_t raw_bytes_read;
266
267
      // read the next raw (encoded) block in from the stream
268
      if constexpr (noexcept(stream_(raw_bytes, block_capacity)) || !TOML_EXCEPTIONS)
269
1.68M
      {
270
1.68M
        raw_bytes_read = stream_(raw_bytes, block_capacity);
271
      }
272
#if TOML_EXCEPTIONS
273
      else
274
      {
275
        try
276
        {
277
          raw_bytes_read = stream_(raw_bytes, block_capacity);
278
        }
279
        catch (const std::exception& exc)
280
        {
281
          throw parse_error{ exc.what(), next_pos_, source_path_ };
282
        }
283
        catch (...)
284
        {
285
          throw parse_error{ "An unspecified error occurred", next_pos_, source_path_ };
286
        }
287
      }
288
1.68M
#endif // TOML_EXCEPTIONS
289
290
      // handle a zero-byte read
291
1.68M
      if TOML_UNLIKELY(!raw_bytes_read)
292
0
      {
293
0
        if (stream_.eof())
294
0
        {
295
          // EOF only sets the error state if the decoder wants more input, otherwise
296
          // a zero-byte read might have just caused the underlying stream to realize it's exhaused and set
297
          // the EOF flag, and that's totally fine
298
0
          if (decoder_.needs_more_input())
299
0
            utf8_reader_error("Encountered EOF during incomplete utf-8 code point sequence",
300
0
                      next_pos_,
301
0
                      source_path_);
302
0
        }
303
0
        else
304
0
        {
305
0
          utf8_reader_error("Reading from the underlying stream failed - zero bytes read",
306
0
                    next_pos_,
307
0
                    source_path_);
308
0
        }
309
0
        return false;
310
0
      }
311
312
1.68M
      TOML_ASSERT_ASSUME(raw_bytes_read);
313
1.68M
      std::memset(&codepoints_, 0, sizeof(codepoints_));
314
315
      // helper for calculating decoded codepoint line+cols
316
1.68M
      const auto calc_positions = [&]() noexcept
317
1.68M
      {
318
1.68M
        for (size_t i = 0; i < codepoints_.count; i++)
319
1.68M
        {
320
1.68M
          auto& cp  = codepoints_.buffer[i];
321
1.68M
          cp.position = next_pos_;
322
323
1.68M
          if (cp == U'\n')
324
1.68M
          {
325
1.68M
            next_pos_.line++;
326
1.68M
            next_pos_.column = source_index{ 1 };
327
1.68M
          }
328
1.68M
          else
329
1.68M
            next_pos_.column++;
330
1.68M
        }
331
1.68M
      };
332
333
      // decide whether we need to use the UTF-8 decoder or if we can treat this block as plain ASCII
334
1.68M
      const auto ascii_fast_path = !decoder_.needs_more_input() && impl::is_ascii(raw_bytes, raw_bytes_read);
335
336
      // ASCII fast-path
337
1.68M
      if (ascii_fast_path)
338
1.66M
      {
339
1.66M
        decoder_.reset();
340
1.66M
        currently_decoding_.count = {};
341
342
1.66M
        codepoints_.count = raw_bytes_read;
343
54.9M
        for (size_t i = 0; i < codepoints_.count; i++)
344
53.2M
        {
345
53.2M
          auto& cp  = codepoints_.buffer[i];
346
53.2M
          cp.value  = static_cast<char32_t>(raw_bytes[i]);
347
53.2M
          cp.bytes[0] = raw_bytes[i];
348
53.2M
          cp.count  = 1u;
349
53.2M
        }
350
1.66M
      }
351
352
      // UTF-8 slow-path
353
13.2k
      else
354
13.2k
      {
355
        // helper for getting precise error location
356
13.2k
        const auto error_pos = [&]() noexcept -> const source_position&
357
13.2k
        { //
358
13.2k
          return codepoints_.count ? codepoints_.buffer[codepoints_.count - 1u].position : next_pos_;
359
13.2k
        };
360
361
395k
        for (size_t i = 0; i < raw_bytes_read; i++)
362
382k
        {
363
382k
          decoder_(static_cast<uint8_t>(raw_bytes[i]));
364
382k
          if TOML_UNLIKELY(decoder_.error())
365
90
          {
366
90
            calc_positions();
367
90
            utf8_reader_error("Encountered invalid utf-8 sequence", error_pos(), source_path_);
368
0
            utf8_reader_return_after_error(false);
369
0
          }
370
371
382k
          currently_decoding_.bytes[currently_decoding_.count++] = raw_bytes[i];
372
373
382k
          if (decoder_.has_code_point())
374
339k
          {
375
339k
            auto& cp = codepoints_.buffer[codepoints_.count++];
376
377
339k
            cp.value = decoder_.codepoint;
378
339k
            cp.count = currently_decoding_.count;
379
339k
            std::memcpy(cp.bytes, currently_decoding_.bytes, currently_decoding_.count);
380
339k
            currently_decoding_.count = {};
381
339k
          }
382
42.6k
          else if TOML_UNLIKELY(currently_decoding_.count == 4u)
383
0
          {
384
0
            calc_positions();
385
0
            utf8_reader_error("Encountered overlong utf-8 sequence", error_pos(), source_path_);
386
0
            utf8_reader_return_after_error(false);
387
0
          }
388
382k
        }
389
13.2k
        if TOML_UNLIKELY(decoder_.needs_more_input() && stream_.eof())
390
36
        {
391
36
          calc_positions();
392
36
          utf8_reader_error("Encountered EOF during incomplete utf-8 code point sequence",
393
36
                    error_pos(),
394
36
                    source_path_);
395
0
          utf8_reader_return_after_error(false);
396
0
        }
397
13.2k
      }
398
399
1.68M
      TOML_ASSERT_ASSUME(codepoints_.count);
400
1.68M
      calc_positions();
401
402
      // handle general I/O errors
403
      // (down here so the next_pos_ benefits from calc_positions())
404
1.68M
      if TOML_UNLIKELY(stream_.error())
405
0
      {
406
0
        utf8_reader_error("An I/O error occurred while reading from the underlying stream",
407
0
                  next_pos_,
408
0
                  source_path_);
409
0
        utf8_reader_return_after_error(false);
410
0
      }
411
412
1.68M
      return true;
413
1.68M
    }
Unexecuted instantiation: toml::v3::impl::utf8_reader<std::__1::basic_istream<char, std::__1::char_traits<char> > >::read_next_block()
414
415
    public:
416
    template <typename U, typename String = std::string_view>
417
    TOML_NODISCARD_CTOR
418
    explicit utf8_reader(U&& source, String&& source_path = {}) noexcept(
419
      std::is_nothrow_constructible_v<utf8_byte_stream<T>, U&&>)
420
7.88k
      : stream_{ static_cast<U&&>(source) }
421
7.88k
    {
422
7.88k
      currently_decoding_.count = {};
423
424
7.88k
      codepoints_.current = {};
425
7.88k
      codepoints_.count = {};
426
427
7.88k
      if (!source_path.empty())
428
0
        source_path_ = std::make_shared<const std::string>(static_cast<String&&>(source_path));
429
7.88k
    }
430
431
    TOML_PURE_INLINE_GETTER
432
    const source_path_ptr& source_path() const noexcept final
433
962k
    {
434
962k
      return source_path_;
435
962k
    }
toml::v3::impl::utf8_reader<std::__1::basic_string_view<char, std::__1::char_traits<char> > >::source_path() const
Line
Count
Source
433
962k
    {
434
962k
      return source_path_;
435
962k
    }
Unexecuted instantiation: toml::v3::impl::utf8_reader<std::__1::basic_istream<char, std::__1::char_traits<char> > >::source_path() const
436
437
    TOML_NODISCARD
438
    const utf8_codepoint* read_next() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) final
439
53.6M
    {
440
53.6M
      utf8_reader_error_check({});
441
442
53.6M
      if (codepoints_.current == codepoints_.count)
443
1.68M
      {
444
1.68M
        if TOML_UNLIKELY(!stream_ || !read_next_block())
445
6.09k
          return nullptr;
446
447
1.68M
        TOML_ASSERT_ASSUME(!codepoints_.current);
448
1.68M
      }
449
53.5M
      TOML_ASSERT_ASSUME(codepoints_.count);
450
53.5M
      TOML_ASSERT_ASSUME(codepoints_.count <= block_capacity);
451
53.5M
      TOML_ASSERT_ASSUME(codepoints_.current < codepoints_.count);
452
453
53.5M
      return &codepoints_.buffer[codepoints_.current++];
454
53.6M
    }
toml::v3::impl::utf8_reader<std::__1::basic_string_view<char, std::__1::char_traits<char> > >::read_next()
Line
Count
Source
439
53.6M
    {
440
53.6M
      utf8_reader_error_check({});
441
442
53.6M
      if (codepoints_.current == codepoints_.count)
443
1.68M
      {
444
1.68M
        if TOML_UNLIKELY(!stream_ || !read_next_block())
445
6.09k
          return nullptr;
446
447
1.68M
        TOML_ASSERT_ASSUME(!codepoints_.current);
448
1.68M
      }
449
53.5M
      TOML_ASSERT_ASSUME(codepoints_.count);
450
53.5M
      TOML_ASSERT_ASSUME(codepoints_.count <= block_capacity);
451
53.5M
      TOML_ASSERT_ASSUME(codepoints_.current < codepoints_.count);
452
453
53.5M
      return &codepoints_.buffer[codepoints_.current++];
454
53.6M
    }
Unexecuted instantiation: toml::v3::impl::utf8_reader<std::__1::basic_istream<char, std::__1::char_traits<char> > >::read_next()
455
456
    TOML_NODISCARD
457
    bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) final
458
7.88k
    {
459
7.88k
      return stream_.peek_eof();
460
7.88k
    }
toml::v3::impl::utf8_reader<std::__1::basic_string_view<char, std::__1::char_traits<char> > >::peek_eof() const
Line
Count
Source
458
7.88k
    {
459
7.88k
      return stream_.peek_eof();
460
7.88k
    }
Unexecuted instantiation: toml::v3::impl::utf8_reader<std::__1::basic_istream<char, std::__1::char_traits<char> > >::peek_eof() const
461
462
#if !TOML_EXCEPTIONS
463
464
    TOML_NODISCARD
465
    optional<parse_error>&& error() noexcept final
466
    {
467
      return std::move(err_);
468
    }
469
470
#endif
471
  };
472
473
  template <typename Char>
474
  utf8_reader(std::basic_string_view<Char>, std::string_view) -> utf8_reader<std::basic_string_view<Char>>;
475
  template <typename Char>
476
  utf8_reader(std::basic_string_view<Char>, std::string&&) -> utf8_reader<std::basic_string_view<Char>>;
477
  template <typename Char>
478
  utf8_reader(std::basic_istream<Char>&, std::string_view) -> utf8_reader<std::basic_istream<Char>>;
479
  template <typename Char>
480
  utf8_reader(std::basic_istream<Char>&, std::string&&) -> utf8_reader<std::basic_istream<Char>>;
481
482
#if TOML_EXCEPTIONS
483
55.3M
#define utf8_buffered_reader_error_check(...) static_assert(true)
484
#else
485
#define utf8_buffered_reader_error_check(...)                                                                          \
486
  do                                                                                                                 \
487
  {                                                                                                                  \
488
    if TOML_UNLIKELY(reader_.error())                                                                              \
489
      return __VA_ARGS__;                                                                                        \
490
  }                                                                                                                  \
491
  while (false)
492
493
#endif
494
495
  class TOML_EMPTY_BASES utf8_buffered_reader
496
  {
497
    public:
498
    static constexpr size_t max_history_length = 128;
499
500
    private:
501
    static constexpr size_t history_buffer_size = max_history_length - 1; //'head' is stored in the reader
502
    utf8_reader_interface& reader_;
503
    struct
504
    {
505
      utf8_codepoint buffer[history_buffer_size];
506
      size_t count, first;
507
    } history_          = {};
508
    const utf8_codepoint* head_ = {};
509
    size_t negative_offset_   = {};
510
511
    public:
512
    TOML_NODISCARD_CTOR
513
    explicit utf8_buffered_reader(utf8_reader_interface& reader) noexcept //
514
7.88k
      : reader_{ reader }
515
7.88k
    {}
516
517
    TOML_PURE_INLINE_GETTER
518
    const source_path_ptr& source_path() const noexcept
519
962k
    {
520
962k
      return reader_.source_path();
521
962k
    }
522
523
    TOML_NODISCARD
524
    const utf8_codepoint* read_next() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS)
525
54.7M
    {
526
54.7M
      utf8_buffered_reader_error_check({});
527
528
54.7M
      if (negative_offset_)
529
1.13M
      {
530
1.13M
        negative_offset_--;
531
532
        // an entry negative offset of 1 just means "replay the current head"
533
1.13M
        if (!negative_offset_)
534
602k
          return head_;
535
536
        // otherwise step back into the history buffer
537
536k
        else
538
536k
          return history_.buffer
539
536k
             + ((history_.first + history_.count - negative_offset_) % history_buffer_size);
540
1.13M
      }
541
53.6M
      else
542
53.6M
      {
543
        // first character read from stream
544
53.6M
        if TOML_UNLIKELY(!history_.count && !head_)
545
7.84k
          head_ = reader_.read_next();
546
547
        // subsequent characters and not eof
548
53.5M
        else if (head_)
549
53.5M
        {
550
53.5M
          if TOML_UNLIKELY(history_.count < history_buffer_size)
551
295k
            history_.buffer[history_.count++] = *head_;
552
53.3M
          else
553
53.3M
            history_.buffer[(history_.first++ + history_buffer_size) % history_buffer_size] = *head_;
554
555
53.5M
          head_ = reader_.read_next();
556
53.5M
        }
557
558
53.6M
        return head_;
559
53.6M
      }
560
54.7M
    }
561
562
    TOML_NODISCARD
563
    const utf8_codepoint* step_back(size_t count) noexcept
564
604k
    {
565
604k
      utf8_buffered_reader_error_check({});
566
567
604k
      TOML_ASSERT_ASSUME(history_.count);
568
604k
      TOML_ASSERT_ASSUME(negative_offset_ + count <= history_.count);
569
570
604k
      negative_offset_ += count;
571
572
604k
      return negative_offset_
573
604k
           ? history_.buffer + ((history_.first + history_.count - negative_offset_) % history_buffer_size)
574
604k
           : head_;
575
604k
    }
576
577
    TOML_NODISCARD
578
    bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS)
579
7.88k
    {
580
7.88k
      return reader_.peek_eof();
581
7.88k
    }
582
583
#if !TOML_EXCEPTIONS
584
585
    TOML_NODISCARD
586
    optional<parse_error>&& error() noexcept
587
    {
588
      return reader_.error();
589
    }
590
591
#endif
592
  };
593
}
594
TOML_ANON_NAMESPACE_END;
595
596
//#---------------------------------------------------------------------------------------------------------------------
597
//# PARSER INTERNAL IMPLEMENTATION
598
//#---------------------------------------------------------------------------------------------------------------------
599
600
#if TOML_EXCEPTIONS
601
#define TOML_RETURNS_BY_THROWING [[noreturn]]
602
#else
603
#define TOML_RETURNS_BY_THROWING
604
#endif
605
606
TOML_ANON_NAMESPACE_START
607
{
608
  template <typename... T>
609
  TOML_CONST_GETTER
610
  TOML_INTERNAL_LINKAGE
611
  constexpr bool is_match(char32_t codepoint, T... vals) noexcept
612
3.35M
  {
613
3.35M
    static_assert((std::is_same_v<char32_t, T> && ...));
614
6.78M
    return ((codepoint == vals) || ...);
615
3.35M
  }
bool toml::v3::impl::is_match<char32_t, char32_t>(char32_t, char32_t, char32_t)
Line
Count
Source
612
3.31M
  {
613
3.31M
    static_assert((std::is_same_v<char32_t, T> && ...));
614
6.63M
    return ((codepoint == vals) || ...);
615
3.31M
  }
bool toml::v3::impl::is_match<char32_t, char32_t, char32_t>(char32_t, char32_t, char32_t, char32_t)
Line
Count
Source
612
9.95k
  {
613
9.95k
    static_assert((std::is_same_v<char32_t, T> && ...));
614
27.0k
    return ((codepoint == vals) || ...);
615
9.95k
  }
bool toml::v3::impl::is_match<char32_t, char32_t, char32_t, char32_t, char32_t>(char32_t, char32_t, char32_t, char32_t, char32_t, char32_t)
Line
Count
Source
612
5.00k
  {
613
5.00k
    static_assert((std::is_same_v<char32_t, T> && ...));
614
39.9k
    return ((codepoint == vals) || ...);
615
5.00k
  }
bool toml::v3::impl::is_match<char32_t, char32_t, char32_t, char32_t>(char32_t, char32_t, char32_t, char32_t, char32_t)
Line
Count
Source
612
15.1k
  {
613
15.1k
    static_assert((std::is_same_v<char32_t, T> && ...));
614
80.0k
    return ((codepoint == vals) || ...);
615
15.1k
  }
bool toml::v3::impl::is_match<char32_t, char32_t, char32_t, char32_t, char32_t, char32_t>(char32_t, char32_t, char32_t, char32_t, char32_t, char32_t, char32_t)
Line
Count
Source
612
1.84k
  {
613
1.84k
    static_assert((std::is_same_v<char32_t, T> && ...));
614
7.36k
    return ((codepoint == vals) || ...);
615
1.84k
  }
616
617
  template <uint64_t>
618
  struct parse_integer_traits;
619
  template <>
620
  struct parse_integer_traits<2>
621
  {
622
    static constexpr auto scope_qualifier  = "binary integer"sv;
623
    static constexpr auto is_digit       = impl::is_binary_digit;
624
    static constexpr auto is_signed      = false;
625
    static constexpr auto max_digits     = 63;
626
    static constexpr auto prefix_codepoint = U'b';
627
    static constexpr auto prefix       = "b"sv;
628
    static constexpr auto full_prefix    = "0b"sv;
629
  };
630
  template <>
631
  struct parse_integer_traits<8>
632
  {
633
    static constexpr auto scope_qualifier  = "octal integer"sv;
634
    static constexpr auto is_digit       = impl::is_octal_digit;
635
    static constexpr auto is_signed      = false;
636
    static constexpr auto max_digits     = 21; // strlen("777777777777777777777")
637
    static constexpr auto prefix_codepoint = U'o';
638
    static constexpr auto prefix       = "o"sv;
639
    static constexpr auto full_prefix    = "0o"sv;
640
  };
641
  template <>
642
  struct parse_integer_traits<10>
643
  {
644
    static constexpr auto scope_qualifier = "decimal integer"sv;
645
    static constexpr auto is_digit      = impl::is_decimal_digit;
646
    static constexpr auto is_signed     = true;
647
    static constexpr auto max_digits    = 19; // strlen("9223372036854775807")
648
    static constexpr auto full_prefix   = ""sv;
649
  };
650
  template <>
651
  struct parse_integer_traits<16>
652
  {
653
    static constexpr auto scope_qualifier  = "hexadecimal integer"sv;
654
    static constexpr auto is_digit       = impl::is_hexadecimal_digit;
655
    static constexpr auto is_signed      = false;
656
    static constexpr auto max_digits     = 16; // strlen("7FFFFFFFFFFFFFFF")
657
    static constexpr auto prefix_codepoint = U'x';
658
    static constexpr auto prefix       = "x"sv;
659
    static constexpr auto full_prefix    = "0x"sv;
660
  };
661
662
  TOML_PURE_GETTER
663
  TOML_INTERNAL_LINKAGE
664
  std::string_view to_sv(node_type val) noexcept
665
45
  {
666
45
    return impl::node_type_friendly_names[impl::unwrap_enum(val)];
667
45
  }
668
669
  TOML_PURE_GETTER
670
  TOML_INTERNAL_LINKAGE
671
  std::string_view to_sv(const std::string& str) noexcept
672
131
  {
673
131
    return std::string_view{ str };
674
131
  }
675
676
  TOML_CONST_GETTER
677
  TOML_INTERNAL_LINKAGE
678
  std::string_view to_sv(bool val) noexcept
679
36
  {
680
36
    using namespace std::string_view_literals;
681
682
36
    return val ? "true"sv : "false"sv;
683
36
  }
684
685
  TOML_PURE_GETTER
686
  TOML_INTERNAL_LINKAGE
687
  std::string_view to_sv(const utf8_codepoint& cp) noexcept
688
1.94k
  {
689
1.94k
    if (cp.value <= U'\x1F')
690
231
      return impl::control_char_escapes[cp.value];
691
1.70k
    else if (cp.value == U'\x7F')
692
44
      return "\\u007F"sv;
693
1.66k
    else
694
1.66k
      return std::string_view{ cp.bytes, cp.count };
695
1.94k
  }
696
697
  TOML_PURE_GETTER
698
  TOML_INTERNAL_LINKAGE
699
  std::string_view to_sv(const utf8_codepoint* cp) noexcept
700
612
  {
701
612
    if (cp)
702
612
      return to_sv(*cp);
703
0
    return ""sv;
704
612
  }
705
706
  struct escaped_codepoint
707
  {
708
    const utf8_codepoint& cp;
709
  };
710
711
  template <typename T>
712
  TOML_ATTR(nonnull)
713
  TOML_INTERNAL_LINKAGE
714
  void concatenate(char*& write_pos, char* const buf_end, const T& arg) noexcept
715
21.3k
  {
716
21.3k
    if TOML_UNLIKELY(write_pos >= buf_end)
717
3
      return;
718
719
21.3k
    using arg_type = impl::remove_cvref<T>;
720
721
    // string views
722
    if constexpr (std::is_same_v<arg_type, std::string_view>)
723
21.1k
    {
724
21.1k
      const auto max_chars = static_cast<size_t>(buf_end - write_pos);
725
21.1k
      const auto len     = max_chars < arg.length() ? max_chars : arg.length();
726
21.1k
      std::memcpy(write_pos, arg.data(), len);
727
21.1k
      write_pos += len;
728
    }
729
730
    // doubles
731
    else if constexpr (std::is_same_v<arg_type, double>)
732
    {
733
#if TOML_FLOAT_CHARCONV
734
      const auto result = std::to_chars(write_pos, buf_end, arg);
735
      write_pos     = result.ptr;
736
#else
737
      std::ostringstream ss;
738
      ss.imbue(std::locale::classic());
739
      ss.precision(std::numeric_limits<arg_type>::max_digits10);
740
      ss << arg;
741
      concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
742
#endif
743
    }
744
745
    // 64-bit integers
746
    else if constexpr (impl::is_one_of<arg_type, int64_t, uint64_t>)
747
62
    {
748
62
#if TOML_INT_CHARCONV
749
62
      const auto result = std::to_chars(write_pos, buf_end, arg);
750
62
      write_pos     = result.ptr;
751
#else
752
      std::ostringstream ss;
753
      ss.imbue(std::locale::classic());
754
      using cast_type = std::conditional_t<std::is_signed_v<arg_type>, int64_t, uint64_t>;
755
      ss << static_cast<cast_type>(arg);
756
      concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
757
#endif
758
    }
759
760
    // escaped_codepoint
761
    else if constexpr (std::is_same_v<arg_type, escaped_codepoint>)
762
88
    {
763
88
      if (arg.cp.value <= U'\x7F')
764
20
        concatenate(write_pos, buf_end, to_sv(arg.cp));
765
68
      else
766
68
      {
767
68
        auto val      = static_cast<uint_least32_t>(arg.cp.value);
768
68
        const auto digits = val > 0xFFFFu ? 8u : 4u;
769
68
        constexpr auto mask = uint_least32_t{ 0xFu };
770
68
        char buf[10]    = { '\\', digits > 4 ? 'U' : 'u' };
771
376
        for (auto i = 2u + digits; i-- > 2u;)
772
308
        {
773
308
          const auto hexdig = val & mask;
774
308
          buf[i]        = static_cast<char>(hexdig >= 0xAu ? ('A' + (hexdig - 0xAu)) : ('0' + hexdig));
775
308
          val >>= 4;
776
308
        }
777
68
        concatenate(write_pos, buf_end, std::string_view{ buf, digits + 2u });
778
68
      }
779
    }
780
781
    // all other floats (fallback - coerce to double)
782
    else if constexpr (std::is_floating_point_v<arg_type>)
783
      concatenate(write_pos, buf_end, static_cast<double>(arg));
784
785
    // all other integers (fallback - coerce to (u)int64_t)
786
    else if constexpr (std::is_arithmetic_v<arg_type> && std::is_integral_v<arg_type>)
787
39
    {
788
39
      using cast_type = std::conditional_t<std::is_unsigned_v<arg_type>, uint64_t, int64_t>;
789
39
      concatenate(write_pos, buf_end, static_cast<cast_type>(arg));
790
    }
791
792
    else
793
    {
794
      static_assert(
795
        impl::always_false<T>,
796
        "concatenate() inputs are limited to std::string_views, integers, floats, and escaped_codepoint");
797
    }
798
21.3k
  }
void toml::v3::impl::concatenate<std::__1::basic_string_view<char, std::__1::char_traits<char> > >(char*&, char*, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&)
Line
Count
Source
715
21.1k
  {
716
21.1k
    if TOML_UNLIKELY(write_pos >= buf_end)
717
3
      return;
718
719
21.1k
    using arg_type = impl::remove_cvref<T>;
720
721
    // string views
722
    if constexpr (std::is_same_v<arg_type, std::string_view>)
723
21.1k
    {
724
21.1k
      const auto max_chars = static_cast<size_t>(buf_end - write_pos);
725
21.1k
      const auto len     = max_chars < arg.length() ? max_chars : arg.length();
726
21.1k
      std::memcpy(write_pos, arg.data(), len);
727
21.1k
      write_pos += len;
728
    }
729
730
    // doubles
731
    else if constexpr (std::is_same_v<arg_type, double>)
732
    {
733
#if TOML_FLOAT_CHARCONV
734
      const auto result = std::to_chars(write_pos, buf_end, arg);
735
      write_pos     = result.ptr;
736
#else
737
      std::ostringstream ss;
738
      ss.imbue(std::locale::classic());
739
      ss.precision(std::numeric_limits<arg_type>::max_digits10);
740
      ss << arg;
741
      concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
742
#endif
743
    }
744
745
    // 64-bit integers
746
    else if constexpr (impl::is_one_of<arg_type, int64_t, uint64_t>)
747
    {
748
#if TOML_INT_CHARCONV
749
      const auto result = std::to_chars(write_pos, buf_end, arg);
750
      write_pos     = result.ptr;
751
#else
752
      std::ostringstream ss;
753
      ss.imbue(std::locale::classic());
754
      using cast_type = std::conditional_t<std::is_signed_v<arg_type>, int64_t, uint64_t>;
755
      ss << static_cast<cast_type>(arg);
756
      concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
757
#endif
758
    }
759
760
    // escaped_codepoint
761
    else if constexpr (std::is_same_v<arg_type, escaped_codepoint>)
762
    {
763
      if (arg.cp.value <= U'\x7F')
764
        concatenate(write_pos, buf_end, to_sv(arg.cp));
765
      else
766
      {
767
        auto val      = static_cast<uint_least32_t>(arg.cp.value);
768
        const auto digits = val > 0xFFFFu ? 8u : 4u;
769
        constexpr auto mask = uint_least32_t{ 0xFu };
770
        char buf[10]    = { '\\', digits > 4 ? 'U' : 'u' };
771
        for (auto i = 2u + digits; i-- > 2u;)
772
        {
773
          const auto hexdig = val & mask;
774
          buf[i]        = static_cast<char>(hexdig >= 0xAu ? ('A' + (hexdig - 0xAu)) : ('0' + hexdig));
775
          val >>= 4;
776
        }
777
        concatenate(write_pos, buf_end, std::string_view{ buf, digits + 2u });
778
      }
779
    }
780
781
    // all other floats (fallback - coerce to double)
782
    else if constexpr (std::is_floating_point_v<arg_type>)
783
      concatenate(write_pos, buf_end, static_cast<double>(arg));
784
785
    // all other integers (fallback - coerce to (u)int64_t)
786
    else if constexpr (std::is_arithmetic_v<arg_type> && std::is_integral_v<arg_type>)
787
    {
788
      using cast_type = std::conditional_t<std::is_unsigned_v<arg_type>, uint64_t, int64_t>;
789
      concatenate(write_pos, buf_end, static_cast<cast_type>(arg));
790
    }
791
792
    else
793
    {
794
      static_assert(
795
        impl::always_false<T>,
796
        "concatenate() inputs are limited to std::string_views, integers, floats, and escaped_codepoint");
797
    }
798
21.1k
  }
void toml::v3::impl::concatenate<toml::v3::impl::escaped_codepoint>(char*&, char*, toml::v3::impl::escaped_codepoint const&)
Line
Count
Source
715
88
  {
716
88
    if TOML_UNLIKELY(write_pos >= buf_end)
717
0
      return;
718
719
88
    using arg_type = impl::remove_cvref<T>;
720
721
    // string views
722
    if constexpr (std::is_same_v<arg_type, std::string_view>)
723
    {
724
      const auto max_chars = static_cast<size_t>(buf_end - write_pos);
725
      const auto len     = max_chars < arg.length() ? max_chars : arg.length();
726
      std::memcpy(write_pos, arg.data(), len);
727
      write_pos += len;
728
    }
729
730
    // doubles
731
    else if constexpr (std::is_same_v<arg_type, double>)
732
    {
733
#if TOML_FLOAT_CHARCONV
734
      const auto result = std::to_chars(write_pos, buf_end, arg);
735
      write_pos     = result.ptr;
736
#else
737
      std::ostringstream ss;
738
      ss.imbue(std::locale::classic());
739
      ss.precision(std::numeric_limits<arg_type>::max_digits10);
740
      ss << arg;
741
      concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
742
#endif
743
    }
744
745
    // 64-bit integers
746
    else if constexpr (impl::is_one_of<arg_type, int64_t, uint64_t>)
747
    {
748
#if TOML_INT_CHARCONV
749
      const auto result = std::to_chars(write_pos, buf_end, arg);
750
      write_pos     = result.ptr;
751
#else
752
      std::ostringstream ss;
753
      ss.imbue(std::locale::classic());
754
      using cast_type = std::conditional_t<std::is_signed_v<arg_type>, int64_t, uint64_t>;
755
      ss << static_cast<cast_type>(arg);
756
      concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
757
#endif
758
    }
759
760
    // escaped_codepoint
761
    else if constexpr (std::is_same_v<arg_type, escaped_codepoint>)
762
88
    {
763
88
      if (arg.cp.value <= U'\x7F')
764
20
        concatenate(write_pos, buf_end, to_sv(arg.cp));
765
68
      else
766
68
      {
767
68
        auto val      = static_cast<uint_least32_t>(arg.cp.value);
768
68
        const auto digits = val > 0xFFFFu ? 8u : 4u;
769
68
        constexpr auto mask = uint_least32_t{ 0xFu };
770
68
        char buf[10]    = { '\\', digits > 4 ? 'U' : 'u' };
771
376
        for (auto i = 2u + digits; i-- > 2u;)
772
308
        {
773
308
          const auto hexdig = val & mask;
774
308
          buf[i]        = static_cast<char>(hexdig >= 0xAu ? ('A' + (hexdig - 0xAu)) : ('0' + hexdig));
775
308
          val >>= 4;
776
308
        }
777
68
        concatenate(write_pos, buf_end, std::string_view{ buf, digits + 2u });
778
68
      }
779
    }
780
781
    // all other floats (fallback - coerce to double)
782
    else if constexpr (std::is_floating_point_v<arg_type>)
783
      concatenate(write_pos, buf_end, static_cast<double>(arg));
784
785
    // all other integers (fallback - coerce to (u)int64_t)
786
    else if constexpr (std::is_arithmetic_v<arg_type> && std::is_integral_v<arg_type>)
787
    {
788
      using cast_type = std::conditional_t<std::is_unsigned_v<arg_type>, uint64_t, int64_t>;
789
      concatenate(write_pos, buf_end, static_cast<cast_type>(arg));
790
    }
791
792
    else
793
    {
794
      static_assert(
795
        impl::always_false<T>,
796
        "concatenate() inputs are limited to std::string_views, integers, floats, and escaped_codepoint");
797
    }
798
88
  }
void toml::v3::impl::concatenate<unsigned long>(char*&, char*, unsigned long const&)
Line
Count
Source
715
55
  {
716
55
    if TOML_UNLIKELY(write_pos >= buf_end)
717
0
      return;
718
719
55
    using arg_type = impl::remove_cvref<T>;
720
721
    // string views
722
    if constexpr (std::is_same_v<arg_type, std::string_view>)
723
    {
724
      const auto max_chars = static_cast<size_t>(buf_end - write_pos);
725
      const auto len     = max_chars < arg.length() ? max_chars : arg.length();
726
      std::memcpy(write_pos, arg.data(), len);
727
      write_pos += len;
728
    }
729
730
    // doubles
731
    else if constexpr (std::is_same_v<arg_type, double>)
732
    {
733
#if TOML_FLOAT_CHARCONV
734
      const auto result = std::to_chars(write_pos, buf_end, arg);
735
      write_pos     = result.ptr;
736
#else
737
      std::ostringstream ss;
738
      ss.imbue(std::locale::classic());
739
      ss.precision(std::numeric_limits<arg_type>::max_digits10);
740
      ss << arg;
741
      concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
742
#endif
743
    }
744
745
    // 64-bit integers
746
    else if constexpr (impl::is_one_of<arg_type, int64_t, uint64_t>)
747
55
    {
748
55
#if TOML_INT_CHARCONV
749
55
      const auto result = std::to_chars(write_pos, buf_end, arg);
750
55
      write_pos     = result.ptr;
751
#else
752
      std::ostringstream ss;
753
      ss.imbue(std::locale::classic());
754
      using cast_type = std::conditional_t<std::is_signed_v<arg_type>, int64_t, uint64_t>;
755
      ss << static_cast<cast_type>(arg);
756
      concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
757
#endif
758
    }
759
760
    // escaped_codepoint
761
    else if constexpr (std::is_same_v<arg_type, escaped_codepoint>)
762
    {
763
      if (arg.cp.value <= U'\x7F')
764
        concatenate(write_pos, buf_end, to_sv(arg.cp));
765
      else
766
      {
767
        auto val      = static_cast<uint_least32_t>(arg.cp.value);
768
        const auto digits = val > 0xFFFFu ? 8u : 4u;
769
        constexpr auto mask = uint_least32_t{ 0xFu };
770
        char buf[10]    = { '\\', digits > 4 ? 'U' : 'u' };
771
        for (auto i = 2u + digits; i-- > 2u;)
772
        {
773
          const auto hexdig = val & mask;
774
          buf[i]        = static_cast<char>(hexdig >= 0xAu ? ('A' + (hexdig - 0xAu)) : ('0' + hexdig));
775
          val >>= 4;
776
        }
777
        concatenate(write_pos, buf_end, std::string_view{ buf, digits + 2u });
778
      }
779
    }
780
781
    // all other floats (fallback - coerce to double)
782
    else if constexpr (std::is_floating_point_v<arg_type>)
783
      concatenate(write_pos, buf_end, static_cast<double>(arg));
784
785
    // all other integers (fallback - coerce to (u)int64_t)
786
    else if constexpr (std::is_arithmetic_v<arg_type> && std::is_integral_v<arg_type>)
787
    {
788
      using cast_type = std::conditional_t<std::is_unsigned_v<arg_type>, uint64_t, int64_t>;
789
      concatenate(write_pos, buf_end, static_cast<cast_type>(arg));
790
    }
791
792
    else
793
    {
794
      static_assert(
795
        impl::always_false<T>,
796
        "concatenate() inputs are limited to std::string_views, integers, floats, and escaped_codepoint");
797
    }
798
55
  }
void toml::v3::impl::concatenate<unsigned int>(char*&, char*, unsigned int const&)
Line
Count
Source
715
32
  {
716
32
    if TOML_UNLIKELY(write_pos >= buf_end)
717
0
      return;
718
719
32
    using arg_type = impl::remove_cvref<T>;
720
721
    // string views
722
    if constexpr (std::is_same_v<arg_type, std::string_view>)
723
    {
724
      const auto max_chars = static_cast<size_t>(buf_end - write_pos);
725
      const auto len     = max_chars < arg.length() ? max_chars : arg.length();
726
      std::memcpy(write_pos, arg.data(), len);
727
      write_pos += len;
728
    }
729
730
    // doubles
731
    else if constexpr (std::is_same_v<arg_type, double>)
732
    {
733
#if TOML_FLOAT_CHARCONV
734
      const auto result = std::to_chars(write_pos, buf_end, arg);
735
      write_pos     = result.ptr;
736
#else
737
      std::ostringstream ss;
738
      ss.imbue(std::locale::classic());
739
      ss.precision(std::numeric_limits<arg_type>::max_digits10);
740
      ss << arg;
741
      concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
742
#endif
743
    }
744
745
    // 64-bit integers
746
    else if constexpr (impl::is_one_of<arg_type, int64_t, uint64_t>)
747
    {
748
#if TOML_INT_CHARCONV
749
      const auto result = std::to_chars(write_pos, buf_end, arg);
750
      write_pos     = result.ptr;
751
#else
752
      std::ostringstream ss;
753
      ss.imbue(std::locale::classic());
754
      using cast_type = std::conditional_t<std::is_signed_v<arg_type>, int64_t, uint64_t>;
755
      ss << static_cast<cast_type>(arg);
756
      concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
757
#endif
758
    }
759
760
    // escaped_codepoint
761
    else if constexpr (std::is_same_v<arg_type, escaped_codepoint>)
762
    {
763
      if (arg.cp.value <= U'\x7F')
764
        concatenate(write_pos, buf_end, to_sv(arg.cp));
765
      else
766
      {
767
        auto val      = static_cast<uint_least32_t>(arg.cp.value);
768
        const auto digits = val > 0xFFFFu ? 8u : 4u;
769
        constexpr auto mask = uint_least32_t{ 0xFu };
770
        char buf[10]    = { '\\', digits > 4 ? 'U' : 'u' };
771
        for (auto i = 2u + digits; i-- > 2u;)
772
        {
773
          const auto hexdig = val & mask;
774
          buf[i]        = static_cast<char>(hexdig >= 0xAu ? ('A' + (hexdig - 0xAu)) : ('0' + hexdig));
775
          val >>= 4;
776
        }
777
        concatenate(write_pos, buf_end, std::string_view{ buf, digits + 2u });
778
      }
779
    }
780
781
    // all other floats (fallback - coerce to double)
782
    else if constexpr (std::is_floating_point_v<arg_type>)
783
      concatenate(write_pos, buf_end, static_cast<double>(arg));
784
785
    // all other integers (fallback - coerce to (u)int64_t)
786
    else if constexpr (std::is_arithmetic_v<arg_type> && std::is_integral_v<arg_type>)
787
32
    {
788
32
      using cast_type = std::conditional_t<std::is_unsigned_v<arg_type>, uint64_t, int64_t>;
789
32
      concatenate(write_pos, buf_end, static_cast<cast_type>(arg));
790
    }
791
792
    else
793
    {
794
      static_assert(
795
        impl::always_false<T>,
796
        "concatenate() inputs are limited to std::string_views, integers, floats, and escaped_codepoint");
797
    }
798
32
  }
void toml::v3::impl::concatenate<int>(char*&, char*, int const&)
Line
Count
Source
715
7
  {
716
7
    if TOML_UNLIKELY(write_pos >= buf_end)
717
0
      return;
718
719
7
    using arg_type = impl::remove_cvref<T>;
720
721
    // string views
722
    if constexpr (std::is_same_v<arg_type, std::string_view>)
723
    {
724
      const auto max_chars = static_cast<size_t>(buf_end - write_pos);
725
      const auto len     = max_chars < arg.length() ? max_chars : arg.length();
726
      std::memcpy(write_pos, arg.data(), len);
727
      write_pos += len;
728
    }
729
730
    // doubles
731
    else if constexpr (std::is_same_v<arg_type, double>)
732
    {
733
#if TOML_FLOAT_CHARCONV
734
      const auto result = std::to_chars(write_pos, buf_end, arg);
735
      write_pos     = result.ptr;
736
#else
737
      std::ostringstream ss;
738
      ss.imbue(std::locale::classic());
739
      ss.precision(std::numeric_limits<arg_type>::max_digits10);
740
      ss << arg;
741
      concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
742
#endif
743
    }
744
745
    // 64-bit integers
746
    else if constexpr (impl::is_one_of<arg_type, int64_t, uint64_t>)
747
    {
748
#if TOML_INT_CHARCONV
749
      const auto result = std::to_chars(write_pos, buf_end, arg);
750
      write_pos     = result.ptr;
751
#else
752
      std::ostringstream ss;
753
      ss.imbue(std::locale::classic());
754
      using cast_type = std::conditional_t<std::is_signed_v<arg_type>, int64_t, uint64_t>;
755
      ss << static_cast<cast_type>(arg);
756
      concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
757
#endif
758
    }
759
760
    // escaped_codepoint
761
    else if constexpr (std::is_same_v<arg_type, escaped_codepoint>)
762
    {
763
      if (arg.cp.value <= U'\x7F')
764
        concatenate(write_pos, buf_end, to_sv(arg.cp));
765
      else
766
      {
767
        auto val      = static_cast<uint_least32_t>(arg.cp.value);
768
        const auto digits = val > 0xFFFFu ? 8u : 4u;
769
        constexpr auto mask = uint_least32_t{ 0xFu };
770
        char buf[10]    = { '\\', digits > 4 ? 'U' : 'u' };
771
        for (auto i = 2u + digits; i-- > 2u;)
772
        {
773
          const auto hexdig = val & mask;
774
          buf[i]        = static_cast<char>(hexdig >= 0xAu ? ('A' + (hexdig - 0xAu)) : ('0' + hexdig));
775
          val >>= 4;
776
        }
777
        concatenate(write_pos, buf_end, std::string_view{ buf, digits + 2u });
778
      }
779
    }
780
781
    // all other floats (fallback - coerce to double)
782
    else if constexpr (std::is_floating_point_v<arg_type>)
783
      concatenate(write_pos, buf_end, static_cast<double>(arg));
784
785
    // all other integers (fallback - coerce to (u)int64_t)
786
    else if constexpr (std::is_arithmetic_v<arg_type> && std::is_integral_v<arg_type>)
787
7
    {
788
7
      using cast_type = std::conditional_t<std::is_unsigned_v<arg_type>, uint64_t, int64_t>;
789
7
      concatenate(write_pos, buf_end, static_cast<cast_type>(arg));
790
    }
791
792
    else
793
    {
794
      static_assert(
795
        impl::always_false<T>,
796
        "concatenate() inputs are limited to std::string_views, integers, floats, and escaped_codepoint");
797
    }
798
7
  }
void toml::v3::impl::concatenate<long>(char*&, char*, long const&)
Line
Count
Source
715
7
  {
716
7
    if TOML_UNLIKELY(write_pos >= buf_end)
717
0
      return;
718
719
7
    using arg_type = impl::remove_cvref<T>;
720
721
    // string views
722
    if constexpr (std::is_same_v<arg_type, std::string_view>)
723
    {
724
      const auto max_chars = static_cast<size_t>(buf_end - write_pos);
725
      const auto len     = max_chars < arg.length() ? max_chars : arg.length();
726
      std::memcpy(write_pos, arg.data(), len);
727
      write_pos += len;
728
    }
729
730
    // doubles
731
    else if constexpr (std::is_same_v<arg_type, double>)
732
    {
733
#if TOML_FLOAT_CHARCONV
734
      const auto result = std::to_chars(write_pos, buf_end, arg);
735
      write_pos     = result.ptr;
736
#else
737
      std::ostringstream ss;
738
      ss.imbue(std::locale::classic());
739
      ss.precision(std::numeric_limits<arg_type>::max_digits10);
740
      ss << arg;
741
      concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
742
#endif
743
    }
744
745
    // 64-bit integers
746
    else if constexpr (impl::is_one_of<arg_type, int64_t, uint64_t>)
747
7
    {
748
7
#if TOML_INT_CHARCONV
749
7
      const auto result = std::to_chars(write_pos, buf_end, arg);
750
7
      write_pos     = result.ptr;
751
#else
752
      std::ostringstream ss;
753
      ss.imbue(std::locale::classic());
754
      using cast_type = std::conditional_t<std::is_signed_v<arg_type>, int64_t, uint64_t>;
755
      ss << static_cast<cast_type>(arg);
756
      concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
757
#endif
758
    }
759
760
    // escaped_codepoint
761
    else if constexpr (std::is_same_v<arg_type, escaped_codepoint>)
762
    {
763
      if (arg.cp.value <= U'\x7F')
764
        concatenate(write_pos, buf_end, to_sv(arg.cp));
765
      else
766
      {
767
        auto val      = static_cast<uint_least32_t>(arg.cp.value);
768
        const auto digits = val > 0xFFFFu ? 8u : 4u;
769
        constexpr auto mask = uint_least32_t{ 0xFu };
770
        char buf[10]    = { '\\', digits > 4 ? 'U' : 'u' };
771
        for (auto i = 2u + digits; i-- > 2u;)
772
        {
773
          const auto hexdig = val & mask;
774
          buf[i]        = static_cast<char>(hexdig >= 0xAu ? ('A' + (hexdig - 0xAu)) : ('0' + hexdig));
775
          val >>= 4;
776
        }
777
        concatenate(write_pos, buf_end, std::string_view{ buf, digits + 2u });
778
      }
779
    }
780
781
    // all other floats (fallback - coerce to double)
782
    else if constexpr (std::is_floating_point_v<arg_type>)
783
      concatenate(write_pos, buf_end, static_cast<double>(arg));
784
785
    // all other integers (fallback - coerce to (u)int64_t)
786
    else if constexpr (std::is_arithmetic_v<arg_type> && std::is_integral_v<arg_type>)
787
    {
788
      using cast_type = std::conditional_t<std::is_unsigned_v<arg_type>, uint64_t, int64_t>;
789
      concatenate(write_pos, buf_end, static_cast<cast_type>(arg));
790
    }
791
792
    else
793
    {
794
      static_assert(
795
        impl::always_false<T>,
796
        "concatenate() inputs are limited to std::string_views, integers, floats, and escaped_codepoint");
797
    }
798
7
  }
799
800
  struct error_builder
801
  {
802
    static constexpr std::size_t buf_size = 512;
803
    char buf[buf_size];
804
    char* write_pos       = buf;
805
    char* const max_write_pos = buf + (buf_size - std::size_t{ 1 }); // allow for null terminator
806
807
    TOML_NODISCARD_CTOR
808
    error_builder(std::string_view scope) noexcept
809
4.06k
    {
810
4.06k
      concatenate(write_pos, max_write_pos, "Error while parsing "sv);
811
4.06k
      concatenate(write_pos, max_write_pos, scope);
812
4.06k
      concatenate(write_pos, max_write_pos, ": "sv);
813
4.06k
    }
814
815
    template <typename T>
816
    void append(const T& arg) noexcept
817
9.04k
    {
818
9.04k
      concatenate(write_pos, max_write_pos, arg);
819
9.04k
    }
void toml::v3::impl::error_builder::append<std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&)
Line
Count
Source
817
8.89k
    {
818
8.89k
      concatenate(write_pos, max_write_pos, arg);
819
8.89k
    }
void toml::v3::impl::error_builder::append<toml::v3::impl::escaped_codepoint>(toml::v3::impl::escaped_codepoint const&)
Line
Count
Source
817
88
    {
818
88
      concatenate(write_pos, max_write_pos, arg);
819
88
    }
void toml::v3::impl::error_builder::append<unsigned long>(unsigned long const&)
Line
Count
Source
817
23
    {
818
23
      concatenate(write_pos, max_write_pos, arg);
819
23
    }
void toml::v3::impl::error_builder::append<unsigned int>(unsigned int const&)
Line
Count
Source
817
32
    {
818
32
      concatenate(write_pos, max_write_pos, arg);
819
32
    }
void toml::v3::impl::error_builder::append<int>(int const&)
Line
Count
Source
817
7
    {
818
7
      concatenate(write_pos, max_write_pos, arg);
819
7
    }
820
821
    TOML_RETURNS_BY_THROWING
822
    auto finish(const source_position& pos, const source_path_ptr& source_path) const
823
4.06k
    {
824
4.06k
      *write_pos = '\0';
825
826
4.06k
#if TOML_EXCEPTIONS
827
4.06k
      throw parse_error{ buf, pos, source_path };
828
#else
829
      return parse_error{ std::string(buf, static_cast<size_t>(write_pos - buf)), pos, source_path };
830
#endif
831
4.06k
    }
832
833
    TOML_DELETE_DEFAULTS(error_builder);
834
  };
835
836
  struct parse_scope
837
  {
838
    std::string_view& storage_;
839
    std::string_view parent_;
840
841
    TOML_NODISCARD_CTOR
842
    explicit parse_scope(std::string_view& current_scope, std::string_view new_scope) noexcept
843
1.16M
      : storage_{ current_scope },
844
1.16M
        parent_{ current_scope }
845
1.16M
    {
846
1.16M
      storage_ = new_scope;
847
1.16M
    }
848
849
    ~parse_scope() noexcept
850
1.16M
    {
851
1.16M
      storage_ = parent_;
852
1.16M
    }
853
854
    TOML_DELETE_DEFAULTS(parse_scope);
855
  };
856
1.16M
#define push_parse_scope_2(scope, line) parse_scope ps_##line(current_scope, scope)
857
1.16M
#define push_parse_scope_1(scope, line) push_parse_scope_2(scope, line)
858
1.16M
#define push_parse_scope(scope)     push_parse_scope_1(scope, __LINE__)
859
860
  struct parse_key_buffer
861
  {
862
    std::string buffer;
863
    std::vector<std::pair<size_t, size_t>> segments;
864
    std::vector<source_position> starts;
865
    std::vector<source_position> ends;
866
867
    void clear() noexcept
868
205k
    {
869
205k
      buffer.clear();
870
205k
      segments.clear();
871
205k
      starts.clear();
872
205k
      ends.clear();
873
205k
    }
874
875
    void push_back(std::string_view segment, source_position b, source_position e)
876
1.43M
    {
877
1.43M
      segments.push_back({ buffer.length(), segment.length() });
878
1.43M
      buffer.append(segment);
879
1.43M
      starts.push_back(b);
880
1.43M
      ends.push_back(e);
881
1.43M
    }
882
883
    TOML_PURE_INLINE_GETTER
884
    std::string_view operator[](size_t i) const noexcept
885
2.56M
    {
886
2.56M
      return std::string_view{ buffer.c_str() + segments[i].first, segments[i].second };
887
2.56M
    }
888
889
    TOML_PURE_INLINE_GETTER
890
    std::string_view back() const noexcept
891
204k
    {
892
204k
      return (*this)[segments.size() - 1u];
893
204k
    }
894
895
    TOML_PURE_INLINE_GETTER
896
    bool empty() const noexcept
897
18
    {
898
18
      return segments.empty();
899
18
    }
900
901
    TOML_PURE_INLINE_GETTER
902
    size_t size() const noexcept
903
2.73M
    {
904
2.73M
      return segments.size();
905
2.73M
    }
906
  };
907
908
  struct depth_counter_scope
909
  {
910
    size_t& depth_;
911
912
    TOML_NODISCARD_CTOR
913
    explicit depth_counter_scope(size_t& depth) noexcept //
914
621k
      : depth_{ depth }
915
621k
    {
916
621k
      depth_++;
917
621k
    }
918
919
    ~depth_counter_scope() noexcept
920
621k
    {
921
621k
      depth_--;
922
621k
    }
923
924
    TOML_DELETE_DEFAULTS(depth_counter_scope);
925
  };
926
927
  struct parsed_string
928
  {
929
    std::string_view value;
930
    bool was_multi_line;
931
  };
932
933
  struct table_vector_scope
934
  {
935
    std::vector<table*>& tables;
936
937
    TOML_NODISCARD_CTOR
938
    explicit table_vector_scope(std::vector<table*>& tables_, table& tbl) //
939
8.47k
      : tables{ tables_ }
940
8.47k
    {
941
8.47k
      tables.push_back(&tbl);
942
8.47k
    }
943
944
    ~table_vector_scope() noexcept
945
8.47k
    {
946
8.47k
      tables.pop_back();
947
8.47k
    }
948
949
    TOML_DELETE_DEFAULTS(table_vector_scope);
950
  };
951
}
952
TOML_ANON_NAMESPACE_END;
953
954
#if 1 // parser helper macros
955
956
// Q: "what the fuck is this? MACROS????"
957
// A: The parser needs to work in exceptionless mode (returning error objects directly)
958
//    and exception mode (reporting parse failures by throwing). Two totally different control flows.
959
//    These macros encapsulate the differences between the two modes so I can write code code
960
//    as though I was only targeting one mode and not want yeet myself into the sun.
961
//    They're all #undef'd at the bottom of the parser's implementation so they should be harmless outside
962
//    of toml++.
963
964
113M
#define is_eof()     !cp
965
112M
#define assert_not_eof() TOML_ASSERT_ASSUME(cp != nullptr)
966
#define return_if_eof(...)                                                                                             \
967
9.69M
  do                                                                                                                 \
968
9.69M
  {                                                                                                                  \
969
9.69M
    if TOML_UNLIKELY(is_eof())                                                                                     \
970
9.69M
      return __VA_ARGS__;                                                                                        \
971
9.69M
  }                                                                                                                  \
972
9.69M
  while (false)
973
974
#if TOML_EXCEPTIONS
975
2.63M
#define is_error()          false
976
3.85k
#define return_after_error(...)   TOML_UNREACHABLE
977
7.75k
#define assert_not_error()      static_assert(true)
978
145M
#define return_if_error(...)    static_assert(true)
979
9.69M
#define return_if_error_or_eof(...) return_if_eof(__VA_ARGS__)
980
#else
981
#define is_error()        !!err
982
#define return_after_error(...) return __VA_ARGS__
983
#define assert_not_error()    TOML_ASSERT(!is_error())
984
#define return_if_error(...)                                                                                           \
985
  do                                                                                                                 \
986
  {                                                                                                                  \
987
    if TOML_UNLIKELY(is_error())                                                                                   \
988
      return __VA_ARGS__;                                                                                        \
989
  }                                                                                                                  \
990
  while (false)
991
#define return_if_error_or_eof(...)                                                                                    \
992
  do                                                                                                                 \
993
  {                                                                                                                  \
994
    if TOML_UNLIKELY(is_eof() || is_error())                                                                       \
995
      return __VA_ARGS__;                                                                                        \
996
  }                                                                                                                  \
997
  while (false)
998
#endif
999
1000
#if defined(TOML_BREAK_AT_PARSE_ERRORS) && TOML_BREAK_AT_PARSE_ERRORS
1001
#if defined(__has_builtin)
1002
#if __has_builtin(__builtin_debugtrap)
1003
#define parse_error_break() __builtin_debugtrap()
1004
#elif __has_builtin(__debugbreak)
1005
#define parse_error_break() __debugbreak()
1006
#endif
1007
#endif
1008
#ifndef parse_error_break
1009
#if TOML_MSVC || TOML_ICC
1010
#define parse_error_break() __debugbreak()
1011
#else
1012
#define parse_error_break() TOML_ASSERT(false)
1013
#endif
1014
#endif
1015
#else
1016
4.06k
#define parse_error_break() static_assert(true)
1017
#endif
1018
1019
#define set_error_and_return(ret, ...)                                                                                 \
1020
3.47k
  do                                                                                                                 \
1021
3.47k
  {                                                                                                                  \
1022
3.47k
    if (!is_error())                                                                                               \
1023
3.47k
      set_error(__VA_ARGS__);                                                                                    \
1024
3.47k
    return_after_error(ret);                                                                                       \
1025
3.47k
  }                                                                                                                  \
1026
3.47k
  while (false)
1027
1028
2.78k
#define set_error_and_return_default(...) set_error_and_return({}, __VA_ARGS__)
1029
1030
#define set_error_and_return_if_eof(...)                                                                               \
1031
5.61M
  do                                                                                                                 \
1032
5.61M
  {                                                                                                                  \
1033
5.61M
    if TOML_UNLIKELY(is_eof())                                                                                     \
1034
5.61M
      set_error_and_return(__VA_ARGS__, "encountered end-of-file"sv);                                            \
1035
5.61M
  }                                                                                                                  \
1036
5.61M
  while (false)
1037
1038
#define advance_and_return_if_error(...)                                                                               \
1039
51.7M
  do                                                                                                                 \
1040
51.7M
  {                                                                                                                  \
1041
51.7M
    assert_not_eof();                                                                                              \
1042
51.7M
    advance();                                                                                                     \
1043
51.7M
    return_if_error(__VA_ARGS__);                                                                                  \
1044
51.7M
  }                                                                                                                  \
1045
51.7M
  while (false)
1046
1047
#define advance_and_return_if_error_or_eof(...)                                                                        \
1048
2.46M
  do                                                                                                                 \
1049
2.46M
  {                                                                                                                  \
1050
2.46M
    assert_not_eof();                                                                                              \
1051
2.46M
    advance();                                                                                                     \
1052
2.46M
    return_if_error(__VA_ARGS__);                                                                                  \
1053
2.46M
    set_error_and_return_if_eof(__VA_ARGS__);                                                                      \
1054
2.46M
  }                                                                                                                  \
1055
2.46M
  while (false)
1056
1057
#endif // parser helper macros
1058
1059
TOML_IMPL_NAMESPACE_START
1060
{
1061
  TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, impl_ex, impl_noex);
1062
1063
  class parser
1064
  {
1065
    private:
1066
    static constexpr size_t max_nested_values   = TOML_MAX_NESTED_VALUES;
1067
    static constexpr size_t max_dotted_keys_depth = TOML_MAX_DOTTED_KEYS_DEPTH;
1068
1069
    utf8_buffered_reader reader;
1070
    table root;
1071
    source_position prev_pos = { 1, 1 };
1072
    const utf8_codepoint* cp = {};
1073
    std::vector<table*> implicit_tables;
1074
    std::vector<table*> dotted_key_tables;
1075
    std::vector<table*> open_inline_tables;
1076
    std::vector<array*> table_arrays;
1077
    parse_key_buffer key_buffer;
1078
    std::string string_buffer;
1079
    std::string recording_buffer; // for diagnostics
1080
    bool recording = false, recording_whitespace = true;
1081
    std::string_view current_scope;
1082
    size_t nested_values = {};
1083
#if !TOML_EXCEPTIONS
1084
    mutable optional<parse_error> err;
1085
#endif
1086
1087
    TOML_NODISCARD
1088
    source_position current_position(source_index fallback_offset = 0) const noexcept
1089
3.66M
    {
1090
3.66M
      if (!is_eof())
1091
3.65M
        return cp->position;
1092
8.53k
      return { prev_pos.line, static_cast<source_index>(prev_pos.column + fallback_offset) };
1093
3.66M
    }
1094
1095
    template <typename... T>
1096
    TOML_RETURNS_BY_THROWING
1097
    TOML_NEVER_INLINE
1098
    void set_error_at(source_position pos, const T&... reason) const
1099
4.06k
    {
1100
4.06k
      static_assert(sizeof...(T) > 0);
1101
4.06k
      return_if_error();
1102
1103
4.06k
      error_builder builder{ current_scope };
1104
4.06k
      (builder.append(reason), ...);
1105
1106
4.06k
      parse_error_break();
1107
1108
4.06k
#if TOML_EXCEPTIONS
1109
4.06k
      builder.finish(pos, reader.source_path());
1110
#else
1111
      err.emplace(builder.finish(pos, reader.source_path()));
1112
#endif
1113
4.06k
    }
void toml::v3::impl::impl_ex::parser::set_error_at<std::__1::basic_string_view<char, std::__1::char_traits<char> >, toml::v3::impl::escaped_codepoint, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(toml::v3::source_position, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, toml::v3::impl::escaped_codepoint const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1099
88
    {
1100
88
      static_assert(sizeof...(T) > 0);
1101
88
      return_if_error();
1102
1103
88
      error_builder builder{ current_scope };
1104
88
      (builder.append(reason), ...);
1105
1106
88
      parse_error_break();
1107
1108
88
#if TOML_EXCEPTIONS
1109
88
      builder.finish(pos, reader.source_path());
1110
#else
1111
      err.emplace(builder.finish(pos, reader.source_path()));
1112
#endif
1113
88
    }
void toml::v3::impl::impl_ex::parser::set_error_at<std::__1::basic_string_view<char, std::__1::char_traits<char> > >(toml::v3::source_position, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1099
1.72k
    {
1100
1.72k
      static_assert(sizeof...(T) > 0);
1101
1.72k
      return_if_error();
1102
1103
1.72k
      error_builder builder{ current_scope };
1104
1.72k
      (builder.append(reason), ...);
1105
1106
1.72k
      parse_error_break();
1107
1108
1.72k
#if TOML_EXCEPTIONS
1109
1.72k
      builder.finish(pos, reader.source_path());
1110
#else
1111
      err.emplace(builder.finish(pos, reader.source_path()));
1112
#endif
1113
1.72k
    }
void toml::v3::impl::impl_ex::parser::set_error_at<std::__1::basic_string_view<char, std::__1::char_traits<char> >, unsigned long, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(toml::v3::source_position, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, unsigned long const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1099
17
    {
1100
17
      static_assert(sizeof...(T) > 0);
1101
17
      return_if_error();
1102
1103
17
      error_builder builder{ current_scope };
1104
17
      (builder.append(reason), ...);
1105
1106
17
      parse_error_break();
1107
1108
17
#if TOML_EXCEPTIONS
1109
17
      builder.finish(pos, reader.source_path());
1110
#else
1111
      err.emplace(builder.finish(pos, reader.source_path()));
1112
#endif
1113
17
    }
void toml::v3::impl::impl_ex::parser::set_error_at<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(toml::v3::source_position, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1099
1.97k
    {
1100
1.97k
      static_assert(sizeof...(T) > 0);
1101
1.97k
      return_if_error();
1102
1103
1.97k
      error_builder builder{ current_scope };
1104
1.97k
      (builder.append(reason), ...);
1105
1106
1.97k
      parse_error_break();
1107
1108
1.97k
#if TOML_EXCEPTIONS
1109
1.97k
      builder.finish(pos, reader.source_path());
1110
#else
1111
      err.emplace(builder.finish(pos, reader.source_path()));
1112
#endif
1113
1.97k
    }
void toml::v3::impl::impl_ex::parser::set_error_at<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(toml::v3::source_position, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1099
23
    {
1100
23
      static_assert(sizeof...(T) > 0);
1101
23
      return_if_error();
1102
1103
23
      error_builder builder{ current_scope };
1104
23
      (builder.append(reason), ...);
1105
1106
23
      parse_error_break();
1107
1108
23
#if TOML_EXCEPTIONS
1109
23
      builder.finish(pos, reader.source_path());
1110
#else
1111
      err.emplace(builder.finish(pos, reader.source_path()));
1112
#endif
1113
23
    }
void toml::v3::impl::impl_ex::parser::set_error_at<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(toml::v3::source_position, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1099
88
    {
1100
88
      static_assert(sizeof...(T) > 0);
1101
88
      return_if_error();
1102
1103
88
      error_builder builder{ current_scope };
1104
88
      (builder.append(reason), ...);
1105
1106
88
      parse_error_break();
1107
1108
88
#if TOML_EXCEPTIONS
1109
88
      builder.finish(pos, reader.source_path());
1110
#else
1111
      err.emplace(builder.finish(pos, reader.source_path()));
1112
#endif
1113
88
    }
void toml::v3::impl::impl_ex::parser::set_error_at<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(toml::v3::source_position, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1099
101
    {
1100
101
      static_assert(sizeof...(T) > 0);
1101
101
      return_if_error();
1102
1103
101
      error_builder builder{ current_scope };
1104
101
      (builder.append(reason), ...);
1105
1106
101
      parse_error_break();
1107
1108
101
#if TOML_EXCEPTIONS
1109
101
      builder.finish(pos, reader.source_path());
1110
#else
1111
      err.emplace(builder.finish(pos, reader.source_path()));
1112
#endif
1113
101
    }
void toml::v3::impl::impl_ex::parser::set_error_at<std::__1::basic_string_view<char, std::__1::char_traits<char> >, unsigned long, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(toml::v3::source_position, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, unsigned long const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1099
2
    {
1100
2
      static_assert(sizeof...(T) > 0);
1101
2
      return_if_error();
1102
1103
2
      error_builder builder{ current_scope };
1104
2
      (builder.append(reason), ...);
1105
1106
2
      parse_error_break();
1107
1108
2
#if TOML_EXCEPTIONS
1109
2
      builder.finish(pos, reader.source_path());
1110
#else
1111
      err.emplace(builder.finish(pos, reader.source_path()));
1112
#endif
1113
2
    }
void toml::v3::impl::impl_ex::parser::set_error_at<std::__1::basic_string_view<char, std::__1::char_traits<char> >, unsigned int>(toml::v3::source_position, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, unsigned int const&) const
Line
Count
Source
1099
24
    {
1100
24
      static_assert(sizeof...(T) > 0);
1101
24
      return_if_error();
1102
1103
24
      error_builder builder{ current_scope };
1104
24
      (builder.append(reason), ...);
1105
1106
24
      parse_error_break();
1107
1108
24
#if TOML_EXCEPTIONS
1109
24
      builder.finish(pos, reader.source_path());
1110
#else
1111
      err.emplace(builder.finish(pos, reader.source_path()));
1112
#endif
1113
24
    }
void toml::v3::impl::impl_ex::parser::set_error_at<std::__1::basic_string_view<char, std::__1::char_traits<char> >, unsigned long>(toml::v3::source_position, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, unsigned long const&) const
Line
Count
Source
1099
4
    {
1100
4
      static_assert(sizeof...(T) > 0);
1101
4
      return_if_error();
1102
1103
4
      error_builder builder{ current_scope };
1104
4
      (builder.append(reason), ...);
1105
1106
4
      parse_error_break();
1107
1108
4
#if TOML_EXCEPTIONS
1109
4
      builder.finish(pos, reader.source_path());
1110
#else
1111
      err.emplace(builder.finish(pos, reader.source_path()));
1112
#endif
1113
4
    }
void toml::v3::impl::impl_ex::parser::set_error_at<std::__1::basic_string_view<char, std::__1::char_traits<char> >, unsigned int, std::__1::basic_string_view<char, std::__1::char_traits<char> >, unsigned int>(toml::v3::source_position, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, unsigned int const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, unsigned int const&) const
Line
Count
Source
1099
4
    {
1100
4
      static_assert(sizeof...(T) > 0);
1101
4
      return_if_error();
1102
1103
4
      error_builder builder{ current_scope };
1104
4
      (builder.append(reason), ...);
1105
1106
4
      parse_error_break();
1107
1108
4
#if TOML_EXCEPTIONS
1109
4
      builder.finish(pos, reader.source_path());
1110
#else
1111
      err.emplace(builder.finish(pos, reader.source_path()));
1112
#endif
1113
4
    }
void toml::v3::impl::impl_ex::parser::set_error_at<std::__1::basic_string_view<char, std::__1::char_traits<char> >, int>(toml::v3::source_position, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, int const&) const
Line
Count
Source
1099
7
    {
1100
7
      static_assert(sizeof...(T) > 0);
1101
7
      return_if_error();
1102
1103
7
      error_builder builder{ current_scope };
1104
7
      (builder.append(reason), ...);
1105
1106
7
      parse_error_break();
1107
1108
7
#if TOML_EXCEPTIONS
1109
7
      builder.finish(pos, reader.source_path());
1110
#else
1111
      err.emplace(builder.finish(pos, reader.source_path()));
1112
#endif
1113
7
    }
1114
1115
    template <typename... T>
1116
    TOML_RETURNS_BY_THROWING
1117
    void set_error(const T&... reason) const
1118
3.69k
    {
1119
3.69k
      set_error_at(current_position(1), reason...);
1120
3.69k
    }
void toml::v3::impl::impl_ex::parser::set_error<std::__1::basic_string_view<char, std::__1::char_traits<char> >, toml::v3::impl::escaped_codepoint, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, toml::v3::impl::escaped_codepoint const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1118
88
    {
1119
88
      set_error_at(current_position(1), reason...);
1120
88
    }
void toml::v3::impl::impl_ex::parser::set_error<std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1118
1.43k
    {
1119
1.43k
      set_error_at(current_position(1), reason...);
1120
1.43k
    }
void toml::v3::impl::impl_ex::parser::set_error<std::__1::basic_string_view<char, std::__1::char_traits<char> >, unsigned long, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, unsigned long const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1118
17
    {
1119
17
      set_error_at(current_position(1), reason...);
1120
17
    }
void toml::v3::impl::impl_ex::parser::set_error<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1118
10
    {
1119
10
      set_error_at(current_position(1), reason...);
1120
10
    }
void toml::v3::impl::impl_ex::parser::set_error<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1118
88
    {
1119
88
      set_error_at(current_position(1), reason...);
1120
88
    }
void toml::v3::impl::impl_ex::parser::set_error<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1118
101
    {
1119
101
      set_error_at(current_position(1), reason...);
1120
101
    }
void toml::v3::impl::impl_ex::parser::set_error<std::__1::basic_string_view<char, std::__1::char_traits<char> >, unsigned long, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, unsigned long const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1118
2
    {
1119
2
      set_error_at(current_position(1), reason...);
1120
2
    }
void toml::v3::impl::impl_ex::parser::set_error<std::__1::basic_string_view<char, std::__1::char_traits<char> >, unsigned int>(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, unsigned int const&) const
Line
Count
Source
1118
24
    {
1119
24
      set_error_at(current_position(1), reason...);
1120
24
    }
void toml::v3::impl::impl_ex::parser::set_error<std::__1::basic_string_view<char, std::__1::char_traits<char> >, unsigned long>(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, unsigned long const&) const
Line
Count
Source
1118
4
    {
1119
4
      set_error_at(current_position(1), reason...);
1120
4
    }
void toml::v3::impl::impl_ex::parser::set_error<std::__1::basic_string_view<char, std::__1::char_traits<char> >, unsigned int, std::__1::basic_string_view<char, std::__1::char_traits<char> >, unsigned int>(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, unsigned int const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, unsigned int const&) const
Line
Count
Source
1118
4
    {
1119
4
      set_error_at(current_position(1), reason...);
1120
4
    }
void toml::v3::impl::impl_ex::parser::set_error<std::__1::basic_string_view<char, std::__1::char_traits<char> >, int>(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, int const&) const
Line
Count
Source
1118
7
    {
1119
7
      set_error_at(current_position(1), reason...);
1120
7
    }
void toml::v3::impl::impl_ex::parser::set_error<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> > const&) const
Line
Count
Source
1118
1.92k
    {
1119
1.92k
      set_error_at(current_position(1), reason...);
1120
1.92k
    }
1121
1122
    void go_back(size_t count = 1) noexcept
1123
604k
    {
1124
604k
      return_if_error();
1125
604k
      TOML_ASSERT_ASSUME(count);
1126
1127
604k
      cp     = reader.step_back(count);
1128
604k
      prev_pos = cp->position;
1129
604k
    }
1130
1131
    void advance()
1132
54.7M
    {
1133
54.7M
      return_if_error();
1134
54.7M
      assert_not_eof();
1135
1136
54.7M
      prev_pos = cp->position;
1137
54.7M
      cp     = reader.read_next();
1138
1139
#if !TOML_EXCEPTIONS
1140
      if (reader.error())
1141
      {
1142
        err = std::move(reader.error());
1143
        return;
1144
      }
1145
#endif
1146
1147
54.7M
      if (recording && !is_eof())
1148
39.5M
      {
1149
39.5M
        if (recording_whitespace || !is_whitespace(*cp))
1150
39.5M
          recording_buffer.append(cp->bytes, cp->count);
1151
39.5M
      }
1152
54.7M
    }
1153
1154
    void start_recording(bool include_current = true) noexcept
1155
208k
    {
1156
208k
      return_if_error();
1157
1158
208k
      recording      = true;
1159
208k
      recording_whitespace = true;
1160
208k
      recording_buffer.clear();
1161
208k
      if (include_current && !is_eof())
1162
208k
        recording_buffer.append(cp->bytes, cp->count);
1163
208k
    }
1164
1165
    void stop_recording(size_t pop_bytes = 0) noexcept
1166
207k
    {
1167
207k
      return_if_error();
1168
1169
207k
      recording = false;
1170
207k
      if (pop_bytes)
1171
204k
      {
1172
204k
        if (pop_bytes >= recording_buffer.length())
1173
98
          recording_buffer.clear();
1174
204k
        else if (pop_bytes == 1u)
1175
204k
          recording_buffer.pop_back();
1176
0
        else
1177
0
          recording_buffer.erase(recording_buffer.begin()
1178
0
                         + static_cast<ptrdiff_t>(recording_buffer.length() - pop_bytes),
1179
0
                       recording_buffer.end());
1180
204k
      }
1181
207k
    }
1182
1183
    bool consume_leading_whitespace()
1184
4.94M
    {
1185
4.94M
      return_if_error_or_eof({});
1186
1187
4.94M
      bool consumed = false;
1188
4.94M
      while (!is_eof() && is_horizontal_whitespace(*cp))
1189
6.47k
      {
1190
6.47k
        if TOML_UNLIKELY(!is_ascii_horizontal_whitespace(*cp))
1191
6.47k
          set_error_and_return_default("expected space or tab, saw '"sv, escaped_codepoint{ *cp }, "'"sv);
1192
1193
6.43k
        consumed = true;
1194
6.43k
        advance_and_return_if_error({});
1195
6.43k
      }
1196
4.94M
      return consumed;
1197
4.94M
    }
1198
1199
    bool consume_line_break()
1200
3.18M
    {
1201
3.18M
      return_if_error_or_eof({});
1202
1203
3.18M
      if TOML_UNLIKELY(is_match(*cp, U'\v', U'\f'))
1204
3.18M
        set_error_and_return_default(
1205
3.18M
          R"(vertical tabs '\v' and form-feeds '\f' are not legal line breaks in TOML)"sv);
1206
1207
3.18M
      if (*cp == U'\r')
1208
277
      {
1209
277
        advance_and_return_if_error({}); // skip \r
1210
1211
277
        if TOML_UNLIKELY(is_eof())
1212
277
          set_error_and_return_default("expected '\\n' after '\\r', saw EOF"sv);
1213
1214
274
        if TOML_UNLIKELY(*cp != U'\n')
1215
274
          set_error_and_return_default("expected '\\n' after '\\r', saw '"sv,
1216
274
                         escaped_codepoint{ *cp },
1217
274
                         "'"sv);
1218
274
      }
1219
3.18M
      else if (*cp != U'\n')
1220
1.53M
        return false;
1221
1222
1.64M
      advance_and_return_if_error({}); // skip \n
1223
1.64M
      return true;
1224
3.18M
    }
1225
1226
    bool consume_rest_of_line()
1227
0
    {
1228
0
      return_if_error_or_eof({});
1229
0
1230
0
      do
1231
0
      {
1232
0
        if (is_ascii_vertical_whitespace(*cp))
1233
0
          return consume_line_break();
1234
0
        else
1235
0
          advance();
1236
0
        return_if_error({});
1237
0
      }
1238
0
      while (!is_eof());
1239
0
1240
0
      return true;
1241
0
    }
1242
1243
    bool consume_comment()
1244
1.56M
    {
1245
1.56M
      return_if_error_or_eof({});
1246
1247
1.56M
      if (*cp != U'#')
1248
1.55M
        return false;
1249
1250
2.35k
      push_parse_scope("comment"sv);
1251
1252
2.35k
      advance_and_return_if_error({}); // skip the '#'
1253
1254
161k
      while (!is_eof())
1255
161k
      {
1256
161k
        if (consume_line_break())
1257
2.07k
          return true;
1258
159k
        return_if_error({});
1259
1260
159k
#if TOML_LANG_AT_LEAST(1, 0, 0)
1261
1262
        // toml/issues/567 (disallow non-TAB control characters in comments)
1263
159k
        if TOML_UNLIKELY(is_nontab_control_character(*cp))
1264
159k
          set_error_and_return_default(
1265
159k
            "control characters other than TAB (U+0009) are explicitly prohibited in comments"sv);
1266
1267
        // toml/pull/720 (disallow surrogates in comments)
1268
159k
        else if TOML_UNLIKELY(is_unicode_surrogate(*cp))
1269
0
          set_error_and_return_default(
1270
159k
            "unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited in comments"sv);
1271
159k
#endif
1272
1273
159k
        advance_and_return_if_error({});
1274
159k
      }
1275
1276
277
      return true;
1277
2.35k
    }
1278
1279
    TOML_NODISCARD
1280
    bool consume_expected_sequence(std::u32string_view seq)
1281
2.59k
    {
1282
2.59k
      return_if_error({});
1283
2.59k
      TOML_ASSERT(!seq.empty());
1284
1285
2.59k
      for (auto c : seq)
1286
8.65k
      {
1287
8.65k
        set_error_and_return_if_eof({});
1288
8.61k
        if (*cp != c)
1289
70
          return false;
1290
8.54k
        advance_and_return_if_error({});
1291
8.54k
      }
1292
2.48k
      return true;
1293
2.59k
    }
1294
1295
    template <typename T>
1296
    TOML_NODISCARD
1297
    bool consume_digit_sequence(T* digits, size_t len)
1298
45.7k
    {
1299
45.7k
      return_if_error({});
1300
45.7k
      TOML_ASSERT_ASSUME(digits);
1301
45.7k
      TOML_ASSERT_ASSUME(len);
1302
1303
151k
      for (size_t i = 0; i < len; i++)
1304
106k
      {
1305
106k
        set_error_and_return_if_eof({});
1306
106k
        if (!is_decimal_digit(*cp))
1307
360
          return false;
1308
1309
105k
        digits[i] = static_cast<T>(*cp - U'0');
1310
105k
        advance_and_return_if_error({});
1311
105k
      }
1312
45.3k
      return true;
1313
45.7k
    }
bool toml::v3::impl::impl_ex::parser::consume_digit_sequence<unsigned int>(unsigned int*, unsigned long)
Line
Count
Source
1298
42.0k
    {
1299
42.0k
      return_if_error({});
1300
42.0k
      TOML_ASSERT_ASSUME(digits);
1301
42.0k
      TOML_ASSERT_ASSUME(len);
1302
1303
140k
      for (size_t i = 0; i < len; i++)
1304
99.0k
      {
1305
99.0k
        set_error_and_return_if_eof({});
1306
98.9k
        if (!is_decimal_digit(*cp))
1307
298
          return false;
1308
1309
98.6k
        digits[i] = static_cast<T>(*cp - U'0');
1310
98.6k
        advance_and_return_if_error({});
1311
98.6k
      }
1312
41.7k
      return true;
1313
42.0k
    }
bool toml::v3::impl::impl_ex::parser::consume_digit_sequence<int>(int*, unsigned long)
Line
Count
Source
1298
3.70k
    {
1299
3.70k
      return_if_error({});
1300
3.70k
      TOML_ASSERT_ASSUME(digits);
1301
3.70k
      TOML_ASSERT_ASSUME(len);
1302
1303
11.0k
      for (size_t i = 0; i < len; i++)
1304
7.36k
      {
1305
7.36k
        set_error_and_return_if_eof({});
1306
7.36k
        if (!is_decimal_digit(*cp))
1307
62
          return false;
1308
1309
7.30k
        digits[i] = static_cast<T>(*cp - U'0');
1310
7.30k
        advance_and_return_if_error({});
1311
7.30k
      }
1312
3.63k
      return true;
1313
3.70k
    }
1314
1315
    template <typename T>
1316
    TOML_NODISCARD
1317
    size_t consume_variable_length_digit_sequence(T* buffer, size_t max_len)
1318
3.04k
    {
1319
3.04k
      return_if_error({});
1320
3.04k
      TOML_ASSERT_ASSUME(buffer);
1321
3.04k
      TOML_ASSERT_ASSUME(max_len);
1322
1323
3.04k
      size_t i = {};
1324
50.9k
      for (; i < max_len; i++)
1325
50.4k
      {
1326
50.4k
        if (is_eof() || !is_decimal_digit(*cp))
1327
2.50k
          break;
1328
1329
47.9k
        buffer[i] = static_cast<T>(*cp - U'0');
1330
47.9k
        advance_and_return_if_error({});
1331
47.9k
      }
1332
3.04k
      return i;
1333
3.04k
    }
1334
1335
    TOML_NODISCARD
1336
    TOML_NEVER_INLINE
1337
    std::string_view parse_basic_string(bool multi_line)
1338
4.88k
    {
1339
4.88k
      return_if_error({});
1340
4.88k
      assert_not_eof();
1341
4.88k
      TOML_ASSERT_ASSUME(*cp == U'"');
1342
4.88k
      push_parse_scope("string"sv);
1343
1344
      // skip the '"'
1345
4.88k
      advance_and_return_if_error_or_eof({});
1346
1347
      // multi-line strings ignore a single line ending right at the beginning
1348
4.88k
      if (multi_line)
1349
2.02k
      {
1350
2.02k
        consume_line_break();
1351
2.02k
        return_if_error({});
1352
2.02k
        set_error_and_return_if_eof({});
1353
2.02k
      }
1354
1355
4.88k
      auto& str = string_buffer;
1356
4.88k
      str.clear();
1357
4.88k
      bool escaped       = false;
1358
4.88k
      bool skipping_whitespace = false;
1359
4.88k
      do
1360
4.97M
      {
1361
4.97M
        if (escaped)
1362
4.90k
        {
1363
4.90k
          escaped = false;
1364
1365
          // handle 'line ending slashes' in multi-line mode
1366
4.90k
          if (multi_line && is_whitespace(*cp))
1367
1.86k
          {
1368
1.86k
            consume_leading_whitespace();
1369
1370
1.86k
            if TOML_UNLIKELY(!consume_line_break())
1371
1.86k
              set_error_and_return_default(
1372
1.85k
                "line-ending backslashes must be the last non-whitespace character on the line"sv);
1373
1374
1.85k
            skipping_whitespace = true;
1375
1.85k
            return_if_error({});
1376
1.85k
            continue;
1377
1.86k
          }
1378
1379
3.04k
          bool skip_escaped_codepoint = true;
1380
3.04k
          assert_not_eof();
1381
3.04k
          switch (const auto escaped_codepoint = *cp)
1382
3.04k
          {
1383
            // 'regular' escape codes
1384
255
            case U'b': str += '\b'; break;
1385
258
            case U'f': str += '\f'; break;
1386
326
            case U'n': str += '\n'; break;
1387
203
            case U'r': str += '\r'; break;
1388
220
            case U't': str += '\t'; break;
1389
219
            case U'"': str += '"'; break;
1390
215
            case U'\\': str += '\\'; break;
1391
1392
#if TOML_LANG_UNRELEASED // toml/pull/790 (\e shorthand for \x1B)
1393
            case U'e': str += '\x1B'; break;
1394
#else
1395
1
            case U'e':
1396
1
              set_error_and_return_default(
1397
0
                "escape sequence '\\e' is not supported in TOML 1.0.0 and earlier"sv);
1398
0
#endif
1399
1400
#if TOML_LANG_UNRELEASED // toml/pull/796 (\xHH unicode scalar sequences)
1401
            case U'x': [[fallthrough]];
1402
#else
1403
1
            case U'x':
1404
1
              set_error_and_return_default(
1405
0
                "escape sequence '\\x' is not supported in TOML 1.0.0 and earlier"sv);
1406
0
#endif
1407
1408
            // unicode scalar sequences
1409
951
            case U'u': [[fallthrough]];
1410
1.23k
            case U'U':
1411
1.23k
            {
1412
1.23k
              push_parse_scope("unicode scalar sequence"sv);
1413
1.23k
              advance_and_return_if_error_or_eof({});
1414
1.23k
              skip_escaped_codepoint = false;
1415
1416
1.23k
              uint32_t place_value =
1417
1.23k
                escaped_codepoint == U'U' ? 0x10000000u : (escaped_codepoint == U'u' ? 0x1000u : 0x10u);
1418
1.23k
              uint32_t sequence_value{};
1419
6.99k
              while (place_value)
1420
5.82k
              {
1421
5.82k
                set_error_and_return_if_eof({});
1422
1423
5.80k
                if TOML_UNLIKELY(!is_hexadecimal_digit(*cp))
1424
5.80k
                  set_error_and_return_default("expected hex digit, saw '"sv, to_sv(*cp), "'"sv);
1425
1426
5.76k
                sequence_value += place_value * hex_to_dec(*cp);
1427
5.76k
                place_value /= 16u;
1428
5.76k
                advance_and_return_if_error({});
1429
5.76k
              }
1430
1431
1.17k
              if TOML_UNLIKELY(is_unicode_surrogate(sequence_value))
1432
1.17k
                set_error_and_return_default(
1433
1.16k
                  "unicode surrogates (U+D800 - U+DFFF) are explicitly prohibited"sv);
1434
1.16k
              else if TOML_UNLIKELY(sequence_value > 0x10FFFFu)
1435
24
                set_error_and_return_default("values greater than U+10FFFF are invalid"sv);
1436
1437
1.14k
              if (sequence_value < 0x80)
1438
258
              {
1439
258
                str += static_cast<char>(sequence_value);
1440
258
              }
1441
886
              else if (sequence_value < 0x800u)
1442
238
              {
1443
238
                str += static_cast<char>((sequence_value >> 6) | 0xC0u);
1444
238
                str += static_cast<char>((sequence_value & 0x3Fu) | 0x80u);
1445
238
              }
1446
648
              else if (sequence_value < 0x10000u)
1447
420
              {
1448
420
                str += static_cast<char>((sequence_value >> 12) | 0xE0u);
1449
420
                str += static_cast<char>(((sequence_value >> 6) & 0x3Fu) | 0x80u);
1450
420
                str += static_cast<char>((sequence_value & 0x3Fu) | 0x80u);
1451
420
              }
1452
228
              else if (sequence_value < 0x110000u)
1453
226
              {
1454
226
                str += static_cast<char>((sequence_value >> 18) | 0xF0u);
1455
226
                str += static_cast<char>(((sequence_value >> 12) & 0x3Fu) | 0x80u);
1456
226
                str += static_cast<char>(((sequence_value >> 6) & 0x3Fu) | 0x80u);
1457
226
                str += static_cast<char>((sequence_value & 0x3Fu) | 0x80u);
1458
226
              }
1459
1.14k
              break;
1460
1.17k
            }
1461
1462
              // ???
1463
0
              TOML_UNLIKELY_CASE
1464
104
            default: set_error_and_return_default("unknown escape sequence '\\"sv, to_sv(*cp), "'"sv);
1465
3.04k
          }
1466
1467
2.83k
          if (skip_escaped_codepoint)
1468
1.69k
            advance_and_return_if_error_or_eof({});
1469
2.83k
        }
1470
4.97M
        else
1471
4.97M
        {
1472
          // handle closing delimiters
1473
4.97M
          if (*cp == U'"')
1474
5.17k
          {
1475
5.17k
            if (multi_line)
1476
2.58k
            {
1477
2.58k
              size_t lookaheads       = {};
1478
2.58k
              size_t consecutive_delimiters = 1;
1479
2.58k
              do
1480
7.25k
              {
1481
7.25k
                advance_and_return_if_error({});
1482
7.25k
                lookaheads++;
1483
7.25k
                if (!is_eof() && *cp == U'"')
1484
5.07k
                  consecutive_delimiters++;
1485
2.17k
                else
1486
2.17k
                  break;
1487
7.25k
              }
1488
5.07k
              while (lookaheads < 4u);
1489
1490
2.58k
              switch (consecutive_delimiters)
1491
2.58k
              {
1492
                // """ " (one quote somewhere in a ML string)
1493
533
                case 1:
1494
533
                  str += '"';
1495
533
                  skipping_whitespace = false;
1496
533
                  continue;
1497
1498
                // """ "" (two quotes somewhere in a ML string)
1499
363
                case 2:
1500
363
                  str.append("\"\""sv);
1501
363
                  skipping_whitespace = false;
1502
363
                  continue;
1503
1504
                // """ """ (the end of the string)
1505
754
                case 3: return str;
1506
1507
                // """ """" (one at the end of the string)
1508
523
                case 4: str += '"'; return str;
1509
1510
                // """ """"" (two quotes at the end of the string)
1511
409
                case 5:
1512
409
                  str.append("\"\""sv);
1513
409
                  advance_and_return_if_error({}); // skip the last '"'
1514
409
                  return str;
1515
1516
0
                default: TOML_UNREACHABLE;
1517
2.58k
              }
1518
2.58k
            }
1519
2.59k
            else
1520
2.59k
            {
1521
2.59k
              advance_and_return_if_error({}); // skip the closing delimiter
1522
2.59k
              return str;
1523
2.59k
            }
1524
5.17k
          }
1525
1526
          // handle escapes
1527
4.96M
          else if (*cp == U'\\')
1528
4.91k
          {
1529
4.91k
            advance_and_return_if_error_or_eof({}); // skip the '\'
1530
4.90k
            skipping_whitespace = false;
1531
4.90k
            escaped       = true;
1532
4.90k
            continue;
1533
4.91k
          }
1534
1535
          // handle line endings in multi-line mode
1536
4.96M
          if (multi_line && is_ascii_vertical_whitespace(*cp))
1537
1.05M
          {
1538
1.05M
            consume_line_break();
1539
1.05M
            return_if_error({});
1540
1.05M
            if (!skipping_whitespace)
1541
1.05M
              str += '\n';
1542
1.05M
            continue;
1543
1.05M
          }
1544
1545
          // handle control characters
1546
3.91M
          if TOML_UNLIKELY(is_nontab_control_character(*cp))
1547
3.91M
            set_error_and_return_default(
1548
3.91M
              "unescaped control characters other than TAB (U+0009) are explicitly prohibited"sv);
1549
1550
3.91M
#if TOML_LANG_AT_LEAST(1, 0, 0)
1551
1552
          // handle surrogates in strings
1553
3.91M
          if TOML_UNLIKELY(is_unicode_surrogate(*cp))
1554
3.91M
            set_error_and_return_default(
1555
3.91M
              "unescaped unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited"sv);
1556
3.91M
#endif
1557
1558
3.91M
          if (multi_line)
1559
190k
          {
1560
190k
            if (!skipping_whitespace || !is_horizontal_whitespace(*cp))
1561
187k
            {
1562
187k
              skipping_whitespace = false;
1563
187k
              str.append(cp->bytes, cp->count);
1564
187k
            }
1565
190k
          }
1566
3.72M
          else
1567
3.72M
            str.append(cp->bytes, cp->count);
1568
1569
3.91M
          advance_and_return_if_error({});
1570
3.91M
        }
1571
4.97M
      }
1572
4.97M
      while (!is_eof());
1573
1574
320
      set_error_and_return_default("encountered end-of-file"sv);
1575
320
    }
1576
1577
    TOML_NODISCARD
1578
    TOML_NEVER_INLINE
1579
    std::string_view parse_literal_string(bool multi_line)
1580
6.83k
    {
1581
6.83k
      return_if_error({});
1582
6.83k
      assert_not_eof();
1583
6.83k
      TOML_ASSERT_ASSUME(*cp == U'\'');
1584
6.83k
      push_parse_scope("literal string"sv);
1585
1586
      // skip the delimiter
1587
6.83k
      advance_and_return_if_error_or_eof({});
1588
1589
      // multi-line strings ignore a single line ending right at the beginning
1590
6.83k
      if (multi_line)
1591
4.19k
      {
1592
4.19k
        consume_line_break();
1593
4.19k
        return_if_error({});
1594
4.19k
        set_error_and_return_if_eof({});
1595
4.19k
      }
1596
1597
6.82k
      auto& str = string_buffer;
1598
6.82k
      str.clear();
1599
6.82k
      do
1600
27.4M
      {
1601
27.4M
        return_if_error({});
1602
1603
        // handle closing delimiters
1604
27.4M
        if (*cp == U'\'')
1605
9.37k
        {
1606
9.37k
          if (multi_line)
1607
6.79k
          {
1608
6.79k
            size_t lookaheads       = {};
1609
6.79k
            size_t consecutive_delimiters = 1;
1610
6.79k
            do
1611
19.0k
            {
1612
19.0k
              advance_and_return_if_error({});
1613
19.0k
              lookaheads++;
1614
19.0k
              if (!is_eof() && *cp == U'\'')
1615
13.2k
                consecutive_delimiters++;
1616
5.76k
              else
1617
5.76k
                break;
1618
19.0k
            }
1619
13.2k
            while (lookaheads < 4u);
1620
1621
6.79k
            switch (consecutive_delimiters)
1622
6.79k
            {
1623
              // ''' ' (one quote somewhere in a ML string)
1624
1.78k
              case 1: str += '\''; continue;
1625
1626
              // ''' '' (two quotes somewhere in a ML string)
1627
892
              case 2: str.append("''"sv); continue;
1628
1629
              // ''' ''' (the end of the string)
1630
973
              case 3: return str;
1631
1632
              // ''' '''' (one at the end of the string)
1633
2.11k
              case 4: str += '\''; return str;
1634
1635
              // ''' ''''' (two quotes at the end of the string)
1636
1.02k
              case 5:
1637
1.02k
                str.append("''"sv);
1638
1.02k
                advance_and_return_if_error({}); // skip the last '
1639
1.02k
                return str;
1640
1641
0
              default: TOML_UNREACHABLE;
1642
6.79k
            }
1643
6.79k
          }
1644
2.58k
          else
1645
2.58k
          {
1646
2.58k
            advance_and_return_if_error({}); // skip the closing delimiter
1647
2.58k
            return str;
1648
2.58k
          }
1649
9.37k
        }
1650
1651
        // handle line endings in multi-line mode
1652
27.4M
        if (multi_line && is_ascii_vertical_whitespace(*cp))
1653
267k
        {
1654
267k
          consume_line_break();
1655
267k
          return_if_error({});
1656
267k
          str += '\n';
1657
267k
          continue;
1658
267k
        }
1659
1660
        // handle control characters
1661
27.1M
        if TOML_UNLIKELY(is_nontab_control_character(*cp))
1662
27.1M
          set_error_and_return_default(
1663
27.1M
            "control characters other than TAB (U+0009) are explicitly prohibited"sv);
1664
1665
27.1M
#if TOML_LANG_AT_LEAST(1, 0, 0)
1666
1667
        // handle surrogates in strings
1668
27.1M
        if TOML_UNLIKELY(is_unicode_surrogate(*cp))
1669
27.1M
          set_error_and_return_default("unicode surrogates (U+D800 - U+DFFF) are explicitly prohibited"sv);
1670
27.1M
#endif
1671
1672
27.1M
        str.append(cp->bytes, cp->count);
1673
27.1M
        advance_and_return_if_error({});
1674
27.1M
      }
1675
27.4M
      while (!is_eof());
1676
1677
113
      set_error_and_return_default("encountered end-of-file"sv);
1678
113
    }
1679
1680
    TOML_NODISCARD
1681
    TOML_NEVER_INLINE
1682
    parsed_string parse_string()
1683
11.8k
    {
1684
11.8k
      return_if_error({});
1685
11.8k
      assert_not_eof();
1686
11.8k
      TOML_ASSERT_ASSUME(is_string_delimiter(*cp));
1687
11.8k
      push_parse_scope("string"sv);
1688
1689
      // snapshot length so the recording buffer can be rewound alongside go_back(2u) below
1690
11.8k
      const auto recording_buffer_rollback_size = recording_buffer.length();
1691
1692
      // get the first three characters to determine the string type
1693
11.8k
      const auto first = cp->value;
1694
11.8k
      advance_and_return_if_error_or_eof({});
1695
11.7k
      const auto second = cp->value;
1696
11.7k
      advance_and_return_if_error({});
1697
11.7k
      const auto third = cp ? cp->value : U'\0';
1698
1699
      // if we were eof at the third character then first and second need to be
1700
      // the same string character (otherwise it's an unterminated string)
1701
11.7k
      if (is_eof())
1702
59
      {
1703
59
        if (second == first)
1704
28
          return {};
1705
1706
31
        set_error_and_return_default("encountered end-of-file"sv);
1707
31
      }
1708
1709
      // if the first three characters are all the same string delimiter then
1710
      // it's a multi-line string.
1711
11.7k
      else if (first == second && first == third)
1712
6.22k
      {
1713
6.22k
        return { first == U'\'' ? parse_literal_string(true) : parse_basic_string(true), true };
1714
6.22k
      }
1715
1716
      // otherwise it's just a regular string.
1717
5.50k
      else
1718
5.50k
      {
1719
        // step back two characters so that the current
1720
        // character is the string delimiter
1721
5.50k
        go_back(2u);
1722
5.50k
        if (recording)
1723
4.06k
          recording_buffer.resize(recording_buffer_rollback_size);
1724
1725
5.50k
        return { first == U'\'' ? parse_literal_string(false) : parse_basic_string(false), false };
1726
5.50k
      }
1727
11.7k
    }
1728
1729
    TOML_NODISCARD
1730
    TOML_NEVER_INLINE
1731
    std::string_view parse_bare_key_segment()
1732
1.43M
    {
1733
1.43M
      return_if_error({});
1734
1.43M
      assert_not_eof();
1735
1.43M
      TOML_ASSERT_ASSUME(is_bare_key_character(*cp));
1736
1737
1.43M
      string_buffer.clear();
1738
1739
18.4M
      while (!is_eof())
1740
18.4M
      {
1741
18.4M
        if (!is_bare_key_character(*cp))
1742
1.43M
          break;
1743
1744
16.9M
        string_buffer.append(cp->bytes, cp->count);
1745
16.9M
        advance_and_return_if_error({});
1746
16.9M
      }
1747
1748
1.43M
      return string_buffer;
1749
1.43M
    }
1750
1751
    TOML_NODISCARD
1752
    TOML_NEVER_INLINE
1753
    bool parse_boolean()
1754
747
    {
1755
747
      return_if_error({});
1756
747
      assert_not_eof();
1757
747
      TOML_ASSERT_ASSUME(is_match(*cp, U't', U'f', U'T', U'F'));
1758
747
      push_parse_scope("boolean"sv);
1759
1760
747
      start_recording(true);
1761
747
      auto result = is_match(*cp, U't', U'T');
1762
747
      if (!consume_expected_sequence(result ? U"true"sv : U"false"sv))
1763
747
        set_error_and_return_default("expected '"sv,
1764
711
                       to_sv(result),
1765
711
                       "', saw '"sv,
1766
711
                       to_sv(recording_buffer),
1767
711
                       "'"sv);
1768
711
      stop_recording();
1769
1770
711
      if (cp && !is_value_terminator(*cp))
1771
711
        set_error_and_return_default("expected value-terminator, saw '"sv, to_sv(*cp), "'"sv);
1772
1773
694
      return result;
1774
711
    }
1775
1776
    TOML_NODISCARD
1777
    TOML_NEVER_INLINE
1778
    double parse_inf_or_nan()
1779
1.84k
    {
1780
1.84k
      return_if_error({});
1781
1.84k
      assert_not_eof();
1782
1.84k
      TOML_ASSERT_ASSUME(is_match(*cp, U'i', U'n', U'I', U'N', U'+', U'-'));
1783
1.84k
      push_parse_scope("floating-point"sv);
1784
1785
1.84k
      start_recording(true);
1786
1.84k
      const bool negative = *cp == U'-';
1787
1.84k
      if (negative || *cp == U'+')
1788
524
        advance_and_return_if_error_or_eof({});
1789
1790
1.84k
      const bool inf = is_match(*cp, U'i', U'I');
1791
1.84k
      if (!consume_expected_sequence(inf ? U"inf"sv : U"nan"sv))
1792
1.84k
        set_error_and_return_default("expected '"sv,
1793
1.81k
                       inf ? "inf"sv : "nan"sv,
1794
1.81k
                       "', saw '"sv,
1795
1.81k
                       to_sv(recording_buffer),
1796
1.81k
                       "'"sv);
1797
1.81k
      stop_recording();
1798
1799
1.81k
      if (cp && !is_value_terminator(*cp))
1800
1.81k
        set_error_and_return_default("expected value-terminator, saw '"sv, to_sv(*cp), "'"sv);
1801
1802
1.77k
      return inf ? (negative ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity())
1803
1.77k
             : std::numeric_limits<double>::quiet_NaN();
1804
1.81k
    }
1805
1806
    TOML_NODISCARD
1807
    TOML_NEVER_INLINE
1808
    double parse_float()
1809
5.15k
    {
1810
5.15k
      return_if_error({});
1811
5.15k
      assert_not_eof();
1812
5.15k
      TOML_ASSERT_ASSUME(is_match(*cp, U'+', U'-', U'.') || is_decimal_digit(*cp));
1813
5.15k
      push_parse_scope("floating-point"sv);
1814
1815
      // sign
1816
5.15k
      const int sign = *cp == U'-' ? -1 : 1;
1817
5.15k
      if (is_match(*cp, U'+', U'-'))
1818
1.13k
        advance_and_return_if_error_or_eof({});
1819
1820
      // consume value chars
1821
5.15k
      char chars[utf8_buffered_reader::max_history_length];
1822
5.15k
      size_t length        = {};
1823
5.15k
      const utf8_codepoint* prev = {};
1824
5.15k
      bool seen_decimal = false, seen_exponent = false;
1825
5.15k
      char first_integer_part = '\0';
1826
51.9k
      while (!is_eof() && !is_value_terminator(*cp))
1827
46.9k
      {
1828
46.9k
        if (*cp == U'_')
1829
1.66k
        {
1830
1.66k
          if (!prev || !is_decimal_digit(*prev))
1831
1.66k
            set_error_and_return_default("underscores may only follow digits"sv);
1832
1833
1.66k
          prev = cp;
1834
1.66k
          advance_and_return_if_error_or_eof({});
1835
1.66k
          continue;
1836
1.66k
        }
1837
45.2k
        else if TOML_UNLIKELY(prev && *prev == U'_' && !is_decimal_digit(*cp))
1838
45.2k
          set_error_and_return_default("underscores must be followed by digits"sv);
1839
45.2k
        else if TOML_UNLIKELY(length == sizeof(chars))
1840
45.2k
          set_error_and_return_default("exceeds length limit of "sv,
1841
45.2k
                         sizeof(chars),
1842
45.2k
                         " digits"sv,
1843
45.2k
                         (seen_exponent ? ""sv : " (consider using exponent notation)"sv));
1844
45.2k
        else if (*cp == U'.')
1845
3.70k
        {
1846
          // .1
1847
          // -.1
1848
          // +.1 (no integer part)
1849
3.70k
          if (!first_integer_part)
1850
3.70k
            set_error_and_return_default("expected decimal digit, saw '.'"sv);
1851
1852
          // 1.0e+.10 (exponent cannot have '.')
1853
3.67k
          else if (seen_exponent)
1854
3.67k
            set_error_and_return_default("expected exponent decimal digit or sign, saw '.'"sv);
1855
1856
          // 1.0.e+.10
1857
          // 1..0
1858
          // (multiple '.')
1859
3.67k
          else if (seen_decimal)
1860
5
            set_error_and_return_default("expected decimal digit or exponent, saw '.'"sv);
1861
1862
3.67k
          seen_decimal = true;
1863
3.67k
        }
1864
41.5k
        else if (is_match(*cp, U'e', U'E'))
1865
1.43k
        {
1866
1.43k
          if (prev && !is_decimal_digit(*prev))
1867
1.43k
            set_error_and_return_default("expected decimal digit, saw '"sv, to_sv(*cp), "'"sv);
1868
1869
          // 1.0ee+10 (multiple 'e')
1870
1.41k
          else if (seen_exponent)
1871
3
            set_error_and_return_default("expected decimal digit, saw '"sv, to_sv(*cp), "'"sv);
1872
1873
1.41k
          seen_decimal  = true; // implied
1874
1.41k
          seen_exponent = true;
1875
1.41k
        }
1876
40.1k
        else if (is_match(*cp, U'+', U'-'))
1877
661
        {
1878
          // 1.-0 (sign in mantissa)
1879
661
          if (!seen_exponent)
1880
661
            set_error_and_return_default("expected decimal digit or '.', saw '"sv, to_sv(*cp), "'"sv);
1881
1882
          // 1.0e1-0 (misplaced exponent sign)
1883
659
          else if (!is_match(*prev, U'e', U'E'))
1884
10
            set_error_and_return_default("expected exponent digit, saw '"sv, to_sv(*cp), "'"sv);
1885
661
        }
1886
39.4k
        else if (is_decimal_digit(*cp))
1887
39.4k
        {
1888
39.4k
          if (!seen_decimal)
1889
27.7k
          {
1890
27.7k
            if (!first_integer_part)
1891
5.10k
              first_integer_part = static_cast<char>(cp->bytes[0]);
1892
22.6k
            else if (first_integer_part == '0')
1893
1
              set_error_and_return_default("leading zeroes are prohibited"sv);
1894
27.7k
          }
1895
39.4k
        }
1896
50
        else
1897
50
          set_error_and_return_default("expected decimal digit, saw '"sv, to_sv(*cp), "'"sv);
1898
1899
45.1k
        chars[length++] = static_cast<char>(cp->bytes[0]);
1900
45.1k
        prev      = cp;
1901
45.1k
        advance_and_return_if_error({});
1902
45.1k
      }
1903
1904
      // sanity-check ending state
1905
5.01k
      if (prev)
1906
5.01k
      {
1907
5.01k
        if (*prev == U'_')
1908
1
        {
1909
1
          set_error_and_return_if_eof({});
1910
1
          set_error_and_return_default("underscores must be followed by digits"sv);
1911
1
        }
1912
5.00k
        else if (is_match(*prev, U'e', U'E', U'+', U'-', U'.'))
1913
43
        {
1914
43
          set_error_and_return_if_eof({});
1915
16
          set_error_and_return_default("expected decimal digit, saw '"sv, to_sv(*cp), "'"sv);
1916
16
        }
1917
5.01k
      }
1918
1919
      // convert to double
1920
4.96k
      double result;
1921
#if TOML_FLOAT_CHARCONV
1922
      {
1923
        auto fc_result = std::from_chars(chars, chars + length, result);
1924
        switch (fc_result.ec)
1925
        {
1926
          TOML_LIKELY_CASE
1927
          case std::errc{}: // ok
1928
            return result * sign;
1929
1930
          case std::errc::invalid_argument:
1931
            set_error_and_return_default("'"sv,
1932
                           std::string_view{ chars, length },
1933
                           "' could not be interpreted as a value"sv);
1934
            break;
1935
1936
          case std::errc::result_out_of_range:
1937
            set_error_and_return_default("'"sv,
1938
                           std::string_view{ chars, length },
1939
                           "' is not representable in 64 bits"sv);
1940
            break;
1941
1942
          default: //??
1943
            set_error_and_return_default("an unspecified error occurred while trying to interpret '"sv,
1944
                           std::string_view{ chars, length },
1945
                           "' as a value"sv);
1946
        }
1947
      }
1948
#else
1949
4.96k
      {
1950
4.96k
        std::stringstream ss;
1951
4.96k
        ss.imbue(std::locale::classic());
1952
4.96k
        ss.write(chars, static_cast<std::streamsize>(length));
1953
4.96k
        if ((ss >> result))
1954
4.96k
          return result * sign;
1955
7
        else
1956
4.96k
          set_error_and_return_default("'"sv,
1957
4.96k
                         std::string_view{ chars, length },
1958
4.96k
                         "' could not be interpreted as a value"sv);
1959
4.96k
      }
1960
4.96k
#endif
1961
4.96k
    }
1962
1963
    TOML_NODISCARD
1964
    TOML_NEVER_INLINE
1965
    double parse_hex_float()
1966
10
    {
1967
10
      return_if_error({});
1968
10
      assert_not_eof();
1969
10
      TOML_ASSERT_ASSUME(is_match(*cp, U'0', U'+', U'-'));
1970
10
      push_parse_scope("hexadecimal floating-point"sv);
1971
1972
#if TOML_LANG_UNRELEASED // toml/issues/562 (hexfloats)
1973
1974
      // sign
1975
      const int sign = *cp == U'-' ? -1 : 1;
1976
      if (is_match(*cp, U'+', U'-'))
1977
        advance_and_return_if_error_or_eof({});
1978
1979
      // '0'
1980
      if (*cp != U'0')
1981
        set_error_and_return_default(" expected '0', saw '"sv, to_sv(*cp), "'"sv);
1982
      advance_and_return_if_error_or_eof({});
1983
1984
      // 'x' or 'X'
1985
      if (!is_match(*cp, U'x', U'X'))
1986
        set_error_and_return_default("expected 'x' or 'X', saw '"sv, to_sv(*cp), "'"sv);
1987
      advance_and_return_if_error_or_eof({});
1988
1989
      // <HEX DIGITS> ([.]<HEX DIGITS>)? [pP] [+-]? <DEC DIGITS>
1990
1991
      // consume value fragments
1992
      struct fragment
1993
      {
1994
        char chars[24];
1995
        size_t length;
1996
        double value;
1997
      };
1998
      fragment fragments[] = {
1999
        {}, // mantissa, whole part
2000
        {}, // mantissa, fractional part
2001
        {}  // exponent
2002
      };
2003
      fragment* current_fragment = fragments;
2004
      const utf8_codepoint* prev = {};
2005
      int exponent_sign      = 1;
2006
      while (!is_eof() && !is_value_terminator(*cp))
2007
      {
2008
        if (*cp == U'_')
2009
        {
2010
          if (!prev || !is_hexadecimal_digit(*prev))
2011
            set_error_and_return_default("underscores may only follow digits"sv);
2012
2013
          prev = cp;
2014
          advance_and_return_if_error_or_eof({});
2015
          continue;
2016
        }
2017
        else if (prev && *prev == U'_' && !is_hexadecimal_digit(*cp))
2018
          set_error_and_return_default("underscores must be followed by digits"sv);
2019
        else if (*cp == U'.')
2020
        {
2021
          // 0x10.0p-.0 (exponent cannot have '.')
2022
          if (current_fragment == fragments + 2)
2023
            set_error_and_return_default("expected exponent digit or sign, saw '.'"sv);
2024
2025
          // 0x10.0.p-0 (multiple '.')
2026
          else if (current_fragment == fragments + 1)
2027
            set_error_and_return_default("expected hexadecimal digit or exponent, saw '.'"sv);
2028
2029
          else
2030
            current_fragment++;
2031
        }
2032
        else if (is_match(*cp, U'p', U'P'))
2033
        {
2034
          // 0x10.0pp-0 (multiple 'p')
2035
          if (current_fragment == fragments + 2)
2036
            set_error_and_return_default("expected exponent digit or sign, saw '"sv, to_sv(*cp), "'"sv);
2037
2038
          // 0x.p-0 (mantissa is just '.')
2039
          else if (fragments[0].length == 0u && fragments[1].length == 0u)
2040
            set_error_and_return_default("expected hexadecimal digit, saw '"sv, to_sv(*cp), "'"sv);
2041
2042
          else
2043
            current_fragment = fragments + 2;
2044
        }
2045
        else if (is_match(*cp, U'+', U'-'))
2046
        {
2047
          // 0x-10.0p-0 (sign in mantissa)
2048
          if (current_fragment != fragments + 2)
2049
            set_error_and_return_default("expected hexadecimal digit or '.', saw '"sv, to_sv(*cp), "'"sv);
2050
2051
          // 0x10.0p0- (misplaced exponent sign)
2052
          else if (!is_match(*prev, U'p', U'P'))
2053
            set_error_and_return_default("expected exponent digit, saw '"sv, to_sv(*cp), "'"sv);
2054
2055
          else
2056
            exponent_sign = *cp == U'-' ? -1 : 1;
2057
        }
2058
        else if (current_fragment < fragments + 2 && !is_hexadecimal_digit(*cp))
2059
          set_error_and_return_default("expected hexadecimal digit or '.', saw '"sv, to_sv(*cp), "'"sv);
2060
        else if (current_fragment == fragments + 2 && !is_decimal_digit(*cp))
2061
          set_error_and_return_default("expected exponent digit or sign, saw '"sv, to_sv(*cp), "'"sv);
2062
        else if (current_fragment->length == sizeof(fragment::chars))
2063
          set_error_and_return_default("fragment exceeeds maximum length of "sv,
2064
                         sizeof(fragment::chars),
2065
                         " characters"sv);
2066
        else
2067
          current_fragment->chars[current_fragment->length++] = static_cast<char>(cp->bytes[0]);
2068
2069
        prev = cp;
2070
        advance_and_return_if_error({});
2071
      }
2072
2073
      // sanity-check ending state
2074
      if (current_fragment != fragments + 2 || current_fragment->length == 0u)
2075
      {
2076
        set_error_and_return_if_eof({});
2077
        set_error_and_return_default("missing exponent"sv);
2078
      }
2079
      else if (prev && *prev == U'_')
2080
      {
2081
        set_error_and_return_if_eof({});
2082
        set_error_and_return_default("underscores must be followed by digits"sv);
2083
      }
2084
2085
      // calculate values for the three fragments
2086
      for (int fragment_idx = 0; fragment_idx < 3; fragment_idx++)
2087
      {
2088
        auto& f       = fragments[fragment_idx];
2089
        const uint32_t base = fragment_idx == 2 ? 10u : 16u;
2090
2091
        // left-trim zeroes
2092
        const char* c = f.chars;
2093
        size_t sig    = {};
2094
        while (f.length && *c == '0')
2095
        {
2096
          f.length--;
2097
          c++;
2098
          sig++;
2099
        }
2100
        if (!f.length)
2101
          continue;
2102
2103
        // calculate value
2104
        auto place = 1u;
2105
        for (size_t i = 0; i < f.length - 1u; i++)
2106
          place *= base;
2107
        uint32_t val{};
2108
        while (place)
2109
        {
2110
          if (base == 16)
2111
            val += place * hex_to_dec(*c);
2112
          else
2113
            val += place * static_cast<uint32_t>(*c - '0');
2114
          if (fragment_idx == 1)
2115
            sig++;
2116
          c++;
2117
          place /= base;
2118
        }
2119
        f.value = static_cast<double>(val);
2120
2121
        // shift the fractional part
2122
        if (fragment_idx == 1)
2123
        {
2124
          while (sig--)
2125
            f.value /= base;
2126
        }
2127
      }
2128
2129
      return (fragments[0].value + fragments[1].value) * pow(2.0, fragments[2].value * exponent_sign) * sign;
2130
2131
#else // !TOML_LANG_UNRELEASED
2132
2133
10
      set_error_and_return_default("hexadecimal floating-point values are not supported "
2134
10
                     "in TOML 1.0.0 and earlier"sv);
2135
2136
10
#endif // !TOML_LANG_UNRELEASED
2137
10
    }
2138
2139
    template <uint64_t base>
2140
    TOML_NODISCARD
2141
    TOML_NEVER_INLINE
2142
    int64_t parse_integer()
2143
33.1k
    {
2144
33.1k
      return_if_error({});
2145
33.1k
      assert_not_eof();
2146
33.1k
      using traits = parse_integer_traits<base>;
2147
33.1k
      push_parse_scope(traits::scope_qualifier);
2148
2149
33.1k
      [[maybe_unused]] int64_t sign = 1;
2150
      if constexpr (traits::is_signed)
2151
27.4k
      {
2152
27.4k
        sign = *cp == U'-' ? -1 : 1;
2153
27.4k
        if (is_match(*cp, U'+', U'-'))
2154
8.93k
          advance_and_return_if_error_or_eof({});
2155
27.4k
      }
2156
2157
      if constexpr (base == 10)
2158
27.4k
      {
2159
27.4k
        if (!traits::is_digit(*cp))
2160
27.4k
          set_error_and_return_default("expected expected digit or sign, saw '"sv, to_sv(*cp), "'"sv);
2161
      }
2162
      else
2163
5.74k
      {
2164
        // '0'
2165
5.74k
        if (*cp != U'0')
2166
5.74k
          set_error_and_return_default("expected '0', saw '"sv, to_sv(*cp), "'"sv);
2167
5.73k
        advance_and_return_if_error_or_eof({});
2168
2169
        // 'b', 'o', 'x'
2170
5.73k
        if (*cp != traits::prefix_codepoint)
2171
5.73k
          set_error_and_return_default("expected '"sv, traits::prefix, "', saw '"sv, to_sv(*cp), "'"sv);
2172
5.73k
        advance_and_return_if_error_or_eof({});
2173
2174
5.72k
        if (!traits::is_digit(*cp))
2175
5.72k
          set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv);
2176
5.72k
      }
2177
2178
      // consume digits
2179
33.0k
      char digits[utf8_buffered_reader::max_history_length];
2180
33.1k
      size_t length        = {};
2181
33.1k
      const utf8_codepoint* prev = {};
2182
344k
      while (!is_eof() && !is_value_terminator(*cp))
2183
311k
      {
2184
311k
        if (*cp == U'_')
2185
34.4k
        {
2186
34.4k
          if (!prev || !traits::is_digit(*prev))
2187
34.4k
            set_error_and_return_default("underscores may only follow digits"sv);
2188
2189
34.4k
          prev = cp;
2190
34.4k
          advance_and_return_if_error_or_eof({});
2191
34.4k
          continue;
2192
34.4k
        }
2193
277k
        else if TOML_UNLIKELY(prev && *prev == U'_' && !traits::is_digit(*cp))
2194
277k
          set_error_and_return_default("underscores must be followed by digits"sv);
2195
277k
        else if TOML_UNLIKELY(!traits::is_digit(*cp))
2196
277k
          set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv);
2197
277k
        else if TOML_UNLIKELY(length == sizeof(digits))
2198
277k
          set_error_and_return_default("exceeds length limit of "sv, sizeof(digits), " digits"sv);
2199
277k
        else
2200
277k
          digits[length++] = static_cast<char>(cp->bytes[0]);
2201
2202
277k
        prev = cp;
2203
277k
        advance_and_return_if_error({});
2204
277k
      }
2205
2206
      // sanity check ending state
2207
32.8k
      if (prev && *prev == U'_')
2208
4
      {
2209
4
        set_error_and_return_if_eof({});
2210
4
        set_error_and_return_default("underscores must be followed by digits"sv);
2211
4
      }
2212
2213
      // single digits can be converted trivially
2214
32.8k
      if (length == 1u)
2215
2.25k
      {
2216
2.25k
        int64_t result;
2217
2218
        if constexpr (base == 16)
2219
1.48k
          result = static_cast<int64_t>(hex_to_dec(digits[0]));
2220
        else
2221
771
          result = static_cast<int64_t>(digits[0] - '0');
2222
2223
        if constexpr (traits::is_signed)
2224
0
          result *= sign;
2225
2226
2.25k
        return result;
2227
2.25k
      }
2228
2229
      // bin, oct and hex allow leading zeroes so trim them first
2230
30.5k
      const char* end = digits + length;
2231
30.5k
      const char* msd = digits;
2232
      if constexpr (base != 10)
2233
3.30k
      {
2234
23.7k
        while (msd < end && *msd == '0')
2235
20.4k
          msd++;
2236
3.30k
        if (msd == end)
2237
1.53k
          return 0ll;
2238
      }
2239
2240
      // decimal integers do not allow leading zeroes
2241
      else
2242
27.2k
      {
2243
27.2k
        if TOML_UNLIKELY(digits[0] == '0')
2244
27.2k
          set_error_and_return_default("leading zeroes are prohibited"sv);
2245
27.2k
      }
2246
2247
      // range check
2248
30.5k
      if TOML_UNLIKELY(static_cast<size_t>(end - msd) > traits::max_digits)
2249
30.5k
        set_error_and_return_default("'"sv,
2250
30.5k
                       traits::full_prefix,
2251
30.5k
                       std::string_view{ digits, length },
2252
30.5k
                       "' is not representable as a signed 64-bit integer"sv);
2253
2254
      // do the thing
2255
30.5k
      {
2256
30.5k
        uint64_t result = {};
2257
30.5k
        {
2258
30.5k
          uint64_t power = 1;
2259
281k
          while (--end >= msd)
2260
251k
          {
2261
            if constexpr (base == 16)
2262
4.31k
              result += power * hex_to_dec(*end);
2263
            else
2264
246k
              result += power * static_cast<uint64_t>(*end - '0');
2265
2266
251k
            power *= base;
2267
251k
          }
2268
30.5k
        }
2269
2270
        // range check
2271
30.5k
        static constexpr auto i64_max = static_cast<uint64_t>((std::numeric_limits<int64_t>::max)());
2272
30.5k
        if TOML_UNLIKELY(result > i64_max + (sign < 0 ? 1u : 0u))
2273
30.5k
          set_error_and_return_default("'"sv,
2274
30.4k
                         traits::full_prefix,
2275
30.4k
                         std::string_view{ digits, length },
2276
30.4k
                         "' is not representable as a signed 64-bit integer"sv);
2277
2278
        if constexpr (traits::is_signed)
2279
27.2k
        {
2280
          // avoid signed multiply UB when parsing INT64_MIN
2281
27.2k
          if TOML_UNLIKELY(sign < 0 && result == i64_max + 1u)
2282
194
            return (std::numeric_limits<int64_t>::min)();
2283
2284
27.0k
          return static_cast<int64_t>(result) * sign;
2285
        }
2286
        else
2287
3.26k
          return static_cast<int64_t>(result);
2288
30.4k
      }
2289
30.4k
    }
long toml::v3::impl::impl_ex::parser::parse_integer<16ul>()
Line
Count
Source
2143
2.77k
    {
2144
2.77k
      return_if_error({});
2145
2.77k
      assert_not_eof();
2146
2.77k
      using traits = parse_integer_traits<base>;
2147
2.77k
      push_parse_scope(traits::scope_qualifier);
2148
2149
2.77k
      [[maybe_unused]] int64_t sign = 1;
2150
      if constexpr (traits::is_signed)
2151
      {
2152
        sign = *cp == U'-' ? -1 : 1;
2153
        if (is_match(*cp, U'+', U'-'))
2154
          advance_and_return_if_error_or_eof({});
2155
      }
2156
2157
      if constexpr (base == 10)
2158
      {
2159
        if (!traits::is_digit(*cp))
2160
          set_error_and_return_default("expected expected digit or sign, saw '"sv, to_sv(*cp), "'"sv);
2161
      }
2162
      else
2163
2.77k
      {
2164
        // '0'
2165
2.77k
        if (*cp != U'0')
2166
2.77k
          set_error_and_return_default("expected '0', saw '"sv, to_sv(*cp), "'"sv);
2167
2.76k
        advance_and_return_if_error_or_eof({});
2168
2169
        // 'b', 'o', 'x'
2170
2.76k
        if (*cp != traits::prefix_codepoint)
2171
2.76k
          set_error_and_return_default("expected '"sv, traits::prefix, "', saw '"sv, to_sv(*cp), "'"sv);
2172
2.76k
        advance_and_return_if_error_or_eof({});
2173
2174
2.76k
        if (!traits::is_digit(*cp))
2175
2.76k
          set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv);
2176
2.76k
      }
2177
2178
      // consume digits
2179
2.73k
      char digits[utf8_buffered_reader::max_history_length];
2180
2.77k
      size_t length        = {};
2181
2.77k
      const utf8_codepoint* prev = {};
2182
18.4k
      while (!is_eof() && !is_value_terminator(*cp))
2183
15.7k
      {
2184
15.7k
        if (*cp == U'_')
2185
3.91k
        {
2186
3.91k
          if (!prev || !traits::is_digit(*prev))
2187
3.91k
            set_error_and_return_default("underscores may only follow digits"sv);
2188
2189
3.91k
          prev = cp;
2190
3.91k
          advance_and_return_if_error_or_eof({});
2191
3.90k
          continue;
2192
3.91k
        }
2193
11.8k
        else if TOML_UNLIKELY(prev && *prev == U'_' && !traits::is_digit(*cp))
2194
11.8k
          set_error_and_return_default("underscores must be followed by digits"sv);
2195
11.8k
        else if TOML_UNLIKELY(!traits::is_digit(*cp))
2196
11.8k
          set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv);
2197
11.8k
        else if TOML_UNLIKELY(length == sizeof(digits))
2198
11.8k
          set_error_and_return_default("exceeds length limit of "sv, sizeof(digits), " digits"sv);
2199
11.8k
        else
2200
11.8k
          digits[length++] = static_cast<char>(cp->bytes[0]);
2201
2202
11.8k
        prev = cp;
2203
11.8k
        advance_and_return_if_error({});
2204
11.8k
      }
2205
2206
      // sanity check ending state
2207
2.72k
      if (prev && *prev == U'_')
2208
1
      {
2209
1
        set_error_and_return_if_eof({});
2210
1
        set_error_and_return_default("underscores must be followed by digits"sv);
2211
1
      }
2212
2213
      // single digits can be converted trivially
2214
2.72k
      if (length == 1u)
2215
1.48k
      {
2216
1.48k
        int64_t result;
2217
2218
        if constexpr (base == 16)
2219
1.48k
          result = static_cast<int64_t>(hex_to_dec(digits[0]));
2220
        else
2221
          result = static_cast<int64_t>(digits[0] - '0');
2222
2223
        if constexpr (traits::is_signed)
2224
          result *= sign;
2225
2226
1.48k
        return result;
2227
1.48k
      }
2228
2229
      // bin, oct and hex allow leading zeroes so trim them first
2230
1.23k
      const char* end = digits + length;
2231
1.23k
      const char* msd = digits;
2232
      if constexpr (base != 10)
2233
1.23k
      {
2234
6.36k
        while (msd < end && *msd == '0')
2235
5.12k
          msd++;
2236
1.23k
        if (msd == end)
2237
399
          return 0ll;
2238
      }
2239
2240
      // decimal integers do not allow leading zeroes
2241
      else
2242
      {
2243
        if TOML_UNLIKELY(digits[0] == '0')
2244
          set_error_and_return_default("leading zeroes are prohibited"sv);
2245
      }
2246
2247
      // range check
2248
1.23k
      if TOML_UNLIKELY(static_cast<size_t>(end - msd) > traits::max_digits)
2249
1.23k
        set_error_and_return_default("'"sv,
2250
1.22k
                       traits::full_prefix,
2251
1.22k
                       std::string_view{ digits, length },
2252
1.22k
                       "' is not representable as a signed 64-bit integer"sv);
2253
2254
      // do the thing
2255
1.22k
      {
2256
1.22k
        uint64_t result = {};
2257
1.22k
        {
2258
1.22k
          uint64_t power = 1;
2259
5.54k
          while (--end >= msd)
2260
4.31k
          {
2261
            if constexpr (base == 16)
2262
4.31k
              result += power * hex_to_dec(*end);
2263
            else
2264
              result += power * static_cast<uint64_t>(*end - '0');
2265
2266
4.31k
            power *= base;
2267
4.31k
          }
2268
1.22k
        }
2269
2270
        // range check
2271
1.22k
        static constexpr auto i64_max = static_cast<uint64_t>((std::numeric_limits<int64_t>::max)());
2272
1.22k
        if TOML_UNLIKELY(result > i64_max + (sign < 0 ? 1u : 0u))
2273
1.22k
          set_error_and_return_default("'"sv,
2274
1.20k
                         traits::full_prefix,
2275
1.20k
                         std::string_view{ digits, length },
2276
1.20k
                         "' is not representable as a signed 64-bit integer"sv);
2277
2278
        if constexpr (traits::is_signed)
2279
        {
2280
          // avoid signed multiply UB when parsing INT64_MIN
2281
          if TOML_UNLIKELY(sign < 0 && result == i64_max + 1u)
2282
            return (std::numeric_limits<int64_t>::min)();
2283
2284
          return static_cast<int64_t>(result) * sign;
2285
        }
2286
        else
2287
1.20k
          return static_cast<int64_t>(result);
2288
1.20k
      }
2289
1.20k
    }
long toml::v3::impl::impl_ex::parser::parse_integer<8ul>()
Line
Count
Source
2143
1.75k
    {
2144
1.75k
      return_if_error({});
2145
1.75k
      assert_not_eof();
2146
1.75k
      using traits = parse_integer_traits<base>;
2147
1.75k
      push_parse_scope(traits::scope_qualifier);
2148
2149
1.75k
      [[maybe_unused]] int64_t sign = 1;
2150
      if constexpr (traits::is_signed)
2151
      {
2152
        sign = *cp == U'-' ? -1 : 1;
2153
        if (is_match(*cp, U'+', U'-'))
2154
          advance_and_return_if_error_or_eof({});
2155
      }
2156
2157
      if constexpr (base == 10)
2158
      {
2159
        if (!traits::is_digit(*cp))
2160
          set_error_and_return_default("expected expected digit or sign, saw '"sv, to_sv(*cp), "'"sv);
2161
      }
2162
      else
2163
1.75k
      {
2164
        // '0'
2165
1.75k
        if (*cp != U'0')
2166
1.75k
          set_error_and_return_default("expected '0', saw '"sv, to_sv(*cp), "'"sv);
2167
1.75k
        advance_and_return_if_error_or_eof({});
2168
2169
        // 'b', 'o', 'x'
2170
1.75k
        if (*cp != traits::prefix_codepoint)
2171
1.75k
          set_error_and_return_default("expected '"sv, traits::prefix, "', saw '"sv, to_sv(*cp), "'"sv);
2172
1.75k
        advance_and_return_if_error_or_eof({});
2173
2174
1.75k
        if (!traits::is_digit(*cp))
2175
1.75k
          set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv);
2176
1.75k
      }
2177
2178
      // consume digits
2179
1.72k
      char digits[utf8_buffered_reader::max_history_length];
2180
1.75k
      size_t length        = {};
2181
1.75k
      const utf8_codepoint* prev = {};
2182
29.2k
      while (!is_eof() && !is_value_terminator(*cp))
2183
27.5k
      {
2184
27.5k
        if (*cp == U'_')
2185
11.2k
        {
2186
11.2k
          if (!prev || !traits::is_digit(*prev))
2187
11.2k
            set_error_and_return_default("underscores may only follow digits"sv);
2188
2189
11.2k
          prev = cp;
2190
11.2k
          advance_and_return_if_error_or_eof({});
2191
11.2k
          continue;
2192
11.2k
        }
2193
16.2k
        else if TOML_UNLIKELY(prev && *prev == U'_' && !traits::is_digit(*cp))
2194
16.2k
          set_error_and_return_default("underscores must be followed by digits"sv);
2195
16.2k
        else if TOML_UNLIKELY(!traits::is_digit(*cp))
2196
16.2k
          set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv);
2197
16.2k
        else if TOML_UNLIKELY(length == sizeof(digits))
2198
16.2k
          set_error_and_return_default("exceeds length limit of "sv, sizeof(digits), " digits"sv);
2199
16.2k
        else
2200
16.2k
          digits[length++] = static_cast<char>(cp->bytes[0]);
2201
2202
16.2k
        prev = cp;
2203
16.2k
        advance_and_return_if_error({});
2204
16.2k
      }
2205
2206
      // sanity check ending state
2207
1.69k
      if (prev && *prev == U'_')
2208
1
      {
2209
1
        set_error_and_return_if_eof({});
2210
1
        set_error_and_return_default("underscores must be followed by digits"sv);
2211
1
      }
2212
2213
      // single digits can be converted trivially
2214
1.69k
      if (length == 1u)
2215
372
      {
2216
372
        int64_t result;
2217
2218
        if constexpr (base == 16)
2219
          result = static_cast<int64_t>(hex_to_dec(digits[0]));
2220
        else
2221
372
          result = static_cast<int64_t>(digits[0] - '0');
2222
2223
        if constexpr (traits::is_signed)
2224
          result *= sign;
2225
2226
372
        return result;
2227
372
      }
2228
2229
      // bin, oct and hex allow leading zeroes so trim them first
2230
1.32k
      const char* end = digits + length;
2231
1.32k
      const char* msd = digits;
2232
      if constexpr (base != 10)
2233
1.32k
      {
2234
14.1k
        while (msd < end && *msd == '0')
2235
12.8k
          msd++;
2236
1.32k
        if (msd == end)
2237
775
          return 0ll;
2238
      }
2239
2240
      // decimal integers do not allow leading zeroes
2241
      else
2242
      {
2243
        if TOML_UNLIKELY(digits[0] == '0')
2244
          set_error_and_return_default("leading zeroes are prohibited"sv);
2245
      }
2246
2247
      // range check
2248
1.32k
      if TOML_UNLIKELY(static_cast<size_t>(end - msd) > traits::max_digits)
2249
1.32k
        set_error_and_return_default("'"sv,
2250
1.32k
                       traits::full_prefix,
2251
1.32k
                       std::string_view{ digits, length },
2252
1.32k
                       "' is not representable as a signed 64-bit integer"sv);
2253
2254
      // do the thing
2255
1.32k
      {
2256
1.32k
        uint64_t result = {};
2257
1.32k
        {
2258
1.32k
          uint64_t power = 1;
2259
3.71k
          while (--end >= msd)
2260
2.39k
          {
2261
            if constexpr (base == 16)
2262
              result += power * hex_to_dec(*end);
2263
            else
2264
2.39k
              result += power * static_cast<uint64_t>(*end - '0');
2265
2266
2.39k
            power *= base;
2267
2.39k
          }
2268
1.32k
        }
2269
2270
        // range check
2271
1.32k
        static constexpr auto i64_max = static_cast<uint64_t>((std::numeric_limits<int64_t>::max)());
2272
1.32k
        if TOML_UNLIKELY(result > i64_max + (sign < 0 ? 1u : 0u))
2273
1.32k
          set_error_and_return_default("'"sv,
2274
1.32k
                         traits::full_prefix,
2275
1.32k
                         std::string_view{ digits, length },
2276
1.32k
                         "' is not representable as a signed 64-bit integer"sv);
2277
2278
        if constexpr (traits::is_signed)
2279
        {
2280
          // avoid signed multiply UB when parsing INT64_MIN
2281
          if TOML_UNLIKELY(sign < 0 && result == i64_max + 1u)
2282
            return (std::numeric_limits<int64_t>::min)();
2283
2284
          return static_cast<int64_t>(result) * sign;
2285
        }
2286
        else
2287
1.32k
          return static_cast<int64_t>(result);
2288
1.32k
      }
2289
1.32k
    }
long toml::v3::impl::impl_ex::parser::parse_integer<2ul>()
Line
Count
Source
2143
1.21k
    {
2144
1.21k
      return_if_error({});
2145
1.21k
      assert_not_eof();
2146
1.21k
      using traits = parse_integer_traits<base>;
2147
1.21k
      push_parse_scope(traits::scope_qualifier);
2148
2149
1.21k
      [[maybe_unused]] int64_t sign = 1;
2150
      if constexpr (traits::is_signed)
2151
      {
2152
        sign = *cp == U'-' ? -1 : 1;
2153
        if (is_match(*cp, U'+', U'-'))
2154
          advance_and_return_if_error_or_eof({});
2155
      }
2156
2157
      if constexpr (base == 10)
2158
      {
2159
        if (!traits::is_digit(*cp))
2160
          set_error_and_return_default("expected expected digit or sign, saw '"sv, to_sv(*cp), "'"sv);
2161
      }
2162
      else
2163
1.21k
      {
2164
        // '0'
2165
1.21k
        if (*cp != U'0')
2166
1.21k
          set_error_and_return_default("expected '0', saw '"sv, to_sv(*cp), "'"sv);
2167
1.21k
        advance_and_return_if_error_or_eof({});
2168
2169
        // 'b', 'o', 'x'
2170
1.21k
        if (*cp != traits::prefix_codepoint)
2171
1.21k
          set_error_and_return_default("expected '"sv, traits::prefix, "', saw '"sv, to_sv(*cp), "'"sv);
2172
1.21k
        advance_and_return_if_error_or_eof({});
2173
2174
1.21k
        if (!traits::is_digit(*cp))
2175
1.21k
          set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv);
2176
1.21k
      }
2177
2178
      // consume digits
2179
1.17k
      char digits[utf8_buffered_reader::max_history_length];
2180
1.21k
      size_t length        = {};
2181
1.21k
      const utf8_codepoint* prev = {};
2182
9.79k
      while (!is_eof() && !is_value_terminator(*cp))
2183
8.64k
      {
2184
8.64k
        if (*cp == U'_')
2185
1.36k
        {
2186
1.36k
          if (!prev || !traits::is_digit(*prev))
2187
1.36k
            set_error_and_return_default("underscores may only follow digits"sv);
2188
2189
1.35k
          prev = cp;
2190
1.35k
          advance_and_return_if_error_or_eof({});
2191
1.35k
          continue;
2192
1.35k
        }
2193
7.28k
        else if TOML_UNLIKELY(prev && *prev == U'_' && !traits::is_digit(*cp))
2194
7.28k
          set_error_and_return_default("underscores must be followed by digits"sv);
2195
7.26k
        else if TOML_UNLIKELY(!traits::is_digit(*cp))
2196
7.26k
          set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv);
2197
7.22k
        else if TOML_UNLIKELY(length == sizeof(digits))
2198
7.22k
          set_error_and_return_default("exceeds length limit of "sv, sizeof(digits), " digits"sv);
2199
7.22k
        else
2200
7.22k
          digits[length++] = static_cast<char>(cp->bytes[0]);
2201
2202
7.22k
        prev = cp;
2203
7.22k
        advance_and_return_if_error({});
2204
7.22k
      }
2205
2206
      // sanity check ending state
2207
1.14k
      if (prev && *prev == U'_')
2208
1
      {
2209
1
        set_error_and_return_if_eof({});
2210
1
        set_error_and_return_default("underscores must be followed by digits"sv);
2211
1
      }
2212
2213
      // single digits can be converted trivially
2214
1.14k
      if (length == 1u)
2215
399
      {
2216
399
        int64_t result;
2217
2218
        if constexpr (base == 16)
2219
          result = static_cast<int64_t>(hex_to_dec(digits[0]));
2220
        else
2221
399
          result = static_cast<int64_t>(digits[0] - '0');
2222
2223
        if constexpr (traits::is_signed)
2224
          result *= sign;
2225
2226
399
        return result;
2227
399
      }
2228
2229
      // bin, oct and hex allow leading zeroes so trim them first
2230
743
      const char* end = digits + length;
2231
743
      const char* msd = digits;
2232
      if constexpr (base != 10)
2233
743
      {
2234
3.28k
        while (msd < end && *msd == '0')
2235
2.54k
          msd++;
2236
743
        if (msd == end)
2237
361
          return 0ll;
2238
      }
2239
2240
      // decimal integers do not allow leading zeroes
2241
      else
2242
      {
2243
        if TOML_UNLIKELY(digits[0] == '0')
2244
          set_error_and_return_default("leading zeroes are prohibited"sv);
2245
      }
2246
2247
      // range check
2248
743
      if TOML_UNLIKELY(static_cast<size_t>(end - msd) > traits::max_digits)
2249
743
        set_error_and_return_default("'"sv,
2250
740
                       traits::full_prefix,
2251
740
                       std::string_view{ digits, length },
2252
740
                       "' is not representable as a signed 64-bit integer"sv);
2253
2254
      // do the thing
2255
740
      {
2256
740
        uint64_t result = {};
2257
740
        {
2258
740
          uint64_t power = 1;
2259
3.89k
          while (--end >= msd)
2260
3.15k
          {
2261
            if constexpr (base == 16)
2262
              result += power * hex_to_dec(*end);
2263
            else
2264
3.15k
              result += power * static_cast<uint64_t>(*end - '0');
2265
2266
3.15k
            power *= base;
2267
3.15k
          }
2268
740
        }
2269
2270
        // range check
2271
740
        static constexpr auto i64_max = static_cast<uint64_t>((std::numeric_limits<int64_t>::max)());
2272
740
        if TOML_UNLIKELY(result > i64_max + (sign < 0 ? 1u : 0u))
2273
740
          set_error_and_return_default("'"sv,
2274
740
                         traits::full_prefix,
2275
740
                         std::string_view{ digits, length },
2276
740
                         "' is not representable as a signed 64-bit integer"sv);
2277
2278
        if constexpr (traits::is_signed)
2279
        {
2280
          // avoid signed multiply UB when parsing INT64_MIN
2281
          if TOML_UNLIKELY(sign < 0 && result == i64_max + 1u)
2282
            return (std::numeric_limits<int64_t>::min)();
2283
2284
          return static_cast<int64_t>(result) * sign;
2285
        }
2286
        else
2287
740
          return static_cast<int64_t>(result);
2288
740
      }
2289
740
    }
long toml::v3::impl::impl_ex::parser::parse_integer<10ul>()
Line
Count
Source
2143
27.4k
    {
2144
27.4k
      return_if_error({});
2145
27.4k
      assert_not_eof();
2146
27.4k
      using traits = parse_integer_traits<base>;
2147
27.4k
      push_parse_scope(traits::scope_qualifier);
2148
2149
27.4k
      [[maybe_unused]] int64_t sign = 1;
2150
      if constexpr (traits::is_signed)
2151
27.4k
      {
2152
27.4k
        sign = *cp == U'-' ? -1 : 1;
2153
27.4k
        if (is_match(*cp, U'+', U'-'))
2154
8.93k
          advance_and_return_if_error_or_eof({});
2155
27.4k
      }
2156
2157
      if constexpr (base == 10)
2158
27.4k
      {
2159
27.4k
        if (!traits::is_digit(*cp))
2160
27.4k
          set_error_and_return_default("expected expected digit or sign, saw '"sv, to_sv(*cp), "'"sv);
2161
      }
2162
      else
2163
      {
2164
        // '0'
2165
        if (*cp != U'0')
2166
          set_error_and_return_default("expected '0', saw '"sv, to_sv(*cp), "'"sv);
2167
        advance_and_return_if_error_or_eof({});
2168
2169
        // 'b', 'o', 'x'
2170
        if (*cp != traits::prefix_codepoint)
2171
          set_error_and_return_default("expected '"sv, traits::prefix, "', saw '"sv, to_sv(*cp), "'"sv);
2172
        advance_and_return_if_error_or_eof({});
2173
2174
        if (!traits::is_digit(*cp))
2175
          set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv);
2176
      }
2177
2178
      // consume digits
2179
27.4k
      char digits[utf8_buffered_reader::max_history_length];
2180
27.4k
      size_t length        = {};
2181
27.4k
      const utf8_codepoint* prev = {};
2182
287k
      while (!is_eof() && !is_value_terminator(*cp))
2183
259k
      {
2184
259k
        if (*cp == U'_')
2185
17.8k
        {
2186
17.8k
          if (!prev || !traits::is_digit(*prev))
2187
17.8k
            set_error_and_return_default("underscores may only follow digits"sv);
2188
2189
17.8k
          prev = cp;
2190
17.8k
          advance_and_return_if_error_or_eof({});
2191
17.8k
          continue;
2192
17.8k
        }
2193
242k
        else if TOML_UNLIKELY(prev && *prev == U'_' && !traits::is_digit(*cp))
2194
242k
          set_error_and_return_default("underscores must be followed by digits"sv);
2195
242k
        else if TOML_UNLIKELY(!traits::is_digit(*cp))
2196
242k
          set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv);
2197
241k
        else if TOML_UNLIKELY(length == sizeof(digits))
2198
241k
          set_error_and_return_default("exceeds length limit of "sv, sizeof(digits), " digits"sv);
2199
241k
        else
2200
241k
          digits[length++] = static_cast<char>(cp->bytes[0]);
2201
2202
241k
        prev = cp;
2203
241k
        advance_and_return_if_error({});
2204
241k
      }
2205
2206
      // sanity check ending state
2207
27.2k
      if (prev && *prev == U'_')
2208
1
      {
2209
1
        set_error_and_return_if_eof({});
2210
1
        set_error_and_return_default("underscores must be followed by digits"sv);
2211
1
      }
2212
2213
      // single digits can be converted trivially
2214
27.2k
      if (length == 1u)
2215
0
      {
2216
0
        int64_t result;
2217
2218
        if constexpr (base == 16)
2219
          result = static_cast<int64_t>(hex_to_dec(digits[0]));
2220
        else
2221
0
          result = static_cast<int64_t>(digits[0] - '0');
2222
2223
        if constexpr (traits::is_signed)
2224
0
          result *= sign;
2225
2226
0
        return result;
2227
0
      }
2228
2229
      // bin, oct and hex allow leading zeroes so trim them first
2230
27.2k
      const char* end = digits + length;
2231
27.2k
      const char* msd = digits;
2232
      if constexpr (base != 10)
2233
      {
2234
        while (msd < end && *msd == '0')
2235
          msd++;
2236
        if (msd == end)
2237
          return 0ll;
2238
      }
2239
2240
      // decimal integers do not allow leading zeroes
2241
      else
2242
27.2k
      {
2243
27.2k
        if TOML_UNLIKELY(digits[0] == '0')
2244
27.2k
          set_error_and_return_default("leading zeroes are prohibited"sv);
2245
27.2k
      }
2246
2247
      // range check
2248
27.2k
      if TOML_UNLIKELY(static_cast<size_t>(end - msd) > traits::max_digits)
2249
27.2k
        set_error_and_return_default("'"sv,
2250
27.2k
                       traits::full_prefix,
2251
27.2k
                       std::string_view{ digits, length },
2252
27.2k
                       "' is not representable as a signed 64-bit integer"sv);
2253
2254
      // do the thing
2255
27.2k
      {
2256
27.2k
        uint64_t result = {};
2257
27.2k
        {
2258
27.2k
          uint64_t power = 1;
2259
268k
          while (--end >= msd)
2260
241k
          {
2261
            if constexpr (base == 16)
2262
              result += power * hex_to_dec(*end);
2263
            else
2264
241k
              result += power * static_cast<uint64_t>(*end - '0');
2265
2266
241k
            power *= base;
2267
241k
          }
2268
27.2k
        }
2269
2270
        // range check
2271
27.2k
        static constexpr auto i64_max = static_cast<uint64_t>((std::numeric_limits<int64_t>::max)());
2272
27.2k
        if TOML_UNLIKELY(result > i64_max + (sign < 0 ? 1u : 0u))
2273
27.2k
          set_error_and_return_default("'"sv,
2274
27.2k
                         traits::full_prefix,
2275
27.2k
                         std::string_view{ digits, length },
2276
27.2k
                         "' is not representable as a signed 64-bit integer"sv);
2277
2278
        if constexpr (traits::is_signed)
2279
27.2k
        {
2280
          // avoid signed multiply UB when parsing INT64_MIN
2281
27.2k
          if TOML_UNLIKELY(sign < 0 && result == i64_max + 1u)
2282
194
            return (std::numeric_limits<int64_t>::min)();
2283
2284
27.0k
          return static_cast<int64_t>(result) * sign;
2285
        }
2286
        else
2287
          return static_cast<int64_t>(result);
2288
27.2k
      }
2289
27.2k
    }
2290
2291
    TOML_NODISCARD
2292
    TOML_NEVER_INLINE
2293
    date parse_date(bool part_of_datetime = false)
2294
7.52k
    {
2295
7.52k
      return_if_error({});
2296
7.52k
      assert_not_eof();
2297
7.52k
      TOML_ASSERT_ASSUME(is_decimal_digit(*cp));
2298
7.52k
      push_parse_scope("date"sv);
2299
2300
      // "YYYY"
2301
7.52k
      uint32_t digits[4];
2302
7.52k
      if (!consume_digit_sequence(digits, 4u))
2303
7.52k
        set_error_and_return_default("expected 4-digit year, saw '"sv, to_sv(cp), "'"sv);
2304
7.44k
      const auto year     = digits[3] + digits[2] * 10u + digits[1] * 100u + digits[0] * 1000u;
2305
7.44k
      const auto is_leap_year = (year % 4u == 0u) && ((year % 100u != 0u) || (year % 400u == 0u));
2306
7.44k
      set_error_and_return_if_eof({});
2307
2308
      // '-'
2309
7.44k
      if (*cp != U'-')
2310
7.44k
        set_error_and_return_default("expected '-', saw '"sv, to_sv(*cp), "'"sv);
2311
7.42k
      advance_and_return_if_error_or_eof({});
2312
2313
      // "MM"
2314
7.41k
      if (!consume_digit_sequence(digits, 2u))
2315
7.41k
        set_error_and_return_default("expected 2-digit month, saw '"sv, to_sv(cp), "'"sv);
2316
7.38k
      const auto month = digits[1] + digits[0] * 10u;
2317
7.38k
      if (month == 0u || month > 12u)
2318
7.38k
        set_error_and_return_default("expected month between 1 and 12 (inclusive), saw "sv, month);
2319
7.37k
      const auto max_days_in_month = month == 2u
2320
7.37k
                       ? (is_leap_year ? 29u : 28u)
2321
7.37k
                       : (month == 4u || month == 6u || month == 9u || month == 11u ? 30u : 31u);
2322
7.37k
      set_error_and_return_if_eof({});
2323
2324
      // '-'
2325
7.36k
      if (*cp != U'-')
2326
7.36k
        set_error_and_return_default("expected '-', saw '"sv, to_sv(*cp), "'"sv);
2327
7.34k
      advance_and_return_if_error_or_eof({});
2328
2329
      // "DD"
2330
7.34k
      if (!consume_digit_sequence(digits, 2u))
2331
7.34k
        set_error_and_return_default("expected 2-digit day, saw '"sv, to_sv(cp), "'"sv);
2332
7.31k
      const auto day = digits[1] + digits[0] * 10u;
2333
7.31k
      if (day == 0u || day > max_days_in_month)
2334
7.31k
        set_error_and_return_default("expected day between 1 and "sv,
2335
7.31k
                       max_days_in_month,
2336
7.31k
                       " (inclusive), saw "sv,
2337
7.31k
                       day);
2338
2339
7.31k
      if (!part_of_datetime && !is_eof() && !is_value_terminator(*cp))
2340
7.31k
        set_error_and_return_default("expected value-terminator, saw '"sv, to_sv(*cp), "'"sv);
2341
2342
7.29k
      return { year, month, day };
2343
7.31k
    }
2344
2345
    TOML_NODISCARD
2346
    TOML_NEVER_INLINE
2347
    time parse_time(bool part_of_datetime = false)
2348
6.70k
    {
2349
6.70k
      return_if_error({});
2350
6.70k
      assert_not_eof();
2351
6.70k
      TOML_ASSERT_ASSUME(is_decimal_digit(*cp));
2352
6.70k
      push_parse_scope("time"sv);
2353
2354
6.70k
      static constexpr size_t max_digits = 64; // far more than necessary but needed to allow fractional
2355
                           // millisecond truncation per the spec
2356
6.70k
      uint32_t digits[max_digits];
2357
2358
      // "HH"
2359
6.70k
      if (!consume_digit_sequence(digits, 2u))
2360
6.70k
        set_error_and_return_default("expected 2-digit hour, saw '"sv, to_sv(cp), "'"sv);
2361
6.62k
      const auto hour = digits[1] + digits[0] * 10u;
2362
6.62k
      if (hour > 23u)
2363
6.62k
        set_error_and_return_default("expected hour between 0 to 59 (inclusive), saw "sv, hour);
2364
6.61k
      set_error_and_return_if_eof({});
2365
2366
      // ':'
2367
6.61k
      if (*cp != U':')
2368
6.61k
        set_error_and_return_default("expected ':', saw '"sv, to_sv(*cp), "'"sv);
2369
6.59k
      advance_and_return_if_error_or_eof({});
2370
2371
      // "MM"
2372
6.58k
      if (!consume_digit_sequence(digits, 2u))
2373
6.58k
        set_error_and_return_default("expected 2-digit minute, saw '"sv, to_sv(cp), "'"sv);
2374
6.54k
      const auto minute = digits[1] + digits[0] * 10u;
2375
6.54k
      if (minute > 59u)
2376
6.54k
        set_error_and_return_default("expected minute between 0 and 59 (inclusive), saw "sv, minute);
2377
6.54k
      auto time = toml::time{ hour, minute };
2378
2379
      // ':'
2380
      if constexpr (TOML_LANG_UNRELEASED) // toml/issues/671 (allow omission of seconds)
2381
      {
2382
        if (is_eof() || is_value_terminator(*cp) || (part_of_datetime && is_match(*cp, U'+', U'-', U'Z', U'z')))
2383
          return time;
2384
      }
2385
      else
2386
6.54k
        set_error_and_return_if_eof({});
2387
6.54k
      if (*cp != U':')
2388
6.54k
        set_error_and_return_default("expected ':', saw '"sv, to_sv(*cp), "'"sv);
2389
6.51k
      advance_and_return_if_error_or_eof({});
2390
2391
      // "SS"
2392
6.50k
      if (!consume_digit_sequence(digits, 2u))
2393
6.50k
        set_error_and_return_default("expected 2-digit second, saw '"sv, to_sv(cp), "'"sv);
2394
6.47k
      const auto second = digits[1] + digits[0] * 10u;
2395
6.47k
      if (second > 59u)
2396
6.47k
        set_error_and_return_default("expected second between 0 and 59 (inclusive), saw "sv, second);
2397
6.47k
      time.second = static_cast<decltype(time.second)>(second);
2398
2399
      // '.' (early-exiting is allowed; fractional is optional)
2400
6.47k
      if (is_eof() || is_value_terminator(*cp) || (part_of_datetime && is_match(*cp, U'+', U'-', U'Z', U'z')))
2401
3.36k
        return time;
2402
3.10k
      if (*cp != U'.')
2403
3.10k
        set_error_and_return_default("expected '.', saw '"sv, to_sv(*cp), "'"sv);
2404
3.06k
      advance_and_return_if_error_or_eof({});
2405
2406
      // "FFFFFFFFF"
2407
3.06k
      size_t digit_count = consume_variable_length_digit_sequence(digits, max_digits);
2408
3.06k
      if (!digit_count)
2409
22
      {
2410
22
        set_error_and_return_if_eof({});
2411
22
        set_error_and_return_default("expected fractional digits, saw '"sv, to_sv(*cp), "'"sv);
2412
22
      }
2413
3.04k
      else if (!is_eof())
2414
3.00k
      {
2415
3.00k
        if (digit_count == max_digits && is_decimal_digit(*cp))
2416
3.00k
          set_error_and_return_default("fractional component exceeds maximum precision of "sv, max_digits);
2417
2.99k
        else if (!part_of_datetime && !is_value_terminator(*cp))
2418
9
          set_error_and_return_default("expected value-terminator, saw '"sv, to_sv(*cp), "'"sv);
2419
3.00k
      }
2420
3.03k
      uint32_t value = 0u;
2421
3.03k
      uint32_t place = 1u;
2422
14.7k
      for (auto i = impl::min<size_t>(digit_count, 9u); i-- > 0u;)
2423
11.7k
      {
2424
11.7k
        value += digits[i] * place;
2425
11.7k
        place *= 10u;
2426
11.7k
      }
2427
18.4k
      for (auto i = digit_count; i < 9u; i++) // implicit zeros
2428
15.3k
        value *= 10u;
2429
3.03k
      time.nanosecond = value;
2430
3.03k
      return time;
2431
3.06k
    }
2432
2433
    TOML_NODISCARD
2434
    TOML_NEVER_INLINE
2435
    date_time parse_date_time()
2436
4.83k
    {
2437
4.83k
      return_if_error({});
2438
4.83k
      assert_not_eof();
2439
4.83k
      TOML_ASSERT_ASSUME(is_decimal_digit(*cp));
2440
4.83k
      push_parse_scope("date-time"sv);
2441
2442
      // "YYYY-MM-DD"
2443
4.83k
      auto date = parse_date(true);
2444
4.83k
      set_error_and_return_if_eof({});
2445
2446
      // ' ', 'T' or 't'
2447
4.83k
      if (!is_match(*cp, U' ', U'T', U't'))
2448
4.83k
        set_error_and_return_default("expected space, 'T' or 't', saw '"sv, to_sv(*cp), "'"sv);
2449
4.82k
      advance_and_return_if_error_or_eof({});
2450
2451
      // "HH:MM:SS.FFFFFFFFF"
2452
4.82k
      auto time = parse_time(true);
2453
4.82k
      return_if_error({});
2454
2455
      // no offset
2456
4.82k
      if (is_eof() || is_value_terminator(*cp))
2457
1.02k
        return { date, time };
2458
2459
      // zero offset ('Z' or 'z')
2460
3.79k
      time_offset offset{};
2461
3.79k
      if (is_match(*cp, U'Z', U'z'))
2462
1.78k
        advance_and_return_if_error({});
2463
2464
      // explicit offset ("+/-HH:MM")
2465
2.00k
      else if (is_match(*cp, U'+', U'-'))
2466
1.88k
      {
2467
1.88k
        push_parse_scope("date-time offset"sv);
2468
2469
        // sign
2470
1.88k
        int sign = *cp == U'-' ? -1 : 1;
2471
1.88k
        advance_and_return_if_error_or_eof({});
2472
2473
        // "HH"
2474
1.87k
        int digits[2];
2475
1.87k
        if (!consume_digit_sequence(digits, 2u))
2476
1.87k
          set_error_and_return_default("expected 2-digit hour, saw '"sv, to_sv(cp), "'"sv);
2477
1.84k
        const auto hour = digits[1] + digits[0] * 10;
2478
1.84k
        if (hour > 23)
2479
1.84k
          set_error_and_return_default("expected hour between 0 and 23 (inclusive), saw "sv, hour);
2480
1.83k
        set_error_and_return_if_eof({});
2481
2482
        // ':'
2483
1.83k
        if (*cp != U':')
2484
1.83k
          set_error_and_return_default("expected ':', saw '"sv, to_sv(*cp), "'"sv);
2485
1.82k
        advance_and_return_if_error_or_eof({});
2486
2487
        // "MM"
2488
1.82k
        if (!consume_digit_sequence(digits, 2u))
2489
1.82k
          set_error_and_return_default("expected 2-digit minute, saw '"sv, to_sv(cp), "'"sv);
2490
1.79k
        const auto minute = digits[1] + digits[0] * 10;
2491
1.79k
        if (minute > 59)
2492
1.79k
          set_error_and_return_default("expected minute between 0 and 59 (inclusive), saw "sv, minute);
2493
1.79k
        offset.minutes = static_cast<decltype(offset.minutes)>((hour * 60 + minute) * sign);
2494
1.79k
      }
2495
2496
3.70k
      if (!is_eof() && !is_value_terminator(*cp))
2497
3.70k
        set_error_and_return_default("expected value-terminator, saw '"sv, to_sv(*cp), "'"sv);
2498
2499
3.68k
      return { date, time, offset };
2500
3.70k
    }
2501
2502
    TOML_NODISCARD
2503
    node_ptr parse_array();
2504
2505
    TOML_NODISCARD
2506
    node_ptr parse_inline_table();
2507
2508
    TOML_NODISCARD
2509
    node_ptr parse_value_known_prefixes()
2510
621k
    {
2511
621k
      return_if_error({});
2512
621k
      assert_not_eof();
2513
621k
      TOML_ASSERT_ASSUME(!is_control_character(*cp));
2514
621k
      TOML_ASSERT_ASSUME(*cp != U'_');
2515
2516
621k
      switch (cp->value)
2517
621k
      {
2518
        // arrays
2519
4.76k
        case U'[': return parse_array();
2520
2521
        // inline tables
2522
8.48k
        case U'{': return parse_inline_table();
2523
2524
        // floats beginning with '.'
2525
17
        case U'.': return node_ptr{ new value{ parse_float() } };
2526
2527
        // strings
2528
2.79k
        case U'"': [[fallthrough]];
2529
7.34k
        case U'\'': return node_ptr{ new value{ parse_string().value } };
2530
2531
600k
        default:
2532
600k
        {
2533
600k
          const auto cp_upper = static_cast<uint_least32_t>(cp->value) & ~0x20u;
2534
2535
          // bools
2536
600k
          if (cp_upper == 70u || cp_upper == 84u) // F or T
2537
747
            return node_ptr{ new value{ parse_boolean() } };
2538
2539
          // inf/nan
2540
600k
          else if (cp_upper == 73u || cp_upper == 78u) // I or N
2541
1.32k
            return node_ptr{ new value{ parse_inf_or_nan() } };
2542
2543
598k
          else
2544
598k
            return nullptr;
2545
600k
        }
2546
621k
      }
2547
621k
      TOML_UNREACHABLE;
2548
621k
    }
2549
2550
    TOML_NODISCARD
2551
    node_ptr parse_value()
2552
621k
    {
2553
621k
      return_if_error({});
2554
621k
      assert_not_eof();
2555
621k
      TOML_ASSERT_ASSUME(!is_value_terminator(*cp));
2556
621k
      push_parse_scope("value"sv);
2557
2558
621k
      const depth_counter_scope depth_counter{ nested_values };
2559
621k
      if TOML_UNLIKELY(nested_values > max_nested_values)
2560
621k
        set_error_and_return_default("exceeded maximum nested value depth of "sv,
2561
621k
                       max_nested_values,
2562
621k
                       " (TOML_MAX_NESTED_VALUES)"sv);
2563
2564
      // check if it begins with some control character
2565
      // (note that this will also fail for whitespace but we're assuming we've
2566
      // called consume_leading_whitespace() before calling parse_value())
2567
621k
      if TOML_UNLIKELY(is_control_character(*cp))
2568
621k
        set_error_and_return_default("unexpected control character"sv);
2569
2570
      // underscores at the beginning
2571
621k
      else if (*cp == U'_')
2572
1
        set_error_and_return_default("values may not begin with underscores"sv);
2573
2574
621k
      const auto begin_pos = cp->position;
2575
621k
      node_ptr val;
2576
2577
621k
      do
2578
621k
      {
2579
621k
        TOML_ASSERT_ASSUME(!is_control_character(*cp));
2580
621k
        TOML_ASSERT_ASSUME(*cp != U'_');
2581
2582
        // detect the value type and parse accordingly,
2583
        // starting with value types that can be detected
2584
        // unambiguously from just one character.
2585
2586
621k
        val = parse_value_known_prefixes();
2587
621k
        return_if_error({});
2588
621k
        if (val)
2589
16.3k
          break;
2590
2591
        // value types from here down require more than one character to unambiguously identify
2592
        // so scan ahead and collect a set of value 'traits'.
2593
605k
        enum TOML_CLOSED_FLAGS_ENUM value_traits : int
2594
605k
        {
2595
605k
          has_nothing  = 0,
2596
605k
          has_digits   = 1,
2597
605k
          has_b    = 1 << 1, // as second char only (0b)
2598
605k
          has_e    = 1 << 2, // only float exponents
2599
605k
          has_o    = 1 << 3, // as second char only (0o)
2600
605k
          has_p    = 1 << 4, // only hexfloat exponents
2601
605k
          has_t    = 1 << 5,
2602
605k
          has_x    = 1 << 6, // as second or third char only (0x, -0x, +0x)
2603
605k
          has_z    = 1 << 7,
2604
605k
          has_colon  = 1 << 8,
2605
605k
          has_plus   = 1 << 9,
2606
605k
          has_minus  = 1 << 10,
2607
605k
          has_dot    = 1 << 11,
2608
605k
          begins_sign  = 1 << 12,
2609
605k
          begins_digit = 1 << 13,
2610
605k
          begins_zero  = 1 << 14,
2611
2612
605k
          signs_msk  = has_plus | has_minus,
2613
605k
          bdigit_msk = has_digits | begins_digit,
2614
605k
          bzero_msk  = bdigit_msk | begins_zero,
2615
605k
        };
2616
605k
        value_traits traits  = has_nothing;
2617
793k
        const auto has_any   = [&](auto t) noexcept { return (traits & t) != has_nothing; };
auto toml::v3::impl::impl_ex::parser::parse_value()::{lambda(auto:1)#1}::operator()<toml::v3::impl::impl_ex::parser::parse_value()::value_traits>(toml::v3::impl::impl_ex::parser::parse_value()::value_traits) const
Line
Count
Source
2617
743k
        const auto has_any   = [&](auto t) noexcept { return (traits & t) != has_nothing; };
auto toml::v3::impl::impl_ex::parser::parse_value()::{lambda(auto:1)#1}::operator()<int>(int) const
Line
Count
Source
2617
50.1k
        const auto has_any   = [&](auto t) noexcept { return (traits & t) != has_nothing; };
2618
605k
        const auto has_none  = [&](auto t) noexcept { return (traits & t) == has_nothing; };
2619
1.70M
        const auto add_trait = [&](auto t) noexcept { traits = static_cast<value_traits>(traits | t); };
2620
2621
        // examine the first character to get the 'begins with' traits
2622
        // (good fail-fast opportunity; all the remaining types begin with numeric digits or signs)
2623
605k
        if (is_decimal_digit(*cp))
2624
586k
        {
2625
586k
          add_trait(begins_digit);
2626
586k
          if (*cp == U'0')
2627
16.9k
            add_trait(begins_zero);
2628
586k
        }
2629
18.7k
        else if (is_match(*cp, U'+', U'-'))
2630
12.3k
          add_trait(begins_sign);
2631
6.43k
        else
2632
6.43k
          break;
2633
2634
        // scan the rest of the value to determine the remaining traits
2635
598k
        char32_t chars[utf8_buffered_reader::max_history_length];
2636
598k
        size_t char_count = {}, advance_count = {};
2637
598k
        bool eof_while_scanning = false;
2638
598k
        const auto scan     = [&]() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS)
2639
601k
        {
2640
601k
          if (is_eof())
2641
1
            return;
2642
601k
          TOML_ASSERT_ASSUME(!is_value_terminator(*cp));
2643
2644
601k
          do
2645
1.12M
          {
2646
1.12M
            if (const auto c = **cp; c != U'_')
2647
1.09M
            {
2648
1.09M
              chars[char_count++] = c;
2649
2650
1.09M
              if (is_decimal_digit(c))
2651
1.02M
                add_trait(has_digits);
2652
71.0k
              else if (is_ascii_letter(c))
2653
16.6k
              {
2654
16.6k
                TOML_ASSERT_ASSUME((c >= U'a' && c <= U'z') || (c >= U'A' && c <= U'Z'));
2655
16.6k
                switch (static_cast<char32_t>(c | 32u))
2656
16.6k
                {
2657
1.86k
                  case U'b':
2658
1.86k
                    if (char_count == 2u && has_any(begins_zero))
2659
1.21k
                      add_trait(has_b);
2660
1.86k
                    break;
2661
2662
1.97k
                  case U'e':
2663
1.97k
                    if (char_count > 1u
2664
1.97k
                      && has_none(has_b | has_o | has_p | has_t | has_x | has_z | has_colon)
2665
1.67k
                      && (has_none(has_plus | has_minus) || has_any(begins_sign)))
2666
1.60k
                      add_trait(has_e);
2667
1.97k
                    break;
2668
2669
1.85k
                  case U'o':
2670
1.85k
                    if (char_count == 2u && has_any(begins_zero))
2671
1.75k
                      add_trait(has_o);
2672
1.85k
                    break;
2673
2674
150
                  case U'p':
2675
150
                    if (has_any(has_x))
2676
69
                      add_trait(has_p);
2677
150
                    break;
2678
2679
3.08k
                  case U'x':
2680
3.08k
                    if ((char_count == 2u && has_any(begins_zero))
2681
313
                      || (char_count == 3u && has_any(begins_sign) && chars[1] == U'0'))
2682
2.78k
                      add_trait(has_x);
2683
3.08k
                    break;
2684
2685
1.78k
                  case U't': add_trait(has_t); break;
2686
1.98k
                  case U'z': add_trait(has_z); break;
2687
16.6k
                }
2688
16.6k
              }
2689
54.3k
              else if (c <= U':')
2690
53.0k
              {
2691
53.0k
                TOML_ASSERT_ASSUME(c < U'0' || c > U'9');
2692
53.0k
                switch (c)
2693
53.0k
                {
2694
3.26k
                  case U'+': add_trait(has_plus); break;
2695
27.0k
                  case U'-': add_trait(has_minus); break;
2696
6.93k
                  case U'.': add_trait(has_dot); break;
2697
15.3k
                  case U':': add_trait(has_colon); break;
2698
53.0k
                }
2699
53.0k
              }
2700
1.09M
            }
2701
2702
1.12M
            advance_and_return_if_error();
2703
1.12M
            advance_count++;
2704
1.12M
            eof_while_scanning = is_eof();
2705
1.12M
          }
2706
1.12M
          while (advance_count < (utf8_buffered_reader::max_history_length - 1u) && !is_eof()
2707
1.12M
               && !is_value_terminator(*cp));
2708
601k
        };
2709
598k
        scan();
2710
598k
        return_if_error({});
2711
2712
        // force further scanning if this could have been a date-time with a space instead of a T
2713
598k
        if (char_count == 10u                   //
2714
7.98k
          && (traits | begins_zero) == (bzero_msk | has_minus) //
2715
5.71k
          && chars[4] == U'-'                   //
2716
5.70k
          && chars[7] == U'-'                   //
2717
5.69k
          && !is_eof()                     //
2718
5.68k
          && *cp == U' ')
2719
3.78k
        {
2720
3.78k
          const auto pre_advance_count = advance_count;
2721
3.78k
          const auto pre_scan_traits   = traits;
2722
3.78k
          chars[char_count++]      = *cp;
2723
3.78k
          add_trait(has_t);
2724
2725
3.78k
          const auto backpedal = [&]() noexcept
2726
3.78k
          {
2727
593
            go_back(advance_count - pre_advance_count);
2728
593
            advance_count = pre_advance_count;
2729
593
            traits      = pre_scan_traits;
2730
593
            char_count    = 10u;
2731
593
          };
2732
2733
3.78k
          advance_and_return_if_error({});
2734
3.78k
          advance_count++;
2735
2736
3.78k
          if (is_eof() || !is_decimal_digit(*cp))
2737
592
            backpedal();
2738
3.19k
          else
2739
3.19k
          {
2740
3.19k
            chars[char_count++] = *cp;
2741
2742
3.19k
            advance_and_return_if_error({});
2743
3.19k
            advance_count++;
2744
2745
3.19k
            scan();
2746
3.19k
            return_if_error({});
2747
2748
3.19k
            if (char_count == 12u)
2749
1
              backpedal();
2750
3.19k
          }
2751
3.78k
        }
2752
2753
        // set the reader back to where we started
2754
598k
        go_back(advance_count);
2755
2756
        // if after scanning ahead we still only have one value character,
2757
        // the only valid value type is an integer.
2758
598k
        if (char_count == 1u)
2759
548k
        {
2760
548k
          if (has_any(begins_digit))
2761
548k
          {
2762
548k
            val.reset(new value{ static_cast<int64_t>(chars[0] - U'0') });
2763
548k
            advance(); // skip the digit
2764
548k
            break;
2765
548k
          }
2766
2767
          // anything else would be ambiguous.
2768
8
          else
2769
548k
            set_error_and_return_default(eof_while_scanning ? "encountered end-of-file"sv
2770
548k
                                    : "could not determine value type"sv);
2771
548k
        }
2772
2773
        // now things that can be identified from two or more characters
2774
50.1k
        return_if_error({});
2775
50.1k
        TOML_ASSERT_ASSUME(char_count >= 2u);
2776
2777
        // do some 'fuzzy matching' where there's no ambiguity, since that allows the specific
2778
        // typed parse functions to take over and show better diagnostics if there's an issue
2779
        // (as opposed to the fallback "could not determine type" message)
2780
50.1k
        if (has_any(has_p))
2781
10
          val.reset(new value{ parse_hex_float() });
2782
50.1k
        else if (has_any(has_x | has_o | has_b))
2783
5.74k
        {
2784
5.74k
          int64_t i;
2785
5.74k
          value_flags flags;
2786
5.74k
          if (has_any(has_x))
2787
2.77k
          {
2788
2.77k
            i   = parse_integer<16>();
2789
2.77k
            flags = value_flags::format_as_hexadecimal;
2790
2.77k
          }
2791
2.96k
          else if (has_any(has_o))
2792
1.75k
          {
2793
1.75k
            i   = parse_integer<8>();
2794
1.75k
            flags = value_flags::format_as_octal;
2795
1.75k
          }
2796
1.21k
          else // has_b
2797
1.21k
          {
2798
1.21k
            i   = parse_integer<2>();
2799
1.21k
            flags = value_flags::format_as_binary;
2800
1.21k
          }
2801
5.74k
          return_if_error({});
2802
2803
5.74k
          val.reset(new value{ i });
2804
5.74k
          val->ref_cast<int64_t>().flags(flags);
2805
5.74k
        }
2806
44.3k
        else if (has_any(has_e) || (has_any(begins_digit) && chars[1] == U'.'))
2807
3.39k
          val.reset(new value{ parse_float() });
2808
40.9k
        else if (has_any(begins_sign))
2809
12.1k
        {
2810
          // single-digit signed integers
2811
12.1k
          if (char_count == 2u && has_any(has_digits))
2812
1.63k
          {
2813
1.63k
            val.reset(new value{ static_cast<int64_t>(chars[1] - U'0') * (chars[0] == U'-' ? -1LL : 1LL) });
2814
1.63k
            advance(); // skip the sign
2815
1.63k
            advance(); // skip the digit
2816
1.63k
            break;
2817
1.63k
          }
2818
2819
          // simple signed floats (e.g. +1.0)
2820
10.4k
          if (is_decimal_digit(chars[1]) && chars[2] == U'.')
2821
352
            val.reset(new value{ parse_float() });
2822
2823
          // signed infinity or nan
2824
10.1k
          else if (is_match(chars[1], U'i', U'n', U'I', U'N'))
2825
524
            val.reset(new value{ parse_inf_or_nan() });
2826
10.4k
        }
2827
2828
48.4k
        return_if_error({});
2829
48.4k
        if (val)
2830
9.51k
          break;
2831
2832
        // match trait masks against what they can match exclusively.
2833
        // all correct value parses will come out of this list, so doing this as a switch is likely to
2834
        // be a better friend to the optimizer on the success path (failure path can be slow but that
2835
        // doesn't matter much).
2836
38.9k
        switch (unwrap_enum(traits))
2837
38.9k
        {
2838
          // binary integers
2839
          // 0b10
2840
0
          case bzero_msk | has_b:
2841
0
            val.reset(new value{ parse_integer<2>() });
2842
0
            val->ref_cast<int64_t>().flags(value_flags::format_as_binary);
2843
0
            break;
2844
2845
          // octal integers
2846
          // 0o10
2847
0
          case bzero_msk | has_o:
2848
0
            val.reset(new value{ parse_integer<8>() });
2849
0
            val->ref_cast<int64_t>().flags(value_flags::format_as_octal);
2850
0
            break;
2851
2852
          // decimal integers
2853
          // 00
2854
          // 10
2855
          // +10
2856
          // -10
2857
41
          case bzero_msk: [[fallthrough]];
2858
18.5k
          case bdigit_msk: [[fallthrough]];
2859
27.1k
          case begins_sign | has_digits | has_minus: [[fallthrough]];
2860
27.4k
          case begins_sign | has_digits | has_plus:
2861
27.4k
          {
2862
            // if the value was so long we exhausted the history buffer it's reasonable to assume
2863
            // there was more and the value's actual type is impossible to identify without making the
2864
            // buffer bigger (since it could have actually been a float), so emit an error.
2865
            //
2866
            // (this will likely only come up during fuzzing and similar scenarios)
2867
27.4k
            static constexpr size_t max_numeric_value_length =
2868
27.4k
              utf8_buffered_reader::max_history_length - 2u;
2869
27.4k
            if TOML_UNLIKELY(!eof_while_scanning && advance_count > max_numeric_value_length)
2870
27.4k
              set_error_and_return_default("numeric value too long to identify type - cannot exceed "sv,
2871
27.4k
                             max_numeric_value_length,
2872
27.4k
                             " characters"sv);
2873
2874
27.4k
            val.reset(new value{ parse_integer<10>() });
2875
27.4k
            break;
2876
27.4k
          }
2877
2878
          // hexadecimal integers
2879
          // 0x10
2880
0
          case bzero_msk | has_x:
2881
0
            val.reset(new value{ parse_integer<16>() });
2882
0
            val->ref_cast<int64_t>().flags(value_flags::format_as_hexadecimal);
2883
0
            break;
2884
2885
          // decimal floats
2886
          // 0e1
2887
          // 0e-1
2888
          // 0e+1
2889
          // 0.0
2890
          // 0.0e1
2891
          // 0.0e-1
2892
          // 0.0e+1
2893
0
          case bzero_msk | has_e: [[fallthrough]];
2894
0
          case bzero_msk | has_e | has_minus: [[fallthrough]];
2895
0
          case bzero_msk | has_e | has_plus: [[fallthrough]];
2896
1
          case bzero_msk | has_dot: [[fallthrough]];
2897
1
          case bzero_msk | has_dot | has_e: [[fallthrough]];
2898
1
          case bzero_msk | has_dot | has_e | has_minus: [[fallthrough]];
2899
1
          case bzero_msk | has_dot | has_e | has_plus: [[fallthrough]];
2900
          // 1e1
2901
          // 1e-1
2902
          // 1e+1
2903
          // 1.0
2904
          // 1.0e1
2905
          // 1.0e-1
2906
          // 1.0e+1
2907
1
          case bdigit_msk | has_e: [[fallthrough]];
2908
1
          case bdigit_msk | has_e | has_minus: [[fallthrough]];
2909
1
          case bdigit_msk | has_e | has_plus: [[fallthrough]];
2910
817
          case bdigit_msk | has_dot: [[fallthrough]];
2911
817
          case bdigit_msk | has_dot | has_e: [[fallthrough]];
2912
817
          case bdigit_msk | has_dot | has_e | has_minus: [[fallthrough]];
2913
817
          case bdigit_msk | has_dot | has_e | has_plus: [[fallthrough]];
2914
          // +1e1
2915
          // +1.0
2916
          // +1.0e1
2917
          // +1.0e+1
2918
          // +1.0e-1
2919
          // -1.0e+1
2920
817
          case begins_sign | has_digits | has_e | has_plus: [[fallthrough]];
2921
1.02k
          case begins_sign | has_digits | has_dot | has_plus: [[fallthrough]];
2922
1.02k
          case begins_sign | has_digits | has_dot | has_e | has_plus: [[fallthrough]];
2923
1.02k
          case begins_sign | has_digits | has_dot | has_e | signs_msk: [[fallthrough]];
2924
          // -1e1
2925
          // -1e+1
2926
          // +1e-1
2927
          // -1.0
2928
          // -1.0e1
2929
          // -1.0e-1
2930
1.02k
          case begins_sign | has_digits | has_e | has_minus: [[fallthrough]];
2931
1.02k
          case begins_sign | has_digits | has_e | signs_msk: [[fallthrough]];
2932
1.38k
          case begins_sign | has_digits | has_dot | has_minus: [[fallthrough]];
2933
1.38k
          case begins_sign | has_digits | has_dot | has_e | has_minus:
2934
1.38k
            val.reset(new value{ parse_float() });
2935
1.38k
            break;
2936
2937
          // hexadecimal floats
2938
          // 0x10p0
2939
          // 0x10p-0
2940
          // 0x10p+0
2941
0
          case bzero_msk | has_x | has_p: [[fallthrough]];
2942
0
          case bzero_msk | has_x | has_p | has_minus: [[fallthrough]];
2943
0
          case bzero_msk | has_x | has_p | has_plus: [[fallthrough]];
2944
          // -0x10p0
2945
          // -0x10p-0
2946
          // +0x10p0
2947
          // +0x10p+0
2948
          // -0x10p+0
2949
          // +0x10p-0
2950
0
          case begins_sign | has_digits | has_x | has_p | has_minus: [[fallthrough]];
2951
0
          case begins_sign | has_digits | has_x | has_p | has_plus: [[fallthrough]];
2952
0
          case begins_sign | has_digits | has_x | has_p | signs_msk: [[fallthrough]];
2953
          // 0x10.1p0
2954
          // 0x10.1p-0
2955
          // 0x10.1p+0
2956
0
          case bzero_msk | has_x | has_dot | has_p: [[fallthrough]];
2957
0
          case bzero_msk | has_x | has_dot | has_p | has_minus: [[fallthrough]];
2958
0
          case bzero_msk | has_x | has_dot | has_p | has_plus: [[fallthrough]];
2959
          // -0x10.1p0
2960
          // -0x10.1p-0
2961
          // +0x10.1p0
2962
          // +0x10.1p+0
2963
          // -0x10.1p+0
2964
          // +0x10.1p-0
2965
0
          case begins_sign | has_digits | has_x | has_dot | has_p | has_minus: [[fallthrough]];
2966
0
          case begins_sign | has_digits | has_x | has_dot | has_p | has_plus: [[fallthrough]];
2967
0
          case begins_sign | has_digits | has_x | has_dot | has_p | signs_msk:
2968
0
            val.reset(new value{ parse_hex_float() });
2969
0
            break;
2970
2971
          // times
2972
          // HH:MM
2973
          // HH:MM:SS
2974
          // HH:MM:SS.FFFFFF
2975
331
          case bzero_msk | has_colon: [[fallthrough]];
2976
1.01k
          case bzero_msk | has_colon | has_dot: [[fallthrough]];
2977
1.45k
          case bdigit_msk | has_colon: [[fallthrough]];
2978
1.92k
          case bdigit_msk | has_colon | has_dot: val.reset(new value{ parse_time() }); break;
2979
2980
          // local dates
2981
          // YYYY-MM-DD
2982
992
          case bzero_msk | has_minus: [[fallthrough]];
2983
2.69k
          case bdigit_msk | has_minus: val.reset(new value{ parse_date() }); break;
2984
2985
          // date-times
2986
          // YYYY-MM-DDTHH:MM
2987
          // YYYY-MM-DDTHH:MM-HH:MM
2988
          // YYYY-MM-DDTHH:MM+HH:MM
2989
          // YYYY-MM-DD HH:MM
2990
          // YYYY-MM-DD HH:MM-HH:MM
2991
          // YYYY-MM-DD HH:MM+HH:MM
2992
          // YYYY-MM-DDTHH:MM:SS
2993
          // YYYY-MM-DDTHH:MM:SS-HH:MM
2994
          // YYYY-MM-DDTHH:MM:SS+HH:MM
2995
          // YYYY-MM-DD HH:MM:SS
2996
          // YYYY-MM-DD HH:MM:SS-HH:MM
2997
          // YYYY-MM-DD HH:MM:SS+HH:MM
2998
332
          case bzero_msk | has_minus | has_colon | has_t: [[fallthrough]];
2999
699
          case bzero_msk | signs_msk | has_colon | has_t: [[fallthrough]];
3000
1.50k
          case bdigit_msk | has_minus | has_colon | has_t: [[fallthrough]];
3001
1.79k
          case bdigit_msk | signs_msk | has_colon | has_t: [[fallthrough]];
3002
          // YYYY-MM-DDTHH:MM:SS.FFFFFF
3003
          // YYYY-MM-DDTHH:MM:SS.FFFFFF-HH:MM
3004
          // YYYY-MM-DDTHH:MM:SS.FFFFFF+HH:MM
3005
          // YYYY-MM-DD HH:MM:SS.FFFFFF
3006
          // YYYY-MM-DD HH:MM:SS.FFFFFF-HH:MM
3007
          // YYYY-MM-DD HH:MM:SS.FFFFFF+HH:MM
3008
2.18k
          case bzero_msk | has_minus | has_colon | has_dot | has_t: [[fallthrough]];
3009
2.44k
          case bzero_msk | signs_msk | has_colon | has_dot | has_t: [[fallthrough]];
3010
2.69k
          case bdigit_msk | has_minus | has_colon | has_dot | has_t: [[fallthrough]];
3011
3.01k
          case bdigit_msk | signs_msk | has_colon | has_dot | has_t: [[fallthrough]];
3012
          // YYYY-MM-DDTHH:MMZ
3013
          // YYYY-MM-DD HH:MMZ
3014
          // YYYY-MM-DDTHH:MM:SSZ
3015
          // YYYY-MM-DD HH:MM:SSZ
3016
          // YYYY-MM-DDTHH:MM:SS.FFFFFFZ
3017
          // YYYY-MM-DD HH:MM:SS.FFFFFFZ
3018
3.45k
          case bzero_msk | has_minus | has_colon | has_z | has_t: [[fallthrough]];
3019
3.98k
          case bzero_msk | has_minus | has_colon | has_dot | has_z | has_t: [[fallthrough]];
3020
4.62k
          case bdigit_msk | has_minus | has_colon | has_z | has_t: [[fallthrough]];
3021
4.83k
          case bdigit_msk | has_minus | has_colon | has_dot | has_z | has_t:
3022
4.83k
            val.reset(new value{ parse_date_time() });
3023
4.83k
            break;
3024
38.9k
        }
3025
38.9k
      }
3026
621k
      while (false);
3027
3028
620k
      if (!val)
3029
297
      {
3030
297
        set_error_at(begin_pos, "could not determine value type"sv);
3031
297
        return_after_error({});
3032
297
      }
3033
3034
619k
      val->source_ = { begin_pos, current_position(1), reader.source_path() };
3035
619k
      return val;
3036
620k
    }
3037
3038
    TOML_NEVER_INLINE
3039
    bool parse_key()
3040
205k
    {
3041
205k
      return_if_error({});
3042
205k
      assert_not_eof();
3043
205k
      TOML_ASSERT_ASSUME(is_bare_key_character(*cp) || is_string_delimiter(*cp));
3044
205k
      push_parse_scope("key"sv);
3045
3046
205k
      key_buffer.clear();
3047
205k
      recording_whitespace = false;
3048
3049
1.43M
      while (!is_error())
3050
1.43M
      {
3051
1.43M
        std::string_view key_segment;
3052
1.43M
        const auto key_begin = current_position();
3053
3054
        // bare_key_segment
3055
1.43M
        if (is_bare_key_character(*cp))
3056
1.43M
          key_segment = parse_bare_key_segment();
3057
3058
        // "quoted key segment"
3059
4.52k
        else if (is_string_delimiter(*cp))
3060
4.47k
        {
3061
4.47k
          const auto begin_pos = cp->position;
3062
3063
4.47k
          recording_whitespace = true;
3064
4.47k
          parsed_string str  = parse_string();
3065
4.47k
          recording_whitespace = false;
3066
4.47k
          return_if_error({});
3067
3068
4.47k
          if (str.was_multi_line)
3069
18
          {
3070
18
            set_error_at(begin_pos,
3071
18
                   "multi-line strings are prohibited in "sv,
3072
18
                   key_buffer.empty() ? ""sv : "dotted "sv,
3073
18
                   "keys"sv);
3074
18
            return_after_error({});
3075
18
          }
3076
4.45k
          else
3077
4.45k
            key_segment = str.value;
3078
4.47k
        }
3079
3080
        // ???
3081
45
        else
3082
45
          set_error_and_return_default("expected bare key starting character or string delimiter, saw '"sv,
3083
1.43M
                         to_sv(*cp),
3084
1.43M
                         "'"sv);
3085
3086
1.43M
        const auto key_end = current_position();
3087
3088
        // whitespace following the key segment
3089
1.43M
        consume_leading_whitespace();
3090
3091
        // store segment
3092
1.43M
        key_buffer.push_back(key_segment, key_begin, key_end);
3093
3094
1.43M
        if TOML_UNLIKELY(key_buffer.size() > max_dotted_keys_depth)
3095
1.43M
          set_error_and_return_default("exceeded maximum dotted keys depth of "sv,
3096
1.43M
                         max_dotted_keys_depth,
3097
1.43M
                         " (TOML_MAX_DOTTED_KEYS_DEPTH)"sv);
3098
3099
        // eof or no more key to come
3100
1.43M
        if (is_eof() || *cp != U'.')
3101
204k
          break;
3102
3103
        // was a dotted key - go around again
3104
1.23M
        advance_and_return_if_error_or_eof({});
3105
1.23M
        consume_leading_whitespace();
3106
1.23M
        set_error_and_return_if_eof({});
3107
1.23M
      }
3108
205k
      return_if_error({});
3109
3110
205k
      return true;
3111
205k
    }
3112
3113
    TOML_NODISCARD
3114
    key make_key(size_t segment_index) const
3115
1.13M
    {
3116
1.13M
      TOML_ASSERT(key_buffer.size() > segment_index);
3117
3118
1.13M
      return key{
3119
1.13M
        key_buffer[segment_index],
3120
1.13M
        source_region{ key_buffer.starts[segment_index], key_buffer.ends[segment_index], root.source().path }
3121
1.13M
      };
3122
1.13M
    }
3123
3124
    TOML_NODISCARD
3125
    TOML_NEVER_INLINE
3126
    table* parse_table_header()
3127
170k
    {
3128
170k
      return_if_error({});
3129
170k
      assert_not_eof();
3130
170k
      TOML_ASSERT_ASSUME(*cp == U'[');
3131
170k
      push_parse_scope("table header"sv);
3132
3133
170k
      const source_position header_begin_pos = cp->position;
3134
170k
      source_position header_end_pos;
3135
170k
      bool is_arr = false;
3136
3137
      // parse header
3138
170k
      {
3139
        // skip first '['
3140
170k
        advance_and_return_if_error_or_eof({});
3141
3142
        // skip past any whitespace that followed the '['
3143
170k
        const bool had_leading_whitespace = consume_leading_whitespace();
3144
170k
        set_error_and_return_if_eof({});
3145
3146
        // skip second '[' (if present)
3147
170k
        if (*cp == U'[')
3148
152k
        {
3149
152k
          if (had_leading_whitespace)
3150
152k
            set_error_and_return_default(
3151
152k
              "[[array-of-table]] brackets must be contiguous (i.e. [ [ this ] ] is prohibited)"sv);
3152
3153
152k
          is_arr = true;
3154
152k
          advance_and_return_if_error_or_eof({});
3155
3156
          // skip past any whitespace that followed the '['
3157
152k
          consume_leading_whitespace();
3158
152k
          set_error_and_return_if_eof({});
3159
152k
        }
3160
3161
        // check for a premature closing ']'
3162
170k
        if (*cp == U']')
3163
170k
          set_error_and_return_default("tables with blank bare keys are explicitly prohibited"sv);
3164
3165
170k
        if (!is_bare_key_character(*cp) && !is_string_delimiter(*cp))
3166
170k
          set_error_and_return_default("expected bare key starting character or string delimiter, saw '"sv,
3167
170k
                         to_sv(*cp),
3168
170k
                         "'"sv);
3169
3170
        // get the actual key
3171
170k
        start_recording();
3172
170k
        parse_key();
3173
170k
        stop_recording(1u);
3174
170k
        return_if_error({});
3175
3176
        // skip past any whitespace that followed the key
3177
170k
        consume_leading_whitespace();
3178
170k
        return_if_error({});
3179
170k
        set_error_and_return_if_eof({});
3180
3181
        // consume the closing ']'
3182
170k
        if (*cp != U']')
3183
170k
          set_error_and_return_default("expected ']', saw '"sv, to_sv(*cp), "'"sv);
3184
170k
        if (is_arr)
3185
152k
        {
3186
152k
          advance_and_return_if_error_or_eof({});
3187
152k
          if (*cp != U']')
3188
152k
            set_error_and_return_default("expected ']', saw '"sv, to_sv(*cp), "'"sv);
3189
152k
        }
3190
170k
        advance_and_return_if_error({});
3191
170k
        header_end_pos = current_position(1);
3192
3193
        // handle the rest of the line after the header
3194
170k
        consume_leading_whitespace();
3195
170k
        if (!is_eof() && !consume_comment() && !consume_line_break())
3196
170k
          set_error_and_return_default("expected a comment or whitespace, saw '"sv, to_sv(cp), "'"sv);
3197
170k
      }
3198
169k
      TOML_ASSERT(!key_buffer.empty());
3199
3200
      // check if each parent is a table/table array, or can be created implicitly as a table.
3201
169k
      table* parent = &root;
3202
473k
      for (size_t i = 0, e = key_buffer.size() - 1u; i < e; i++)
3203
303k
      {
3204
303k
        const std::string_view segment = key_buffer[i];
3205
303k
        auto pit             = parent->lower_bound(segment);
3206
3207
        // parent already existed
3208
303k
        if (pit != parent->end() && pit->first == segment)
3209
258k
        {
3210
258k
          node& p = pit->second;
3211
3212
258k
          if (auto tbl = p.as_table())
3213
111k
          {
3214
            // adding to closed inline tables is illegal
3215
111k
            if (tbl->is_inline() && !impl::find(open_inline_tables.begin(), open_inline_tables.end(), tbl))
3216
111k
              set_error_and_return_default("cannot insert '"sv,
3217
111k
                             to_sv(recording_buffer),
3218
111k
                             "' into existing inline table"sv);
3219
3220
111k
            parent = tbl;
3221
111k
          }
3222
146k
          else if (auto arr = p.as_array(); arr && impl::find(table_arrays.begin(), table_arrays.end(), arr))
3223
146k
          {
3224
            // table arrays are a special case;
3225
            // the spec dictates we select the most recently declared element in the array.
3226
146k
            TOML_ASSERT(!arr->empty());
3227
146k
            TOML_ASSERT(arr->back().is_table());
3228
146k
            parent = &arr->back().ref_cast<table>();
3229
146k
          }
3230
10
          else
3231
10
          {
3232
10
            if (!is_arr && p.type() == node_type::table)
3233
10
              set_error_and_return_default("cannot redefine existing table '"sv,
3234
10
                             to_sv(recording_buffer),
3235
10
                             "'"sv);
3236
10
            else
3237
10
              set_error_and_return_default("cannot redefine existing "sv,
3238
10
                             to_sv(p.type()),
3239
10
                             " '"sv,
3240
10
                             to_sv(recording_buffer),
3241
10
                             "' as "sv,
3242
10
                             is_arr ? "array-of-tables"sv : "table"sv);
3243
10
          }
3244
258k
        }
3245
3246
        // need to create a new implicit table
3247
45.1k
        else
3248
45.1k
        {
3249
45.1k
          pit     = parent->emplace_hint<table>(pit, make_key(i));
3250
45.1k
          table& p  = pit->second.ref_cast<table>();
3251
45.1k
          p.source_ = { header_begin_pos, header_end_pos, reader.source_path() };
3252
3253
45.1k
          implicit_tables.push_back(&p);
3254
45.1k
          parent = &p;
3255
45.1k
        }
3256
303k
      }
3257
3258
169k
      const auto last_segment = key_buffer.back();
3259
169k
      auto it         = parent->lower_bound(last_segment);
3260
3261
      // if there was already a matching node some sanity checking is necessary;
3262
      // this is ok if we're making an array and the existing element is already an array (new element)
3263
      // or if we're making a table and the existing element is an implicitly-created table (promote it),
3264
      // otherwise this is a redefinition error.
3265
169k
      if (it != parent->end() && it->first == last_segment)
3266
30.1k
      {
3267
30.1k
        node& matching_node = it->second;
3268
30.1k
        if (auto arr = matching_node.as_array();
3269
30.1k
          is_arr && arr && impl::find(table_arrays.begin(), table_arrays.end(), arr))
3270
18.0k
        {
3271
18.0k
          table& tbl  = arr->emplace_back<table>();
3272
18.0k
          tbl.source_ = { header_begin_pos, header_end_pos, reader.source_path() };
3273
18.0k
          return &tbl;
3274
18.0k
        }
3275
3276
12.1k
        else if (auto tbl = matching_node.as_table(); !is_arr && tbl && !implicit_tables.empty())
3277
12.1k
        {
3278
12.1k
          if (auto found = impl::find(implicit_tables.begin(), implicit_tables.end(), tbl); found)
3279
12.1k
          {
3280
12.1k
            bool ok = true;
3281
12.1k
            if (!tbl->empty())
3282
12.1k
            {
3283
12.1k
              for (auto& [_, child] : *tbl)
3284
120k
              {
3285
120k
                if (!child.is_table() && !child.is_array_of_tables())
3286
10
                {
3287
10
                  ok = false;
3288
10
                  break;
3289
10
                }
3290
120k
              }
3291
12.1k
            }
3292
3293
12.1k
            if (ok)
3294
12.0k
            {
3295
12.0k
              implicit_tables.erase(implicit_tables.cbegin() + (found - implicit_tables.data()));
3296
12.0k
              tbl->source_.begin = header_begin_pos;
3297
12.0k
              tbl->source_.end   = header_end_pos;
3298
12.0k
              return tbl;
3299
12.0k
            }
3300
12.1k
          }
3301
12.1k
        }
3302
3303
        // if we get here it's a redefinition error.
3304
37
        if (!is_arr && matching_node.type() == node_type::table)
3305
24
        {
3306
24
          set_error_at(header_begin_pos,
3307
24
                 "cannot redefine existing table '"sv,
3308
24
                 to_sv(recording_buffer),
3309
24
                 "'"sv);
3310
24
          return_after_error({});
3311
24
        }
3312
13
        else
3313
13
        {
3314
13
          set_error_at(header_begin_pos,
3315
13
                 "cannot redefine existing "sv,
3316
13
                 to_sv(matching_node.type()),
3317
13
                 " '"sv,
3318
13
                 to_sv(recording_buffer),
3319
13
                 "' as "sv,
3320
13
                 is_arr ? "array-of-tables"sv : "table"sv);
3321
13
          return_after_error({});
3322
13
        }
3323
37
      }
3324
3325
      // there was no matching node, sweet - we can freely instantiate a new table/table array.
3326
139k
      else
3327
139k
      {
3328
139k
        auto last_key = make_key(key_buffer.size() - 1u);
3329
3330
        // if it's an array we need to make the array and it's first table element,
3331
        // set the starting regions, and return the table element
3332
139k
        if (is_arr)
3333
134k
        {
3334
134k
          it         = parent->emplace_hint<array>(it, std::move(last_key));
3335
134k
          array& tbl_arr = it->second.ref_cast<array>();
3336
134k
          table_arrays.push_back(&tbl_arr);
3337
134k
          tbl_arr.source_ = { header_begin_pos, header_end_pos, reader.source_path() };
3338
3339
134k
          table& tbl  = tbl_arr.emplace_back<table>();
3340
134k
          tbl.source_ = { header_begin_pos, header_end_pos, reader.source_path() };
3341
134k
          return &tbl;
3342
134k
        }
3343
3344
        // otherwise we're just making a table
3345
5.46k
        else
3346
5.46k
        {
3347
5.46k
          it      = parent->emplace_hint<table>(it, std::move(last_key));
3348
5.46k
          table& tbl  = it->second.ref_cast<table>();
3349
5.46k
          tbl.source_ = { header_begin_pos, header_end_pos, reader.source_path() };
3350
5.46k
          return &tbl;
3351
5.46k
        }
3352
139k
      }
3353
169k
    }
3354
3355
    TOML_NEVER_INLINE
3356
    bool parse_key_value_pair_and_insert(table* tbl)
3357
35.4k
    {
3358
35.4k
      return_if_error({});
3359
35.4k
      assert_not_eof();
3360
35.4k
      TOML_ASSERT_ASSUME(is_string_delimiter(*cp) || is_bare_key_character(*cp));
3361
35.4k
      push_parse_scope("key-value pair"sv);
3362
3363
      // read the key into the key buffer
3364
35.4k
      start_recording();
3365
35.4k
      parse_key();
3366
35.4k
      stop_recording(1u);
3367
35.4k
      return_if_error({});
3368
35.4k
      TOML_ASSERT(key_buffer.size() >= 1u);
3369
3370
      // skip past any whitespace that followed the key
3371
35.4k
      consume_leading_whitespace();
3372
35.4k
      set_error_and_return_if_eof({});
3373
3374
      // '='
3375
35.2k
      if (*cp != U'=')
3376
35.2k
        set_error_and_return_default("expected '=', saw '"sv, to_sv(*cp), "'"sv);
3377
35.0k
      advance_and_return_if_error_or_eof({});
3378
3379
      // skip past any whitespace that followed the '='
3380
35.0k
      consume_leading_whitespace();
3381
35.0k
      return_if_error({});
3382
35.0k
      set_error_and_return_if_eof({});
3383
3384
      // check that the next character could actually be a value
3385
35.0k
      if (is_value_terminator(*cp))
3386
35.0k
        set_error_and_return_default("expected value, saw '"sv, to_sv(*cp), "'"sv);
3387
3388
      // if it's a dotted kvp we need to spawn the parent sub-tables if necessary,
3389
      // and set the target table to the second-to-last one in the chain
3390
35.0k
      if (key_buffer.size() > 1u)
3391
4.19k
      {
3392
924k
        for (size_t i = 0; i < key_buffer.size() - 1u; i++)
3393
919k
        {
3394
919k
          const std::string_view segment = key_buffer[i];
3395
919k
          auto pit             = tbl->lower_bound(segment);
3396
3397
          // parent already existed
3398
919k
          if (pit != tbl->end() && pit->first == segment)
3399
1.28k
          {
3400
1.28k
            table* p = pit->second.as_table();
3401
3402
            // redefinition
3403
1.28k
            if TOML_UNLIKELY(!p
3404
1.28k
              || !(impl::find(dotted_key_tables.begin(), dotted_key_tables.end(), p)
3405
1.28k
                 || impl::find(implicit_tables.begin(), implicit_tables.end(), p)))
3406
11
            {
3407
11
              set_error_at(key_buffer.starts[i],
3408
11
                     "cannot redefine existing "sv,
3409
11
                     to_sv(pit->second.type()),
3410
11
                     " as dotted key-value pair"sv);
3411
11
              return_after_error({});
3412
11
            }
3413
3414
1.27k
            tbl = p;
3415
1.27k
          }
3416
3417
          // need to create a new implicit table
3418
918k
          else
3419
918k
          {
3420
918k
            pit     = tbl->emplace_hint<table>(pit, make_key(i));
3421
918k
            table& p  = pit->second.ref_cast<table>();
3422
918k
            p.source_ = pit->first.source();
3423
3424
918k
            dotted_key_tables.push_back(&p);
3425
918k
            tbl = &p;
3426
918k
          }
3427
919k
        }
3428
4.19k
      }
3429
3430
      // ensure this isn't a redefinition
3431
35.0k
      const std::string_view last_segment = key_buffer.back();
3432
35.0k
      auto it               = tbl->lower_bound(last_segment);
3433
35.0k
      if (it != tbl->end() && it->first == last_segment)
3434
11
      {
3435
11
        set_error("cannot redefine existing "sv,
3436
11
              to_sv(it->second.type()),
3437
11
              " '"sv,
3438
11
              to_sv(recording_buffer),
3439
11
              "'"sv);
3440
11
        return_after_error({});
3441
11
      }
3442
3443
      // create the key first since the key buffer will likely get overwritten during value parsing (inline
3444
      // tables)
3445
35.0k
      auto last_key = make_key(key_buffer.size() - 1u);
3446
3447
      // now we can actually parse the value
3448
35.0k
      node_ptr val = parse_value();
3449
35.0k
      return_if_error({});
3450
3451
35.0k
      tbl->emplace_hint<node_ptr>(it, std::move(last_key), std::move(val));
3452
35.0k
      return true;
3453
35.0k
    }
3454
3455
    void parse_document()
3456
7.75k
    {
3457
7.75k
      assert_not_error();
3458
7.75k
      assert_not_eof();
3459
7.75k
      push_parse_scope("root table"sv);
3460
3461
7.75k
      table* current_table = &root;
3462
3463
7.75k
      do
3464
200k
      {
3465
200k
        return_if_error();
3466
3467
        // leading whitespace, line endings, comments
3468
200k
        if (consume_leading_whitespace() || consume_line_break() || consume_comment())
3469
2.75k
          continue;
3470
197k
        return_if_error();
3471
3472
        // [tables]
3473
        // [[table array]]
3474
197k
        if (*cp == U'[')
3475
170k
          current_table = parse_table_header();
3476
3477
        // bare_keys
3478
        // dotted.keys
3479
        // "quoted keys"
3480
27.2k
        else if (is_bare_key_character(*cp) || is_string_delimiter(*cp))
3481
27.0k
        {
3482
27.0k
          push_parse_scope("key-value pair"sv);
3483
3484
27.0k
          parse_key_value_pair_and_insert(current_table);
3485
3486
          // handle the rest of the line after the kvp
3487
          // (this is not done in parse_key_value_pair() because that is also used for inline tables)
3488
27.0k
          consume_leading_whitespace();
3489
27.0k
          return_if_error();
3490
27.0k
          if (!is_eof() && !consume_comment() && !consume_line_break())
3491
53
            set_error("expected a comment or whitespace, saw '"sv, to_sv(cp), "'"sv);
3492
27.0k
        }
3493
3494
236
        else // ??
3495
236
          set_error("expected keys, tables, whitespace or comments, saw '"sv, to_sv(cp), "'"sv);
3496
197k
      }
3497
200k
      while (!is_eof());
3498
3499
7.75k
      auto eof_pos   = current_position(1);
3500
7.75k
      root.source_.end = eof_pos;
3501
7.75k
      if (current_table && current_table != &root && current_table->source_.end <= current_table->source_.begin)
3502
0
        current_table->source_.end = eof_pos;
3503
7.75k
    }
3504
3505
    static void update_region_ends(node& nde) noexcept
3506
1.40M
    {
3507
1.40M
      const auto type = nde.type();
3508
1.40M
      if (type > node_type::array)
3509
594k
        return;
3510
3511
812k
      if (type == node_type::table)
3512
680k
      {
3513
680k
        auto& tbl = nde.ref_cast<table>();
3514
680k
        if (tbl.is_inline()) // inline tables (and all their inline descendants) are already correctly
3515
                   // terminated
3516
587
          return;
3517
3518
679k
        auto end = nde.source_.end;
3519
679k
        for (auto&& [k, v] : tbl)
3520
681k
        {
3521
681k
          TOML_UNUSED(k);
3522
681k
          update_region_ends(v);
3523
681k
          if (end < v.source_.end)
3524
510k
            end = v.source_.end;
3525
681k
        }
3526
679k
      }
3527
131k
      else // arrays
3528
131k
      {
3529
131k
        auto& arr = nde.ref_cast<array>();
3530
131k
        auto end  = nde.source_.end;
3531
131k
        for (auto&& v : arr)
3532
721k
        {
3533
721k
          update_region_ends(v);
3534
721k
          if (end < v.source_.end)
3535
17.5k
            end = v.source_.end;
3536
721k
        }
3537
131k
        nde.source_.end = end;
3538
131k
      }
3539
812k
    }
3540
3541
    public:
3542
    parser(utf8_reader_interface&& reader_) //
3543
7.88k
      : reader{ reader_ }
3544
7.88k
    {
3545
7.88k
      root.source_ = { prev_pos, prev_pos, reader.source_path() };
3546
3547
7.88k
      if (!reader.peek_eof())
3548
7.84k
      {
3549
7.84k
        cp = reader.read_next();
3550
3551
#if !TOML_EXCEPTIONS
3552
        if (reader.error())
3553
        {
3554
          err = std::move(reader.error());
3555
          return;
3556
        }
3557
#endif
3558
3559
7.84k
        if (cp)
3560
7.75k
          parse_document();
3561
7.84k
      }
3562
3563
7.88k
      update_region_ends(root);
3564
7.88k
    }
3565
3566
    TOML_NODISCARD
3567
    operator parse_result() && noexcept
3568
3.69k
    {
3569
3.69k
#if TOML_EXCEPTIONS
3570
3571
3.69k
      return { std::move(root) };
3572
3573
#else
3574
3575
      if (err)
3576
        return parse_result{ *std::move(err) };
3577
      else
3578
        return parse_result{ std::move(root) };
3579
3580
#endif
3581
3.69k
    }
3582
  };
3583
3584
  TOML_EXTERNAL_LINKAGE
3585
  node_ptr parser::parse_array()
3586
4.76k
  {
3587
4.76k
    return_if_error({});
3588
4.76k
    assert_not_eof();
3589
4.76k
    TOML_ASSERT_ASSUME(*cp == U'[');
3590
4.76k
    push_parse_scope("array"sv);
3591
3592
    // skip opening '['
3593
4.76k
    advance_and_return_if_error_or_eof({});
3594
3595
4.74k
    node_ptr arr_ptr{ new array{} };
3596
4.74k
    array& arr = arr_ptr->ref_cast<array>();
3597
4.74k
    enum class TOML_CLOSED_ENUM parse_type : int
3598
4.74k
    {
3599
4.74k
      none,
3600
4.74k
      comma,
3601
4.74k
      val
3602
4.74k
    };
3603
4.74k
    parse_type prev = parse_type::none;
3604
3605
1.17M
    while (!is_error())
3606
1.17M
    {
3607
1.30M
      while (consume_leading_whitespace() || consume_line_break() || consume_comment())
3608
135k
        continue;
3609
1.17M
      set_error_and_return_if_eof({});
3610
3611
      // commas - only legal after a value
3612
1.17M
      if (*cp == U',')
3613
582k
      {
3614
582k
        if (prev == parse_type::val)
3615
582k
        {
3616
582k
          prev = parse_type::comma;
3617
582k
          advance_and_return_if_error_or_eof({});
3618
582k
          continue;
3619
582k
        }
3620
1
        set_error_and_return_default("expected value or closing ']', saw comma"sv);
3621
1
      }
3622
3623
      // closing ']'
3624
589k
      else if (*cp == U']')
3625
2.17k
      {
3626
2.17k
        advance_and_return_if_error({});
3627
2.17k
        break;
3628
2.17k
      }
3629
3630
      // must be a value
3631
587k
      else
3632
587k
      {
3633
587k
        if (prev == parse_type::val)
3634
21
        {
3635
21
          set_error_and_return_default("expected comma or closing ']', saw '"sv, to_sv(*cp), "'"sv);
3636
0
          continue;
3637
21
        }
3638
587k
        prev = parse_type::val;
3639
3640
587k
        auto val = parse_value();
3641
587k
        return_if_error({});
3642
3643
587k
        if (!arr.capacity())
3644
2.38k
          arr.reserve(4u);
3645
587k
        arr.emplace_back<node_ptr>(std::move(val));
3646
587k
      }
3647
1.17M
    }
3648
3649
4.56k
    return_if_error({});
3650
4.56k
    return arr_ptr;
3651
4.74k
  }
3652
3653
  TOML_EXTERNAL_LINKAGE
3654
  node_ptr parser::parse_inline_table()
3655
8.48k
  {
3656
8.48k
    return_if_error({});
3657
8.48k
    assert_not_eof();
3658
8.48k
    TOML_ASSERT_ASSUME(*cp == U'{');
3659
8.48k
    push_parse_scope("inline table"sv);
3660
3661
    // skip opening '{'
3662
8.48k
    advance_and_return_if_error_or_eof({});
3663
3664
8.47k
    node_ptr tbl_ptr{ new table{} };
3665
8.47k
    table& tbl = tbl_ptr->ref_cast<table>();
3666
8.47k
    tbl.is_inline(true);
3667
8.47k
    table_vector_scope table_scope{ open_inline_tables, tbl };
3668
3669
8.47k
    enum class TOML_CLOSED_ENUM parse_type : int
3670
8.47k
    {
3671
8.47k
      none,
3672
8.47k
      comma,
3673
8.47k
      kvp
3674
8.47k
    };
3675
8.47k
    parse_type prev = parse_type::none;
3676
17.3k
    while (!is_error())
3677
13.9k
    {
3678
      if constexpr (TOML_LANG_UNRELEASED) // toml/issues/516 (newlines/trailing commas in inline tables)
3679
      {
3680
        while (consume_leading_whitespace() || consume_line_break() || consume_comment())
3681
          continue;
3682
      }
3683
      else
3684
13.9k
      {
3685
14.2k
        while (consume_leading_whitespace())
3686
281
          continue;
3687
13.9k
      }
3688
13.9k
      return_if_error({});
3689
13.9k
      set_error_and_return_if_eof({});
3690
3691
      // commas - only legal after a key-value pair
3692
13.9k
      if (*cp == U',')
3693
488
      {
3694
488
        if (prev == parse_type::kvp)
3695
487
        {
3696
487
          prev = parse_type::comma;
3697
487
          advance_and_return_if_error_or_eof({});
3698
487
        }
3699
1
        else
3700
488
          set_error_and_return_default("expected key-value pair or closing '}', saw comma"sv);
3701
488
      }
3702
3703
      // closing '}'
3704
13.4k
      else if (*cp == U'}')
3705
5.03k
      {
3706
        if constexpr (!TOML_LANG_UNRELEASED) // toml/issues/516 (newlines/trailing commas in inline tables)
3707
5.03k
        {
3708
5.03k
          if (prev == parse_type::comma)
3709
0
          {
3710
0
            set_error_and_return_default("expected key-value pair, saw closing '}' (dangling comma)"sv);
3711
0
            continue;
3712
0
          }
3713
5.03k
        }
3714
5.03k
        advance_and_return_if_error({});
3715
5.03k
        break;
3716
5.03k
      }
3717
3718
      // key-value pair
3719
8.41k
      else if (is_string_delimiter(*cp) || is_bare_key_character(*cp))
3720
8.35k
      {
3721
8.35k
        if (prev == parse_type::kvp)
3722
8.35k
          set_error_and_return_default("expected comma or closing '}', saw '"sv, to_sv(*cp), "'"sv);
3723
8.35k
        else
3724
8.35k
        {
3725
8.35k
          prev = parse_type::kvp;
3726
8.35k
          parse_key_value_pair_and_insert(&tbl);
3727
8.35k
        }
3728
8.35k
      }
3729
3730
      /// ???
3731
60
      else
3732
60
        set_error_and_return_default("expected key or closing '}', saw '"sv, to_sv(*cp), "'"sv);
3733
13.9k
    }
3734
3735
8.40k
    return_if_error({});
3736
8.40k
    return tbl_ptr;
3737
8.47k
  }
3738
3739
  TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS
3740
}
3741
TOML_IMPL_NAMESPACE_END;
3742
3743
#undef TOML_RETURNS_BY_THROWING
3744
#undef advance_and_return_if_error
3745
#undef advance_and_return_if_error_or_eof
3746
#undef assert_not_eof
3747
#undef assert_not_error
3748
#undef is_eof
3749
#undef is_error
3750
#undef parse_error_break
3751
#undef push_parse_scope
3752
#undef push_parse_scope_1
3753
#undef push_parse_scope_2
3754
#undef return_after_error
3755
#undef return_if_eof
3756
#undef return_if_error
3757
#undef return_if_error_or_eof
3758
#undef set_error_and_return
3759
#undef set_error_and_return_default
3760
#undef set_error_and_return_if_eof
3761
#undef utf8_buffered_reader_error_check
3762
#undef utf8_reader_error
3763
#undef utf8_reader_error_check
3764
#undef utf8_reader_return_after_error
3765
3766
//#---------------------------------------------------------------------------------------------------------------------
3767
//# PARSER PUBLIC IMPLEMENTATION
3768
//#---------------------------------------------------------------------------------------------------------------------
3769
3770
TOML_ANON_NAMESPACE_START
3771
{
3772
  TOML_NODISCARD
3773
  TOML_INTERNAL_LINKAGE
3774
  parse_result do_parse(utf8_reader_interface && reader)
3775
7.88k
  {
3776
7.88k
    return impl::parser{ std::move(reader) };
3777
7.88k
  }
3778
3779
  TOML_NODISCARD
3780
  TOML_INTERNAL_LINKAGE
3781
  parse_result do_parse_file(std::string_view file_path)
3782
0
  {
3783
0
#if TOML_EXCEPTIONS
3784
0
#define TOML_PARSE_FILE_ERROR(msg, path)                                                                               \
3785
0
  throw parse_error(msg, source_position{}, std::make_shared<const std::string>(std::move(path)))
3786
0
#else
3787
0
#define TOML_PARSE_FILE_ERROR(msg, path)                                                                               \
3788
0
  return parse_result(parse_error(msg, source_position{}, std::make_shared<const std::string>(std::move(path))))
3789
0
#endif
3790
0
3791
0
    std::string file_path_str(file_path);
3792
0
3793
0
    // open file with a custom-sized stack buffer
3794
0
    std::ifstream file;
3795
0
    TOML_OVERALIGNED char file_buffer[sizeof(void*) * 1024u];
3796
0
    file.rdbuf()->pubsetbuf(file_buffer, sizeof(file_buffer));
3797
0
#if TOML_WINDOWS && !(defined(__MINGW32__) || defined(__MINGW64__))
3798
0
    file.open(impl::widen(file_path_str).c_str(), std::ifstream::in | std::ifstream::binary | std::ifstream::ate);
3799
0
#else
3800
0
    file.open(file_path_str, std::ifstream::in | std::ifstream::binary | std::ifstream::ate);
3801
0
#endif
3802
0
    if (!file.is_open())
3803
0
      TOML_PARSE_FILE_ERROR("File could not be opened for reading", file_path_str);
3804
0
3805
0
    // get size
3806
0
    const auto file_size = file.tellg();
3807
0
    if (file_size == -1)
3808
0
      TOML_PARSE_FILE_ERROR("Could not determine file size", file_path_str);
3809
0
    file.seekg(0, std::ifstream::beg);
3810
0
3811
0
    // read the whole file into memory first if the file isn't too large
3812
0
    constexpr auto large_file_threshold = 1024 * 1024 * 2; // 2 MB
3813
0
    if (file_size <= large_file_threshold)
3814
0
    {
3815
0
      std::vector<char> file_data;
3816
0
      file_data.resize(static_cast<size_t>(file_size));
3817
0
      file.read(file_data.data(), static_cast<std::streamsize>(file_size));
3818
0
      return parse(std::string_view{ file_data.data(), file_data.size() }, std::move(file_path_str));
3819
0
    }
3820
0
3821
0
    // otherwise parse it using the streams
3822
0
    else
3823
0
      return parse(file, std::move(file_path_str));
3824
0
3825
0
#undef TOML_PARSE_FILE_ERROR
3826
0
  }
3827
}
3828
TOML_ANON_NAMESPACE_END;
3829
3830
TOML_NAMESPACE_START
3831
{
3832
  TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex);
3833
3834
  TOML_EXTERNAL_LINKAGE
3835
  parse_result TOML_CALLCONV parse(std::string_view doc, std::string_view source_path)
3836
7.88k
  {
3837
7.88k
    return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, source_path });
3838
7.88k
  }
3839
3840
  TOML_EXTERNAL_LINKAGE
3841
  parse_result TOML_CALLCONV parse(std::string_view doc, std::string && source_path)
3842
0
  {
3843
0
    return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, std::move(source_path) });
3844
0
  }
3845
3846
  TOML_EXTERNAL_LINKAGE
3847
  parse_result TOML_CALLCONV parse(std::istream & doc, std::string_view source_path)
3848
0
  {
3849
0
    return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, source_path });
3850
0
  }
3851
3852
  TOML_EXTERNAL_LINKAGE
3853
  parse_result TOML_CALLCONV parse(std::istream & doc, std::string && source_path)
3854
0
  {
3855
0
    return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, std::move(source_path) });
3856
0
  }
3857
3858
  TOML_EXTERNAL_LINKAGE
3859
  parse_result TOML_CALLCONV parse_file(std::string_view file_path)
3860
0
  {
3861
0
    return TOML_ANON_NAMESPACE::do_parse_file(file_path);
3862
0
  }
3863
3864
#if TOML_HAS_CHAR8
3865
3866
  TOML_EXTERNAL_LINKAGE
3867
  parse_result TOML_CALLCONV parse(std::u8string_view doc, std::string_view source_path)
3868
  {
3869
    return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, source_path });
3870
  }
3871
3872
  TOML_EXTERNAL_LINKAGE
3873
  parse_result TOML_CALLCONV parse(std::u8string_view doc, std::string && source_path)
3874
  {
3875
    return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, std::move(source_path) });
3876
  }
3877
3878
  TOML_EXTERNAL_LINKAGE
3879
  parse_result TOML_CALLCONV parse_file(std::u8string_view file_path)
3880
  {
3881
    std::string file_path_str;
3882
    file_path_str.resize(file_path.length());
3883
    memcpy(file_path_str.data(), file_path.data(), file_path.length());
3884
    return TOML_ANON_NAMESPACE::do_parse_file(file_path_str);
3885
  }
3886
3887
#endif // TOML_HAS_CHAR8
3888
3889
#if TOML_ENABLE_WINDOWS_COMPAT
3890
3891
  TOML_EXTERNAL_LINKAGE
3892
  parse_result TOML_CALLCONV parse(std::string_view doc, std::wstring_view source_path)
3893
  {
3894
    return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, impl::narrow(source_path) });
3895
  }
3896
3897
  TOML_EXTERNAL_LINKAGE
3898
  parse_result TOML_CALLCONV parse(std::istream & doc, std::wstring_view source_path)
3899
  {
3900
    return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, impl::narrow(source_path) });
3901
  }
3902
3903
  TOML_EXTERNAL_LINKAGE
3904
  parse_result TOML_CALLCONV parse_file(std::wstring_view file_path)
3905
  {
3906
    return TOML_ANON_NAMESPACE::do_parse_file(impl::narrow(file_path));
3907
  }
3908
3909
#endif // TOML_ENABLE_WINDOWS_COMPAT
3910
3911
#if TOML_HAS_CHAR8 && TOML_ENABLE_WINDOWS_COMPAT
3912
3913
  TOML_EXTERNAL_LINKAGE
3914
  parse_result TOML_CALLCONV parse(std::u8string_view doc, std::wstring_view source_path)
3915
  {
3916
    return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, impl::narrow(source_path) });
3917
  }
3918
3919
#endif // TOML_HAS_CHAR8 && TOML_ENABLE_WINDOWS_COMPAT
3920
3921
  TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS
3922
}
3923
TOML_NAMESPACE_END;
3924
3925
#undef TOML_OVERALIGNED
3926
#include "header_end.hpp"
3927
#endif // TOML_ENABLE_PARSER