• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tstack / lnav / 18478704608-2574

13 Oct 2025 09:28PM UTC coverage: 70.045% (-0.07%) from 70.119%
18478704608-2574

push

github

tstack
[md4c] add HTML generator

A bunch of external-access stuff.

69 of 167 new or added lines in 18 files covered. (41.32%)

1 existing line in 1 file now uncovered.

50324 of 71845 relevant lines covered (70.05%)

415329.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.15
/src/base/intern_string.hh
1
/**
2
 * Copyright (c) 2014, Timothy Stack
3
 *
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *
9
 * * Redistributions of source code must retain the above copyright notice, this
10
 * list of conditions and the following disclaimer.
11
 * * Redistributions in binary form must reproduce the above copyright notice,
12
 * this list of conditions and the following disclaimer in the documentation
13
 * and/or other materials provided with the distribution.
14
 * * Neither the name of Timothy Stack nor the names of its contributors
15
 * may be used to endorse or promote products derived from this software
16
 * without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 *
29
 * @file intern_string.hh
30
 */
31

32
#ifndef intern_string_hh
33
#define intern_string_hh
34

35
#include <optional>
36
#include <ostream>
37
#include <string>
38
#include <string_view>
39
#include <tuple>
40
#include <vector>
41

42
#include <assert.h>
43
#include <string.h>
44
#include <sys/types.h>
45

46
#include "fmt/format.h"
47
#include "mapbox/variant.hpp"
48
#include "result.h"
49
#include "strnatcmp.h"
50

51
unsigned long hash_str(const char* str, size_t len);
52

53
struct string_fragment {
54
    using iterator = const char*;
55

56
    static constexpr string_fragment invalid()
186,265✔
57
    {
58
        string_fragment retval;
186,265✔
59

60
        retval.invalidate();
186,265✔
61
        return retval;
186,265✔
62
    }
63

64
    static string_fragment from_string_view(std::string_view str)
65
    {
66
        return string_fragment{str.data(), 0, (int) str.size()};
67
    }
68

69
    static string_fragment from_c_str(const char* str)
170,435✔
70
    {
71
        return string_fragment{str, 0, str != nullptr ? (int) strlen(str) : 0};
170,435✔
72
    }
73

74
    static string_fragment from_c_str(const unsigned char* str)
146✔
75
    {
76
        return string_fragment{
292✔
77
            str, 0, str != nullptr ? (int) strlen((char*) str) : 0};
292✔
78
    }
79

80
    template<typename T, std::size_t N>
81
    static constexpr string_fragment from_const(const T (&str)[N])
193,731✔
82
    {
83
        return string_fragment{str, 0, (int) N - 1};
193,731✔
84
    }
85

86
    static string_fragment from_str(const std::string& str)
287,403✔
87
    {
88
        return string_fragment{str.c_str(), 0, (int) str.size()};
287,403✔
89
    }
90

91
    static string_fragment from_substr(const std::string& str,
92
                                       size_t offset,
93
                                       size_t length)
94
    {
95
        return string_fragment{
96
            str.c_str(), (int) offset, (int) (offset + length)};
97
    }
98

99
    static string_fragment from_str_range(const std::string& str,
143,998✔
100
                                          size_t begin,
101
                                          size_t end)
102
    {
103
        return string_fragment{str.c_str(), (int) begin, (int) end};
143,998✔
104
    }
105

106
    static string_fragment from_bytes(const char* bytes, size_t len)
12,721,498✔
107
    {
108
        return string_fragment{bytes, 0, (int) len};
12,721,498✔
109
    }
110

111
    static string_fragment from_bytes(const unsigned char* bytes, size_t len)
6,069,749✔
112
    {
113
        return string_fragment{(const char*) bytes, 0, (int) len};
6,069,749✔
114
    }
115

116
    static string_fragment from_memory_buffer(const fmt::memory_buffer& buf)
977✔
117
    {
118
        return string_fragment{buf.data(), 0, (int) buf.size()};
977✔
119
    }
120

121
    static string_fragment from_byte_range(const char* bytes,
161,708,082✔
122
                                           size_t begin,
123
                                           size_t end)
124
    {
125
        return string_fragment{bytes, (int) begin, (int) end};
161,708,082✔
126
    }
127

128
    constexpr string_fragment() : sf_string(nullptr), sf_begin(0), sf_end(0) {}
7,651,873✔
129

130
    explicit constexpr string_fragment(const char* str,
370,418,739✔
131
                                       int begin = 0,
132
                                       int end = -1)
133
        : sf_string(str), sf_begin(begin),
370,418,739✔
134
          sf_end(end == -1
370,418,739✔
135
                     ? static_cast<int>(std::string::traits_type::length(str))
370,418,739✔
136
                     : end)
137
    {
138
    }
370,418,739✔
139

140
    explicit string_fragment(const unsigned char* str,
489✔
141
                             int begin = 0,
142
                             int end = -1)
143
        : sf_string((const char*) str), sf_begin(begin),
489✔
144
          sf_end(end == -1 ? strlen((const char*) str) : end)
489✔
145
    {
146
    }
489✔
147

148
    string_fragment(const std::string& str)
258,400,845✔
149
        : sf_string(str.c_str()), sf_begin(0), sf_end(str.length())
258,400,845✔
150
    {
151
    }
258,400,845✔
152

153
    constexpr bool is_valid() const
146,415,223✔
154
    {
155
        return this->sf_begin != -1 && this->sf_begin <= this->sf_end;
146,415,223✔
156
    }
157

158
    constexpr int length() const { return this->sf_end - this->sf_begin; }
894,077,286✔
159

160
    Result<ssize_t, const char*> utf8_length() const;
161

162
    size_t column_to_byte_index(size_t col) const;
163

164
    size_t byte_to_column_index(size_t byte_index) const;
165

166
    std::tuple<int, int> byte_to_column_index(size_t byte_start,
×
167
                                              size_t byte_end) const
168
    {
169
        return {
170
            this->byte_to_column_index(byte_start),
×
171
            this->byte_to_column_index(byte_end),
×
172
        };
173
    }
174

175
    size_t column_width() const;
176

177
    constexpr const char* data() const
38,153,233✔
178
    {
179
        return &this->sf_string[this->sf_begin];
38,153,233✔
180
    }
181

182
    const unsigned char* udata() const
249,609,831✔
183
    {
184
        return (const unsigned char*) &this->sf_string[this->sf_begin];
249,609,831✔
185
    }
186

187
    char* writable_data(int offset = 0)
2,663✔
188
    {
189
        return (char*) &this->sf_string[this->sf_begin + offset];
2,663✔
190
    }
191

192
    constexpr char front() const { return this->sf_string[this->sf_begin]; }
137,534✔
193

194
    uint32_t front_codepoint() const;
195

196
    constexpr char back() const { return this->sf_string[this->sf_end - 1]; }
2,868✔
197

198
    constexpr void pop_back()
437✔
199
    {
200
        if (!this->empty()) {
437✔
201
            this->sf_end -= 1;
437✔
202
        }
203
    }
437✔
204

205
    iterator begin() const { return &this->sf_string[this->sf_begin]; }
46,599,579✔
206

207
    iterator end() const { return &this->sf_string[this->sf_end]; }
46,772,657✔
208

209
    constexpr bool empty() const { return !this->is_valid() || length() == 0; }
138,411,910✔
210

211
    Result<ssize_t, const char*> codepoint_to_byte_index(
212
        ssize_t cp_index) const;
213

214
    string_fragment sub_cell_range(int cell_start, int cell_end) const;
215

216
    constexpr const char& operator[](size_t index) const
2,424,544✔
217
    {
218
        return this->sf_string[sf_begin + index];
2,424,544✔
219
    }
220

221
    bool operator==(const std::string& str) const
9,728✔
222
    {
223
        if (this->length() != (int) str.length()) {
9,728✔
224
            return false;
7,888✔
225
        }
226

227
        return memcmp(
1,840✔
228
                   &this->sf_string[this->sf_begin], str.c_str(), str.length())
1,840✔
229
            == 0;
1,840✔
230
    }
231

232
    bool operator==(const string_fragment& sf) const
1,596,738✔
233
    {
234
        if (this->length() != sf.length()) {
1,596,738✔
235
            return false;
794,648✔
236
        }
237

238
        return memcmp(this->data(), sf.data(), sf.length()) == 0;
802,090✔
239
    }
240

241
    bool operator!=(const string_fragment& rhs) const
365✔
242
    {
243
        return !(*this == rhs);
365✔
244
    }
245

246
    bool operator<(const string_fragment& rhs) const
2,067,028✔
247
    {
248
        auto rc = strncmp(
2,067,028✔
249
            this->data(), rhs.data(), std::min(this->length(), rhs.length()));
2,067,028✔
250
        if (rc < 0 || (rc == 0 && this->length() < rhs.length())) {
2,067,028✔
251
            return true;
1,217,452✔
252
        }
253

254
        return false;
849,576✔
255
    }
256

257
    bool iequal(const string_fragment& sf) const
234,899,061✔
258
    {
259
        if (this->length() != sf.length()) {
234,899,061✔
260
            return false;
234,136,661✔
261
        }
262

263
        return strnatcasecmp(
762,400✔
264
                   this->length(), this->data(), sf.length(), sf.data())
265
            == 0;
762,400✔
266
    }
267

268
    template<std::size_t N>
269
    bool operator==(const char (&str)[N]) const
2,482,562✔
270
    {
271
        return (N - 1) == (size_t) this->length()
2,482,562✔
272
            && strncmp(this->data(), str, N - 1) == 0;
2,482,562✔
273
    }
274

275
    bool operator!=(const char* str) const { return !(*this == str); }
4,044✔
276

277
    template<typename... Args>
278
    bool is_one_of(Args... args) const
8,148✔
279
    {
280
        return (this->operator==(args) || ...);
9,482✔
281
    }
282

283
    bool startswith(const char* prefix) const
35,220,760✔
284
    {
285
        const auto* iter = this->begin();
35,220,760✔
286

287
        while (*prefix != '\0' && iter < this->end() && *prefix == *iter) {
65,193,097✔
288
            prefix += 1;
29,972,337✔
289
            iter += 1;
29,972,337✔
290
        }
291

292
        return *prefix == '\0';
35,220,760✔
293
    }
294

295
    bool endswith(const char* suffix) const
128,278✔
296
    {
297
        int suffix_len = strlen(suffix);
128,278✔
298

299
        if (suffix_len > this->length()) {
128,278✔
300
            return false;
36✔
301
        }
302

303
        const auto* curr = this->end() - suffix_len;
128,242✔
304
        while (*suffix != '\0' && *curr == *suffix) {
130,933✔
305
            suffix += 1;
2,691✔
306
            curr += 1;
2,691✔
307
        }
308

309
        return *suffix == '\0';
128,242✔
310
    }
311

312
    constexpr string_fragment substr(int begin) const
3,447,145✔
313
    {
314
        return string_fragment{
6,894,290✔
315
            this->sf_string, this->sf_begin + begin, this->sf_end};
3,447,145✔
316
    }
317

318
    string_fragment sub_range(int begin, int end) const
181,578,440✔
319
    {
320
        if (this->sf_begin + begin > this->sf_end) {
181,578,440✔
321
            begin = this->sf_end - this->sf_begin;
×
322
        }
323
        if (this->sf_begin + end > this->sf_end) {
181,578,440✔
324
            end = this->sf_end - this->sf_begin;
3,016✔
325
        }
326
        return string_fragment{
363,156,880✔
327
            this->sf_string, this->sf_begin + begin, this->sf_begin + end};
181,578,440✔
328
    }
329

330
    constexpr bool contains(const string_fragment& sf) const
×
331
    {
332
        return this->sf_string == sf.sf_string && this->sf_begin <= sf.sf_begin
×
333
            && sf.sf_end <= this->sf_end;
×
334
    }
335

336
    constexpr size_t count(char ch) const
172✔
337
    {
338
        size_t retval = 0;
172✔
339

340
        for (int lpc = this->sf_begin; lpc < this->sf_end; lpc++) {
19,278✔
341
            if (this->sf_string[lpc] == ch) {
19,106✔
342
                retval += 1;
463✔
343
            }
344
        }
345

346
        return retval;
172✔
347
    }
348

349
    std::optional<int> find(char ch) const
11,204✔
350
    {
351
        for (int lpc = this->sf_begin; lpc < this->sf_end; lpc++) {
522,895✔
352
            if (this->sf_string[lpc] == ch) {
515,958✔
353
                return lpc - this->sf_begin;
4,267✔
354
            }
355
        }
356

357
        return std::nullopt;
6,937✔
358
    }
359

360
    std::optional<int> rfind(char ch) const;
361

362
    std::optional<int> next_word(int start_col) const;
363
    std::optional<int> prev_word(int start_col) const;
364

365
    template<typename P>
366
    string_fragment find_left_boundary(size_t start,
11,570✔
367
                                       P&& predicate,
368
                                       size_t count = 1) const
369
    {
370
        assert((int) start <= this->length());
371

372
        if (this->empty()) {
11,570✔
373
            return *this;
×
374
        }
375

376
        if (start > 0 && start == static_cast<size_t>(this->length())) {
11,570✔
377
            start -= 1;
42✔
378
        }
379
        while (true) {
380
            if (predicate(this->data()[start])) {
68,147✔
381
                count -= 1;
11,477✔
382
                if (count == 0) {
11,477✔
383
                    start += 1;
11,473✔
384
                    break;
11,473✔
385
                }
386
            } else if (start == 0) {
56,670✔
387
                break;
97✔
388
            }
389
            start -= 1;
56,577✔
390
        }
391

392
        return string_fragment{
393
            this->sf_string,
11,570✔
394
            this->sf_begin + (int) start,
11,570✔
395
            this->sf_end,
11,570✔
396
        };
11,570✔
397
    }
398

399
    template<typename P>
400
    string_fragment find_right_boundary(size_t start,
22✔
401
                                        P&& predicate,
402
                                        size_t count = 1) const
403
    {
404
        while ((int) start < this->length()) {
195✔
405
            if (predicate(this->data()[start])) {
181✔
406
                count -= 1;
12✔
407
                if (count == 0) {
12✔
408
                    break;
8✔
409
                }
410
            }
411
            start += 1;
173✔
412
        }
413

414
        return string_fragment{
415
            this->sf_string,
22✔
416
            this->sf_begin,
22✔
417
            this->sf_begin + (int) start,
22✔
418
        };
22✔
419
    }
420

421
    template<typename P>
422
    string_fragment find_boundaries_around(size_t start,
11✔
423
                                           P&& predicate,
424
                                           size_t count = 1) const
425
    {
426
        auto left = this->find_left_boundary(start, predicate, count);
11✔
427

428
        return left.find_right_boundary(
11✔
429
            start - left.sf_begin, predicate, count);
22✔
430
    }
431

432
    std::optional<std::pair<uint32_t, string_fragment>> consume_codepoint()
3,817,401✔
433
        const
434
    {
435
        auto cp = this->front_codepoint();
3,817,401✔
436
        auto index_res = this->codepoint_to_byte_index(1);
3,817,401✔
437

438
        if (index_res.isErr()) {
3,817,401✔
439
            return std::nullopt;
404,164✔
440
        }
441

442
        return std::make_pair(cp, this->substr(index_res.unwrap()));
3,413,237✔
443
    }
3,817,401✔
444

445
    template<typename P>
446
    std::optional<string_fragment> consume(P predicate) const
14,748✔
447
    {
448
        int consumed = 0;
14,748✔
449
        while (consumed < this->length()) {
19,120✔
450
            if (!predicate(this->data()[consumed])) {
8,715✔
451
                break;
4,343✔
452
            }
453

454
            consumed += 1;
4,372✔
455
        }
456

457
        if (consumed == 0) {
14,748✔
458
            return std::nullopt;
10,388✔
459
        }
460

461
        return string_fragment{
462
            this->sf_string,
4,360✔
463
            this->sf_begin + consumed,
4,360✔
464
            this->sf_end,
4,360✔
465
        };
4,360✔
466
    }
467

468
    std::optional<string_fragment> consume_n(int amount) const;
469

470
    template<typename P>
471
    string_fragment skip(P predicate) const
17,912✔
472
    {
473
        int offset = 0;
17,912✔
474
        while (offset < this->length() && predicate(this->data()[offset])) {
32,362✔
475
            offset += 1;
14,450✔
476
        }
477

478
        return string_fragment{
479
            this->sf_string,
17,912✔
480
            this->sf_begin + offset,
17,912✔
481
            this->sf_end,
17,912✔
482
        };
17,912✔
483
    }
484

485
    using split_result
486
        = std::optional<std::pair<string_fragment, string_fragment>>;
487

488
    template<typename P>
489
    split_result split_while(P&& predicate) const
18,375✔
490
    {
491
        int consumed = 0;
18,375✔
492
        while (consumed < this->length()) {
2,854,794✔
493
            if (!predicate(this->data()[consumed])) {
2,844,252✔
494
                break;
7,833✔
495
            }
496

497
            consumed += 1;
2,836,419✔
498
        }
499

500
        if (consumed == 0) {
18,375✔
501
            return std::nullopt;
344✔
502
        }
503

504
        return std::make_pair(
36,062✔
505
            string_fragment{
506
                this->sf_string,
18,031✔
507
                this->sf_begin,
18,031✔
508
                this->sf_begin + consumed,
18,031✔
509
            },
510
            string_fragment{
511
                this->sf_string,
18,031✔
512
                this->sf_begin + consumed,
18,031✔
513
                this->sf_end,
18,031✔
514
            });
18,031✔
515
    }
516

517
    using split_when_result = std::pair<string_fragment, string_fragment>;
518

519
    template<typename P>
520
    split_when_result split_when(P&& predicate) const
13,556✔
521
    {
522
        int consumed = 0;
13,556✔
523
        while (consumed < this->length()) {
289,437✔
524
            if (predicate(this->data()[consumed])) {
282,284✔
525
                break;
6,403✔
526
            }
527

528
            consumed += 1;
275,881✔
529
        }
530

531
        return std::make_pair(
27,112✔
532
            string_fragment{
533
                this->sf_string,
13,556✔
534
                this->sf_begin,
13,556✔
535
                this->sf_begin + consumed,
13,556✔
536
            },
537
            string_fragment{
538
                this->sf_string,
13,556✔
539
                this->sf_begin + consumed
13,556✔
540
                    + ((consumed == this->length()) ? 0 : 1),
13,556✔
541
                this->sf_end,
13,556✔
542
            });
27,112✔
543
    }
544

545
    template<typename P>
546
    split_result split_pair(P&& predicate) const
5,510✔
547
    {
548
        int consumed = 0;
5,510✔
549
        while (consumed < this->length()) {
81,958✔
550
            if (predicate(this->data()[consumed])) {
81,958✔
551
                break;
5,510✔
552
            }
553

554
            consumed += 1;
76,448✔
555
        }
556

557
        if (consumed == this->length()) {
5,510✔
558
            return std::nullopt;
×
559
        }
560

561
        return std::make_pair(
11,020✔
562
            string_fragment{
563
                this->sf_string,
5,510✔
564
                this->sf_begin,
5,510✔
565
                this->sf_begin + consumed,
5,510✔
566
            },
567
            string_fragment{
568
                this->sf_string,
5,510✔
569
                this->sf_begin + consumed + 1,
5,510✔
570
                this->sf_end,
5,510✔
571
            });
5,510✔
572
    }
573

574
    template<typename P>
575
    split_result rsplit_pair(P&& predicate) const
1,850,326✔
576
    {
577
        if (this->empty()) {
1,850,326✔
578
            return std::nullopt;
×
579
        }
580

581
        auto curr = this->sf_end - 1;
1,850,326✔
582
        while (curr >= this->sf_begin) {
18,999,863✔
583
            if (predicate(this->sf_string[curr])) {
18,999,863✔
584
                return std::make_pair(
3,700,652✔
585
                    string_fragment{
586
                        this->sf_string,
1,850,326✔
587
                        this->sf_begin,
1,850,326✔
588
                        this->sf_begin + curr,
1,850,326✔
589
                    },
590
                    string_fragment{
591
                        this->sf_string,
1,850,326✔
592
                        this->sf_begin + curr + 1,
1,850,326✔
593
                        this->sf_end,
1,850,326✔
594
                    });
1,850,326✔
595
            }
596

597
            curr -= 1;
17,149,537✔
598
        }
599

600
        return std::nullopt;
×
601
    }
602

603
    split_result split_n(int amount) const;
604

605
    std::vector<string_fragment> split_lines() const;
606

607
    struct tag1 {
608
        const char t_value;
609

610
        constexpr explicit tag1(const char value) : t_value(value) {}
1,889,944✔
611

612
        constexpr bool operator()(char ch) const { return this->t_value == ch; }
19,427,368✔
613
    };
614

615
    struct quoted_string_body {
616
        bool qs_in_escape{false};
617

618
        bool operator()(char ch)
6,928✔
619
        {
620
            if (this->qs_in_escape) {
6,928✔
621
                this->qs_in_escape = false;
1✔
622
                return true;
1✔
623
            }
624
            if (ch == '\\') {
6,927✔
625
                this->qs_in_escape = true;
1✔
626
                return true;
1✔
627
            }
628
            if (ch == '"') {
6,926✔
629
                return false;
376✔
630
            }
631
            return true;
6,550✔
632
        }
633
    };
634

635
    const char* to_string(char* buf) const
636
    {
637
        memcpy(buf, this->data(), this->length());
638
        buf[this->length()] = '\0';
639

640
        return buf;
641
    }
642

643
    std::string to_string() const
10,667,969✔
644
    {
645
        if (!this->is_valid()) {
10,667,969✔
646
            return "<invalid>";
×
647
        }
648

649
        return {this->data(), (size_t) this->length()};
32,003,907✔
650
    }
651

652
    template<typename OutputIt>
653
    void to_hex_string(OutputIt out) const
654
    {
655
        for (const auto ch : *this) {
656
            fmt::format_to(out, FMT_STRING("{:02x}"), ch);
657
        }
658
    }
659

660
    std::string to_unquoted_string() const;
661

662
    void clear()
30,112✔
663
    {
664
        this->sf_begin = 0;
30,112✔
665
        this->sf_end = 0;
30,112✔
666
    }
30,112✔
667

668
    constexpr void invalidate()
255,189✔
669
    {
670
        this->sf_begin = -1;
255,189✔
671
        this->sf_end = -1;
255,189✔
672
    }
255,189✔
673

674
    string_fragment trim(const char* tokens) const;
675
    string_fragment rtrim(const char* tokens) const;
676
    string_fragment trim() const;
677

678
    string_fragment prepend(const char* str, int amount) const
7,352✔
679
    {
680
        return string_fragment{
14,704✔
681
            str,
682
            this->sf_begin + amount,
7,352✔
683
            this->sf_end + amount,
7,352✔
684
        };
7,352✔
685
    }
686

687
    string_fragment erase_before(const char* str, int amount) const
2,270✔
688
    {
689
        return string_fragment{
4,540✔
690
            str,
691
            this->sf_begin - amount,
2,270✔
692
            this->sf_end - amount,
2,270✔
693
        };
2,270✔
694
    }
695

696
    string_fragment erase(const char* str, int amount) const
697
    {
698
        return string_fragment{
699
            str,
700
            this->sf_begin,
701
            this->sf_end - amount,
702
        };
703
    }
704

705
    template<typename A>
706
    const char* to_c_str(A allocator) const
222,814✔
707
    {
708
        auto* retval = allocator.allocate(this->length() + 1);
222,814✔
709
        memcpy(retval, this->data(), this->length());
222,814✔
710
        retval[this->length()] = '\0';
222,814✔
711
        return retval;
222,814✔
712
    }
713

714
    template<typename A>
715
    string_fragment to_owned(A allocator) const
222,814✔
716
    {
717
        return string_fragment{
718
            this->to_c_str(allocator),
719
            0,
720
            this->length(),
721
        };
222,814✔
722
    }
723

724
    std::string_view to_string_view() const
41,681✔
725
    {
726
        return std::string_view{
83,362✔
727
            this->data(),
728
            static_cast<std::string_view::size_type>(this->length())};
83,362✔
729
    }
730

731
    enum class case_style : uint8_t {
732
        lower,
733
        upper,
734
        camel,
735
        mixed,
736
    };
737

738
    case_style detect_text_case_style() const;
739

740
    std::string to_string_with_case_style(case_style style) const;
741

742
    unsigned long hash() const
78,514✔
743
    {
744
        return hash_str(this->data(), this->length());
78,514✔
745
    }
746

747
    const char* sf_string;
748
    int32_t sf_begin;
749
    int32_t sf_end;
750
};
751

752
inline bool
753
operator==(const std::string& left, const string_fragment& right)
1,123✔
754
{
755
    return right == left;
1,123✔
756
}
757

758
inline bool
759
operator<(const char* left, const string_fragment& right)
760
{
761
    int rc = strncmp(left, right.data(), right.length());
762
    return rc < 0;
763
}
764

765
inline void
766
operator+=(std::string& left, const string_fragment& right)
11,548✔
767
{
768
    left.append(right.data(), right.length());
11,548✔
769
}
11,548✔
770

771
inline bool
772
operator<(const string_fragment& left, const char* right)
773
{
774
    return strncmp(left.data(), right, left.length()) < 0;
775
}
776

777
inline std::ostream&
778
operator<<(std::ostream& os, const string_fragment& sf)
2,492✔
779
{
780
    os.write(sf.data(), sf.length());
2,492✔
781
    return os;
2,492✔
782
}
783

784
class string_fragment_producer {
785
public:
786
    struct eof {};
787
    struct error {
788
        std::string what;
789
    };
790
    using next_result = mapbox::util::variant<eof, string_fragment, error>;
791
    static std::unique_ptr<string_fragment_producer> from(string_fragment sf);
792

793
    Result<void, std::string> for_each(
16,569✔
794
        std::function<Result<void, std::string>(string_fragment)> cb)
795
    {
796
        while (true) {
797
            auto next_res = this->next();
42,159✔
798
            if (next_res.is<error>()) {
42,159✔
799
                auto err = next_res.get<error>();
×
800

801
                return Err(err.what);
×
802
            }
803

804
            if (next_res.is<eof>()) {
42,159✔
805
                break;
16,569✔
806
            }
807

808
            const auto sf = next_res.get<string_fragment>();
25,590✔
809
            auto cb_res = cb(sf);
25,590✔
810

811
            if (cb_res.isErr()) {
25,590✔
812
                return Err(cb_res.unwrapErr());
×
813
            }
814
        }
67,749✔
815

816
        return Ok();
16,569✔
817
    }
818

819
    virtual ~string_fragment_producer() {}
90,533✔
820

NEW
821
    virtual size_t estimated_size() const
×
822
    {
NEW
823
        return 1024;
×
824
    }
825

826
    virtual next_result next() = 0;
827

828
    std::string to_string();
829
};
830

831
class intern_string {
832
public:
833
    static const intern_string* lookup(const char* str, ssize_t len) noexcept;
834

835
    static const intern_string* lookup(const string_fragment& sf) noexcept;
836

837
    static const intern_string* lookup(const std::string& str) noexcept;
838

839
    const char* get() const { return this->is_str.c_str(); };
121,967,543✔
840

841
    const char* data() const { return this->is_str.c_str(); };
842

843
    size_t size() const { return this->is_str.size(); }
3,025,843✔
844

845
    std::string to_string() const { return this->is_str; }
1,394,351✔
846

847
    string_fragment to_string_fragment() const
1,588,900✔
848
    {
849
        return string_fragment{this->is_str};
1,588,900✔
850
    }
851

852
    bool startswith(const char* prefix) const;
853

854
    struct intern_table;
855
    static std::shared_ptr<intern_table> get_table_lifetime();
856

857
private:
858
    friend intern_table;
859

860
    intern_string(const char* str, ssize_t len)
2,792,637✔
861
        : is_next(nullptr), is_str(str, (size_t) len)
5,585,274✔
862
    {
863
    }
2,792,637✔
864

865
    intern_string* is_next;
866
    std::string is_str;
867
};
868

869
using intern_table_lifetime = std::shared_ptr<intern_string::intern_table>;
870

871
class intern_string_t {
872
public:
873
    using iterator = const char*;
874

875
    intern_string_t(const intern_string* is = nullptr) : ist_interned_string(is)
21,955,513✔
876
    {
877
    }
21,955,513✔
878

879
    const intern_string* unwrap() const { return this->ist_interned_string; }
880

881
    void clear() { this->ist_interned_string = nullptr; };
38,888✔
882

883
    bool empty() const { return this->ist_interned_string == nullptr; }
130,485,760✔
884

885
    const char* get() const
121,991,224✔
886
    {
887
        if (this->empty()) {
121,991,224✔
888
            return "";
32,455✔
889
        }
890
        return this->ist_interned_string->get();
121,958,769✔
891
    }
892

893
    const char* c_str() const { return this->get(); }
796,666✔
894

895
    const char* data() const { return this->get(); }
×
896

897
    iterator begin() const { return this->get(); }
898

899
    iterator end() const { return this->get() + this->size(); }
900

901
    size_t size() const
3,025,852✔
902
    {
903
        if (this->ist_interned_string == nullptr) {
3,025,852✔
904
            return 0;
9✔
905
        }
906
        return this->ist_interned_string->size();
3,025,843✔
907
    }
908

909
    size_t hash() const
3,144,039✔
910
    {
911
        auto ptr = (uintptr_t) this->ist_interned_string;
3,144,039✔
912

913
        return ptr;
3,144,039✔
914
    }
915

916
    std::string to_string() const
1,394,371✔
917
    {
918
        if (this->ist_interned_string == nullptr) {
1,394,371✔
919
            return "";
40✔
920
        }
921
        return this->ist_interned_string->to_string();
1,394,351✔
922
    }
923

924
    string_fragment to_string_fragment() const
1,588,900✔
925
    {
926
        if (this->ist_interned_string == nullptr) {
1,588,900✔
927
            return string_fragment{"", 0, 0};
×
928
        }
929
        return this->ist_interned_string->to_string_fragment();
1,588,900✔
930
    }
931

932
    bool operator<(const intern_string_t& rhs) const
59,215,522✔
933
    {
934
        return strcmp(this->get(), rhs.get()) < 0;
59,215,522✔
935
    }
936

937
    bool operator==(const intern_string_t& rhs) const
129,567,468✔
938
    {
939
        return this->ist_interned_string == rhs.ist_interned_string;
129,567,468✔
940
    }
941

942
    bool operator!=(const intern_string_t& rhs) const
25,923✔
943
    {
944
        return !(*this == rhs);
25,923✔
945
    }
946

947
    bool operator==(const char* rhs) const
60,132✔
948
    {
949
        return strcmp(this->get(), rhs) == 0;
60,132✔
950
    }
951

952
    bool operator!=(const char* rhs) const
563✔
953
    {
954
        return strcmp(this->get(), rhs) != 0;
563✔
955
    }
956

957
    static bool case_lt(const intern_string_t& lhs, const intern_string_t& rhs)
958
    {
959
        return strnatcasecmp(lhs.size(), lhs.get(), rhs.size(), rhs.get()) < 0;
×
960
    }
961

962
private:
963
    const intern_string* ist_interned_string;
964
};
965

966
namespace fmt {
967
template<>
968
struct formatter<string_fragment> : formatter<string_view> {
969
    template<typename FormatContext>
970
    auto format(const string_fragment& sf, FormatContext& ctx) const
2,063,965✔
971
    {
972
        return formatter<string_view>::format(
2,063,965✔
973
            string_view{sf.data(), (size_t) sf.length()}, ctx);
4,127,930✔
974
    }
975
};
976

977
template<>
978
struct formatter<intern_string_t> : formatter<string_view> {
979
    template<typename FormatContext>
980
    auto format(const intern_string_t& is, FormatContext& ctx)
994,500✔
981
    {
982
        return formatter<string_view>::format(
994,500✔
983
            string_view{is.get(), (size_t) is.size()}, ctx);
1,989,000✔
984
    }
985
};
986
}  // namespace fmt
987

988
namespace std {
989
template<>
990
struct hash<const intern_string_t> {
991
    std::size_t operator()(const intern_string_t& ist) const
3,144,039✔
992
    {
993
        return ist.hash();
3,144,039✔
994
    }
995
};
996
}  // namespace std
997

998
inline bool
999
operator<(const char* left, const intern_string_t& right)
1,484✔
1000
{
1001
    int rc = strncmp(left, right.get(), right.size());
1,484✔
1002
    return rc < 0;
1,484✔
1003
}
1004

1005
inline bool
1006
operator<(const intern_string_t& left, const char* right)
416✔
1007
{
1008
    return strncmp(left.get(), right, left.size()) < 0;
416✔
1009
}
1010

1011
inline bool
1012
operator==(const intern_string_t& left, const string_fragment& sf)
×
1013
{
1014
    return ((int) left.size() == sf.length())
×
1015
        && (memcmp(left.get(), sf.data(), left.size()) == 0);
×
1016
}
1017

1018
inline bool
1019
operator<(const intern_string_t& left, const string_fragment& sf)
1020
{
1021
    return left.to_string_fragment() < sf;
1022
}
1023

1024
inline bool
1025
operator<(const string_fragment& lhs, const intern_string_t& rhs)
1026
{
1027
    return lhs < rhs.to_string_fragment();
1028
}
1029

1030
inline bool
1031
operator==(const string_fragment& left, const intern_string_t& right)
1,236,752✔
1032
{
1033
    return (left.length() == (int) right.size())
1,236,752✔
1034
        && (memcmp(left.data(), right.get(), left.length()) == 0);
1,236,752✔
1035
}
1036

1037
constexpr string_fragment
1038
operator"" _frag(const char* str, std::size_t len)
74,427✔
1039
{
1040
    return string_fragment{str, 0, (int) len};
74,427✔
1041
}
1042

1043
namespace std {
1044
inline string
1045
to_string(const string_fragment& s)
10,028✔
1046
{
1047
    return {s.data(), (size_t) s.length()};
30,084✔
1048
}
1049

1050
inline string
1051
to_string(const intern_string_t& s)
×
1052
{
1053
    return s.to_string();
×
1054
}
1055
}  // namespace std
1056

1057
inline string_fragment
1058
to_string_fragment(const string_fragment& s)
1059
{
1060
    return s;
1061
}
1062

1063
inline string_fragment
1064
to_string_fragment(const intern_string_t& s)
1065
{
1066
    return string_fragment(s.get(), 0, s.size());
1067
}
1068

1069
inline string_fragment
1070
to_string_fragment(const std::string& s)
×
1071
{
1072
    return string_fragment(s.c_str(), 0, s.length());
×
1073
}
1074

1075
inline string_fragment
1076
to_string_fragment(const std::string_view& sv)
1077
{
1078
    return string_fragment::from_bytes(sv.data(), sv.length());
1079
}
1080

1081
struct frag_hasher {
1082
    size_t operator()(const string_fragment& sf) const
1,107,288✔
1083
    {
1084
        return hash_str(sf.data(), sf.length());
1,107,288✔
1085
    }
1086
};
1087

1088
struct intern_hasher {
1089
    size_t operator()(const intern_string_t& is) const
791,425✔
1090
    {
1091
        return hash_str(is.c_str(), is.size());
791,425✔
1092
    }
1093
};
1094

1095
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc