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

tstack / lnav / 17743879018-2517

15 Sep 2025 07:09PM UTC coverage: 64.914% (-0.04%) from 64.951%
17743879018-2517

push

github

tstack
[build] missed adding file

45705 of 70409 relevant lines covered (64.91%)

406498.12 hits per line

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

93.62
/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()
181,133✔
57
    {
58
        string_fragment retval;
181,133✔
59

60
        retval.invalidate();
181,133✔
61
        return retval;
181,133✔
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)
164,350✔
70
    {
71
        return string_fragment{str, 0, str != nullptr ? (int) strlen(str) : 0};
164,350✔
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])
185,439✔
82
    {
83
        return string_fragment{str, 0, (int) N - 1};
185,439✔
84
    }
85

86
    static string_fragment from_str(const std::string& str)
278,399✔
87
    {
88
        return string_fragment{str.c_str(), 0, (int) str.size()};
278,399✔
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,
132,340✔
100
                                          size_t begin,
101
                                          size_t end)
102
    {
103
        return string_fragment{str.c_str(), (int) begin, (int) end};
132,340✔
104
    }
105

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

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

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

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

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

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

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

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

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

158
    constexpr int length() const { return this->sf_end - this->sf_begin; }
857,857,657✔
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
35,618,713✔
178
    {
179
        return &this->sf_string[this->sf_begin];
35,618,713✔
180
    }
181

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

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

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

194
    uint32_t front_codepoint() const;
195

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

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

205
    iterator begin() const { return &this->sf_string[this->sf_begin]; }
44,664,118✔
206

207
    iterator end() const { return &this->sf_string[this->sf_end]; }
44,828,880✔
208

209
    constexpr bool empty() const { return !this->is_valid() || length() == 0; }
132,934,859✔
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,340,891✔
217
    {
218
        return this->sf_string[sf_begin + index];
2,340,891✔
219
    }
220

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

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

232
    bool operator==(const string_fragment& sf) const
1,410,543✔
233
    {
234
        if (this->length() != sf.length()) {
1,410,543✔
235
            return false;
765,304✔
236
        }
237

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

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

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

254
        return false;
823,420✔
255
    }
256

257
    bool iequal(const string_fragment& sf) const
226,046,434✔
258
    {
259
        if (this->length() != sf.length()) {
226,046,434✔
260
            return false;
225,309,003✔
261
        }
262

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

268
    template<std::size_t N>
269
    bool operator==(const char (&str)[N]) const
2,391,346✔
270
    {
271
        return (N - 1) == (size_t) this->length()
2,391,346✔
272
            && strncmp(this->data(), str, N - 1) == 0;
2,391,346✔
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
7,429✔
279
    {
280
        return (this->operator==(args) || ...);
8,663✔
281
    }
282

283
    bool startswith(const char* prefix) const
33,751,919✔
284
    {
285
        const auto* iter = this->begin();
33,751,919✔
286

287
        while (*prefix != '\0' && iter < this->end() && *prefix == *iter) {
62,429,957✔
288
            prefix += 1;
28,678,038✔
289
            iter += 1;
28,678,038✔
290
        }
291

292
        return *prefix == '\0';
33,751,919✔
293
    }
294

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

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

303
        const auto* curr = this->end() - suffix_len;
122,255✔
304
        while (*suffix != '\0' && *curr == *suffix) {
124,927✔
305
            suffix += 1;
2,672✔
306
            curr += 1;
2,672✔
307
        }
308

309
        return *suffix == '\0';
122,255✔
310
    }
311

312
    constexpr string_fragment substr(int begin) const
3,350,512✔
313
    {
314
        return string_fragment{
6,701,024✔
315
            this->sf_string, this->sf_begin + begin, this->sf_end};
3,350,512✔
316
    }
317

318
    string_fragment sub_range(int begin, int end) const
173,808,630✔
319
    {
320
        if (this->sf_begin + begin > this->sf_end) {
173,808,630✔
321
            begin = this->sf_end - this->sf_begin;
×
322
        }
323
        if (this->sf_begin + end > this->sf_end) {
173,808,630✔
324
            end = this->sf_end - this->sf_begin;
2,950✔
325
        }
326
        return string_fragment{
347,617,260✔
327
            this->sf_string, this->sf_begin + begin, this->sf_begin + end};
173,808,630✔
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
154✔
337
    {
338
        size_t retval = 0;
154✔
339

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

346
        return retval;
154✔
347
    }
348

349
    std::optional<int> find(char ch) const
10,830✔
350
    {
351
        for (int lpc = this->sf_begin; lpc < this->sf_end; lpc++) {
507,273✔
352
            if (this->sf_string[lpc] == ch) {
500,459✔
353
                return lpc - this->sf_begin;
4,016✔
354
            }
355
        }
356

357
        return std::nullopt;
6,814✔
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,209✔
367
                                       P&& predicate,
368
                                       size_t count = 1) const
369
    {
370
        assert((int) start <= this->length());
371

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

376
        if (start > 0 && start == static_cast<size_t>(this->length())) {
11,209✔
377
            start -= 1;
6✔
378
        }
379
        while (true) {
380
            if (predicate(this->data()[start])) {
66,032✔
381
                count -= 1;
11,173✔
382
                if (count == 0) {
11,173✔
383
                    start += 1;
11,169✔
384
                    break;
11,169✔
385
                }
386
            } else if (start == 0) {
54,859✔
387
                break;
40✔
388
            }
389
            start -= 1;
54,823✔
390
        }
391

392
        return string_fragment{
393
            this->sf_string,
11,209✔
394
            this->sf_begin + (int) start,
11,209✔
395
            this->sf_end,
11,209✔
396
        };
11,209✔
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,712,052✔
433
        const
434
    {
435
        auto cp = this->front_codepoint();
3,712,052✔
436
        auto index_res = this->codepoint_to_byte_index(1);
3,712,052✔
437

438
        if (index_res.isErr()) {
3,712,052✔
439
            return std::nullopt;
394,558✔
440
        }
441

442
        return std::make_pair(cp, this->substr(index_res.unwrap()));
3,317,494✔
443
    }
3,712,052✔
444

445
    template<typename P>
446
    std::optional<string_fragment> consume(P predicate) const
14,177✔
447
    {
448
        int consumed = 0;
14,177✔
449
        while (consumed < this->length()) {
18,168✔
450
            if (!predicate(this->data()[consumed])) {
7,953✔
451
                break;
3,962✔
452
            }
453

454
            consumed += 1;
3,991✔
455
        }
456

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

461
        return string_fragment{
462
            this->sf_string,
3,979✔
463
            this->sf_begin + consumed,
3,979✔
464
            this->sf_end,
3,979✔
465
        };
3,979✔
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,178✔
472
    {
473
        int offset = 0;
17,178✔
474
        while (offset < this->length() && predicate(this->data()[offset])) {
31,226✔
475
            offset += 1;
14,048✔
476
        }
477

478
        return string_fragment{
479
            this->sf_string,
17,178✔
480
            this->sf_begin + offset,
17,178✔
481
            this->sf_end,
17,178✔
482
        };
17,178✔
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
17,483✔
490
    {
491
        int consumed = 0;
17,483✔
492
        while (consumed < this->length()) {
2,412,951✔
493
            if (!predicate(this->data()[consumed])) {
2,402,610✔
494
                break;
7,142✔
495
            }
496

497
            consumed += 1;
2,395,468✔
498
        }
499

500
        if (consumed == 0) {
17,483✔
501
            return std::nullopt;
342✔
502
        }
503

504
        return std::make_pair(
34,282✔
505
            string_fragment{
506
                this->sf_string,
17,141✔
507
                this->sf_begin,
17,141✔
508
                this->sf_begin + consumed,
17,141✔
509
            },
510
            string_fragment{
511
                this->sf_string,
17,141✔
512
                this->sf_begin + consumed,
17,141✔
513
                this->sf_end,
17,141✔
514
            });
17,141✔
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
11,780✔
521
    {
522
        int consumed = 0;
11,780✔
523
        while (consumed < this->length()) {
268,329✔
524
            if (predicate(this->data()[consumed])) {
262,019✔
525
                break;
5,470✔
526
            }
527

528
            consumed += 1;
256,549✔
529
        }
530

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

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

554
            consumed += 1;
74,943✔
555
        }
556

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

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

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

581
        auto curr = this->sf_end - 1;
1,789,346✔
582
        while (curr >= this->sf_begin) {
18,373,683✔
583
            if (predicate(this->sf_string[curr])) {
18,373,683✔
584
                return std::make_pair(
3,578,692✔
585
                    string_fragment{
586
                        this->sf_string,
1,789,346✔
587
                        this->sf_begin,
1,789,346✔
588
                        this->sf_begin + curr,
1,789,346✔
589
                    },
590
                    string_fragment{
591
                        this->sf_string,
1,789,346✔
592
                        this->sf_begin + curr + 1,
1,789,346✔
593
                        this->sf_end,
1,789,346✔
594
                    });
1,789,346✔
595
            }
596

597
            curr -= 1;
16,584,337✔
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,826,945✔
611

612
        constexpr bool operator()(char ch) const { return this->t_value == ch; }
18,777,129✔
613
    };
614

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

618
        bool operator()(char ch)
6,864✔
619
        {
620
            if (this->qs_in_escape) {
6,864✔
621
                this->qs_in_escape = false;
1✔
622
                return true;
1✔
623
            }
624
            if (ch == '\\') {
6,863✔
625
                this->qs_in_escape = true;
1✔
626
                return true;
1✔
627
            }
628
            if (ch == '"') {
6,862✔
629
                return false;
346✔
630
            }
631
            return true;
6,516✔
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,142,660✔
644
    {
645
        if (!this->is_valid()) {
10,142,660✔
646
            return "<invalid>";
×
647
        }
648

649
        return {this->data(), (size_t) this->length()};
30,427,980✔
650
    }
651

652
    std::string to_unquoted_string() const;
653

654
    void clear()
30,092✔
655
    {
656
        this->sf_begin = 0;
30,092✔
657
        this->sf_end = 0;
30,092✔
658
    }
30,092✔
659

660
    constexpr void invalidate()
249,998✔
661
    {
662
        this->sf_begin = -1;
249,998✔
663
        this->sf_end = -1;
249,998✔
664
    }
249,998✔
665

666
    string_fragment trim(const char* tokens) const;
667
    string_fragment rtrim(const char* tokens) const;
668
    string_fragment trim() const;
669

670
    string_fragment prepend(const char* str, int amount) const
7,042✔
671
    {
672
        return string_fragment{
14,084✔
673
            str,
674
            this->sf_begin + amount,
7,042✔
675
            this->sf_end + amount,
7,042✔
676
        };
7,042✔
677
    }
678

679
    string_fragment erase_before(const char* str, int amount) const
2,184✔
680
    {
681
        return string_fragment{
4,368✔
682
            str,
683
            this->sf_begin - amount,
2,184✔
684
            this->sf_end - amount,
2,184✔
685
        };
2,184✔
686
    }
687

688
    string_fragment erase(const char* str, int amount) const
689
    {
690
        return string_fragment{
691
            str,
692
            this->sf_begin,
693
            this->sf_end - amount,
694
        };
695
    }
696

697
    template<typename A>
698
    const char* to_c_str(A allocator) const
97,112✔
699
    {
700
        auto* retval = allocator.allocate(this->length() + 1);
97,112✔
701
        memcpy(retval, this->data(), this->length());
97,112✔
702
        retval[this->length()] = '\0';
97,112✔
703
        return retval;
97,112✔
704
    }
705

706
    template<typename A>
707
    string_fragment to_owned(A allocator) const
97,112✔
708
    {
709
        return string_fragment{
710
            this->to_c_str(allocator),
711
            0,
712
            this->length(),
713
        };
97,112✔
714
    }
715

716
    std::string_view to_string_view() const
40,519✔
717
    {
718
        return std::string_view{
81,038✔
719
            this->data(),
720
            static_cast<std::string_view::size_type>(this->length())};
81,038✔
721
    }
722

723
    enum class case_style : uint8_t {
724
        lower,
725
        upper,
726
        camel,
727
        mixed,
728
    };
729

730
    case_style detect_text_case_style() const;
731

732
    std::string to_string_with_case_style(case_style style) const;
733

734
    unsigned long hash() const
35,774✔
735
    {
736
        return hash_str(this->data(), this->length());
35,774✔
737
    }
738

739
    const char* sf_string;
740
    int32_t sf_begin;
741
    int32_t sf_end;
742
};
743

744
inline bool
745
operator==(const std::string& left, const string_fragment& right)
862✔
746
{
747
    return right == left;
862✔
748
}
749

750
inline bool
751
operator<(const char* left, const string_fragment& right)
752
{
753
    int rc = strncmp(left, right.data(), right.length());
754
    return rc < 0;
755
}
756

757
inline void
758
operator+=(std::string& left, const string_fragment& right)
10,123✔
759
{
760
    left.append(right.data(), right.length());
10,123✔
761
}
10,123✔
762

763
inline bool
764
operator<(const string_fragment& left, const char* right)
765
{
766
    return strncmp(left.data(), right, left.length()) < 0;
767
}
768

769
inline std::ostream&
770
operator<<(std::ostream& os, const string_fragment& sf)
2,477✔
771
{
772
    os.write(sf.data(), sf.length());
2,477✔
773
    return os;
2,477✔
774
}
775

776
class string_fragment_producer {
777
public:
778
    struct eof {};
779
    struct error {
780
        std::string what;
781
    };
782
    using next_result = mapbox::util::variant<eof, string_fragment, error>;
783
    static std::unique_ptr<string_fragment_producer> from(string_fragment sf);
784

785
    Result<void, std::string> for_each(
15,984✔
786
        std::function<Result<void, std::string>(string_fragment)> cb)
787
    {
788
        while (true) {
789
            auto next_res = this->next();
40,555✔
790
            if (next_res.is<error>()) {
40,555✔
791
                auto err = next_res.get<error>();
×
792

793
                return Err(err.what);
×
794
            }
795

796
            if (next_res.is<eof>()) {
40,555✔
797
                break;
15,984✔
798
            }
799

800
            const auto sf = next_res.get<string_fragment>();
24,571✔
801
            auto cb_res = cb(sf);
24,571✔
802

803
            if (cb_res.isErr()) {
24,571✔
804
                return Err(cb_res.unwrapErr());
×
805
            }
806
        }
65,126✔
807

808
        return Ok();
15,984✔
809
    }
810

811
    virtual ~string_fragment_producer() {}
87,932✔
812

813
    virtual next_result next() = 0;
814

815
    std::string to_string();
816
};
817

818
class intern_string {
819
public:
820
    static const intern_string* lookup(const char* str, ssize_t len) noexcept;
821

822
    static const intern_string* lookup(const string_fragment& sf) noexcept;
823

824
    static const intern_string* lookup(const std::string& str) noexcept;
825

826
    const char* get() const { return this->is_str.c_str(); };
117,041,147✔
827

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

830
    size_t size() const { return this->is_str.size(); }
2,975,052✔
831

832
    std::string to_string() const { return this->is_str; }
1,340,975✔
833

834
    string_fragment to_string_fragment() const
1,538,386✔
835
    {
836
        return string_fragment{this->is_str};
1,538,386✔
837
    }
838

839
    bool startswith(const char* prefix) const;
840

841
    struct intern_table;
842
    static std::shared_ptr<intern_table> get_table_lifetime();
843

844
private:
845
    friend intern_table;
846

847
    intern_string(const char* str, ssize_t len)
2,686,995✔
848
        : is_next(nullptr), is_str(str, (size_t) len)
5,373,990✔
849
    {
850
    }
2,686,995✔
851

852
    intern_string* is_next;
853
    std::string is_str;
854
};
855

856
using intern_table_lifetime = std::shared_ptr<intern_string::intern_table>;
857

858
class intern_string_t {
859
public:
860
    using iterator = const char*;
861

862
    intern_string_t(const intern_string* is = nullptr) : ist_interned_string(is)
21,019,432✔
863
    {
864
    }
21,019,432✔
865

866
    const intern_string* unwrap() const { return this->ist_interned_string; }
867

868
    void clear() { this->ist_interned_string = nullptr; };
37,872✔
869

870
    bool empty() const { return this->ist_interned_string == nullptr; }
125,049,462✔
871

872
    const char* get() const
117,064,192✔
873
    {
874
        if (this->empty()) {
117,064,192✔
875
            return "";
31,577✔
876
        }
877
        return this->ist_interned_string->get();
117,032,615✔
878
    }
879

880
    const char* c_str() const { return this->get(); }
774,799✔
881

882
    const char* data() const { return this->get(); }
×
883

884
    iterator begin() const { return this->get(); }
885

886
    iterator end() const { return this->get() + this->size(); }
887

888
    size_t size() const
2,975,060✔
889
    {
890
        if (this->ist_interned_string == nullptr) {
2,975,060✔
891
            return 0;
8✔
892
        }
893
        return this->ist_interned_string->size();
2,975,052✔
894
    }
895

896
    size_t hash() const
3,031,940✔
897
    {
898
        auto ptr = (uintptr_t) this->ist_interned_string;
3,031,940✔
899

900
        return ptr;
3,031,940✔
901
    }
902

903
    std::string to_string() const
1,340,995✔
904
    {
905
        if (this->ist_interned_string == nullptr) {
1,340,995✔
906
            return "";
40✔
907
        }
908
        return this->ist_interned_string->to_string();
1,340,975✔
909
    }
910

911
    string_fragment to_string_fragment() const
1,538,386✔
912
    {
913
        if (this->ist_interned_string == nullptr) {
1,538,386✔
914
            return string_fragment{"", 0, 0};
×
915
        }
916
        return this->ist_interned_string->to_string_fragment();
1,538,386✔
917
    }
918

919
    bool operator<(const intern_string_t& rhs) const
56,805,950✔
920
    {
921
        return strcmp(this->get(), rhs.get()) < 0;
56,805,950✔
922
    }
923

924
    bool operator==(const intern_string_t& rhs) const
124,092,186✔
925
    {
926
        return this->ist_interned_string == rhs.ist_interned_string;
124,092,186✔
927
    }
928

929
    bool operator!=(const intern_string_t& rhs) const
24,701✔
930
    {
931
        return !(*this == rhs);
24,701✔
932
    }
933

934
    bool operator==(const char* rhs) const
47,739✔
935
    {
936
        return strcmp(this->get(), rhs) == 0;
47,739✔
937
    }
938

939
    bool operator!=(const char* rhs) const
283✔
940
    {
941
        return strcmp(this->get(), rhs) != 0;
283✔
942
    }
943

944
    static bool case_lt(const intern_string_t& lhs, const intern_string_t& rhs)
945
    {
946
        return strnatcasecmp(lhs.size(), lhs.get(), rhs.size(), rhs.get()) < 0;
×
947
    }
948

949
private:
950
    const intern_string* ist_interned_string;
951
};
952

953
namespace fmt {
954
template<>
955
struct formatter<string_fragment> : formatter<string_view> {
956
    template<typename FormatContext>
957
    auto format(const string_fragment& sf, FormatContext& ctx) const
1,913,475✔
958
    {
959
        return formatter<string_view>::format(
1,913,475✔
960
            string_view{sf.data(), (size_t) sf.length()}, ctx);
3,826,950✔
961
    }
962
};
963

964
template<>
965
struct formatter<intern_string_t> : formatter<string_view> {
966
    template<typename FormatContext>
967
    auto format(const intern_string_t& is, FormatContext& ctx)
965,983✔
968
    {
969
        return formatter<string_view>::format(
965,983✔
970
            string_view{is.get(), (size_t) is.size()}, ctx);
1,931,966✔
971
    }
972
};
973
}  // namespace fmt
974

975
namespace std {
976
template<>
977
struct hash<const intern_string_t> {
978
    std::size_t operator()(const intern_string_t& ist) const
3,031,940✔
979
    {
980
        return ist.hash();
3,031,940✔
981
    }
982
};
983
}  // namespace std
984

985
inline bool
986
operator<(const char* left, const intern_string_t& right)
1,484✔
987
{
988
    int rc = strncmp(left, right.get(), right.size());
1,484✔
989
    return rc < 0;
1,484✔
990
}
991

992
inline bool
993
operator<(const intern_string_t& left, const char* right)
416✔
994
{
995
    return strncmp(left.get(), right, left.size()) < 0;
416✔
996
}
997

998
inline bool
999
operator==(const intern_string_t& left, const string_fragment& sf)
×
1000
{
1001
    return ((int) left.size() == sf.length())
×
1002
        && (memcmp(left.get(), sf.data(), left.size()) == 0);
×
1003
}
1004

1005
inline bool
1006
operator<(const intern_string_t& left, const string_fragment& sf)
1007
{
1008
    return left.to_string_fragment() < sf;
1009
}
1010

1011
inline bool
1012
operator<(const string_fragment& lhs, const intern_string_t& rhs)
1013
{
1014
    return lhs < rhs.to_string_fragment();
1015
}
1016

1017
inline bool
1018
operator==(const string_fragment& left, const intern_string_t& right)
1,236,410✔
1019
{
1020
    return (left.length() == (int) right.size())
1,236,410✔
1021
        && (memcmp(left.data(), right.get(), left.length()) == 0);
1,236,410✔
1022
}
1023

1024
constexpr string_fragment
1025
operator"" _frag(const char* str, std::size_t len)
69,444✔
1026
{
1027
    return string_fragment{str, 0, (int) len};
69,444✔
1028
}
1029

1030
namespace std {
1031
inline string
1032
to_string(const string_fragment& s)
9,852✔
1033
{
1034
    return {s.data(), (size_t) s.length()};
29,556✔
1035
}
1036

1037
inline string
1038
to_string(const intern_string_t& s)
×
1039
{
1040
    return s.to_string();
×
1041
}
1042
}  // namespace std
1043

1044
inline string_fragment
1045
to_string_fragment(const string_fragment& s)
1046
{
1047
    return s;
1048
}
1049

1050
inline string_fragment
1051
to_string_fragment(const intern_string_t& s)
1052
{
1053
    return string_fragment(s.get(), 0, s.size());
1054
}
1055

1056
inline string_fragment
1057
to_string_fragment(const std::string& s)
×
1058
{
1059
    return string_fragment(s.c_str(), 0, s.length());
×
1060
}
1061

1062
inline string_fragment
1063
to_string_fragment(const std::string_view& sv)
1064
{
1065
    return string_fragment::from_bytes(sv.data(), sv.length());
1066
}
1067

1068
struct frag_hasher {
1069
    size_t operator()(const string_fragment& sf) const
841,863✔
1070
    {
1071
        return hash_str(sf.data(), sf.length());
841,863✔
1072
    }
1073
};
1074

1075
struct intern_hasher {
1076
    size_t operator()(const intern_string_t& is) const
769,653✔
1077
    {
1078
        return hash_str(is.c_str(), is.size());
769,653✔
1079
    }
1080
};
1081

1082
#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