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

tstack / lnav / 17589970077-2502

09 Sep 2025 05:00PM UTC coverage: 65.196% (-5.0%) from 70.225%
17589970077-2502

push

github

tstack
[format] add fields for source file/line

Knowing the source file/line context in a log
message can help find log messages when using
log2src.

56 of 70 new or added lines in 2 files covered. (80.0%)

13954 existing lines in 210 files now uncovered.

45516 of 69814 relevant lines covered (65.2%)

404154.37 hits per line

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

93.85
/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()
180,441✔
57
    {
58
        string_fragment retval;
180,441✔
59

60
        retval.invalidate();
180,441✔
61
        return retval;
180,441✔
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)
163,814✔
70
    {
71
        return string_fragment{str, 0, str != nullptr ? (int) strlen(str) : 0};
163,814✔
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])
183,902✔
82
    {
83
        return string_fragment{str, 0, (int) N - 1};
183,902✔
84
    }
85

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

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

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

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

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

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

130
    explicit constexpr string_fragment(const char* str,
352,478,418✔
131
                                       int begin = 0,
132
                                       int end = -1)
133
        : sf_string(str), sf_begin(begin),
352,478,418✔
134
          sf_end(end == -1
352,478,418✔
135
                     ? static_cast<int>(std::string::traits_type::length(str))
352,478,418✔
136
                     : end)
137
    {
138
    }
352,478,418✔
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)
248,148,075✔
149
        : sf_string(str.c_str()), sf_begin(0), sf_end(str.length())
248,148,075✔
150
    {
151
    }
248,148,075✔
152

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

158
    constexpr int length() const { return this->sf_end - this->sf_begin; }
865,071,688✔
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

UNCOV
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),
×
UNCOV
171
            this->byte_to_column_index(byte_end),
×
172
        };
173
    }
174

175
    size_t column_width() const;
176

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

182
    const unsigned char* udata() const
235,853,083✔
183
    {
184
        return (const unsigned char*) &this->sf_string[this->sf_begin];
235,853,083✔
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]; }
131,382✔
193

194
    uint32_t front_codepoint() const;
195

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

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

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

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

209
    constexpr bool empty() const { return !this->is_valid() || length() == 0; }
131,933,229✔
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,332,844✔
217
    {
218
        return this->sf_string[sf_begin + index];
2,332,844✔
219
    }
220

221
    bool operator==(const std::string& str) const
8,995✔
222
    {
223
        if (this->length() != (int) str.length()) {
8,995✔
224
            return false;
7,303✔
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,405,055✔
233
    {
234
        if (this->length() != sf.length()) {
1,405,055✔
235
            return false;
762,827✔
236
        }
237

238
        return memcmp(this->data(), sf.data(), sf.length()) == 0;
642,228✔
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,988,498✔
247
    {
248
        auto rc = strncmp(
1,988,498✔
249
            this->data(), rhs.data(), std::min(this->length(), rhs.length()));
1,988,498✔
250
        if (rc < 0 || (rc == 0 && this->length() < rhs.length())) {
1,988,498✔
251
            return true;
1,167,928✔
252
        }
253

254
        return false;
820,570✔
255
    }
256

257
    bool iequal(const string_fragment& sf) const
225,282,930✔
258
    {
259
        if (this->length() != sf.length()) {
225,282,930✔
260
            return false;
224,547,985✔
261
        }
262

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

268
    template<std::size_t N>
269
    bool operator==(const char (&str)[N]) const
2,382,979✔
270
    {
271
        return (N - 1) == (size_t) this->length()
2,382,979✔
272
            && strncmp(this->data(), str, N - 1) == 0;
2,382,979✔
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,511,600✔
284
    {
285
        const auto* iter = this->begin();
33,511,600✔
286

287
        while (*prefix != '\0' && iter < this->end() && *prefix == *iter) {
61,978,032✔
288
            prefix += 1;
28,466,432✔
289
            iter += 1;
28,466,432✔
290
        }
291

292
        return *prefix == '\0';
33,511,600✔
293
    }
294

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

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

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

309
        return *suffix == '\0';
121,649✔
310
    }
311

312
    constexpr string_fragment substr(int begin) const
3,289,096✔
313
    {
314
        return string_fragment{
6,578,192✔
315
            this->sf_string, this->sf_begin + begin, this->sf_end};
3,289,096✔
316
    }
317

318
    string_fragment sub_range(int begin, int end) const
172,456,304✔
319
    {
320
        if (this->sf_begin + begin > this->sf_end) {
172,456,304✔
UNCOV
321
            begin = this->sf_end - this->sf_begin;
×
322
        }
323
        if (this->sf_begin + end > this->sf_end) {
172,456,304✔
324
            end = this->sf_end - this->sf_begin;
2,938✔
325
        }
326
        return string_fragment{
344,912,608✔
327
            this->sf_string, this->sf_begin + begin, this->sf_begin + end};
172,456,304✔
328
    }
329

UNCOV
330
    constexpr bool contains(const string_fragment& sf) const
×
331
    {
UNCOV
332
        return this->sf_string == sf.sf_string && this->sf_begin <= sf.sf_begin
×
UNCOV
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,772✔
350
    {
351
        for (int lpc = this->sf_begin; lpc < this->sf_end; lpc++) {
503,844✔
352
            if (this->sf_string[lpc] == ch) {
497,079✔
353
                return lpc - this->sf_begin;
4,007✔
354
            }
355
        }
356

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

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

376
        if (start > 0 && start == static_cast<size_t>(this->length())) {
11,103✔
377
            start -= 1;
6✔
378
        }
379
        while (true) {
380
            if (predicate(this->data()[start])) {
65,634✔
381
                count -= 1;
11,067✔
382
                if (count == 0) {
11,067✔
383
                    start += 1;
11,063✔
384
                    break;
11,063✔
385
                }
386
            } else if (start == 0) {
54,567✔
387
                break;
40✔
388
            }
389
            start -= 1;
54,531✔
390
        }
391

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

438
        if (index_res.isErr()) {
3,643,906✔
439
            return std::nullopt;
386,568✔
440
        }
441

442
        return std::make_pair(cp, this->substr(index_res.unwrap()));
3,257,338✔
443
    }
3,643,906✔
444

445
    template<typename P>
446
    std::optional<string_fragment> consume(P predicate) const
14,161✔
447
    {
448
        int consumed = 0;
14,161✔
449
        while (consumed < this->length()) {
18,152✔
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,161✔
458
            return std::nullopt;
10,182✔
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,149✔
472
    {
473
        int offset = 0;
17,149✔
474
        while (offset < this->length() && predicate(this->data()[offset])) {
31,197✔
475
            offset += 1;
14,048✔
476
        }
477

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

497
            consumed += 1;
2,394,228✔
498
        }
499

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

504
        return std::make_pair(
34,250✔
505
            string_fragment{
506
                this->sf_string,
17,125✔
507
                this->sf_begin,
17,125✔
508
                this->sf_begin + consumed,
17,125✔
509
            },
510
            string_fragment{
511
                this->sf_string,
17,125✔
512
                this->sf_begin + consumed,
17,125✔
513
                this->sf_end,
17,125✔
514
            });
17,125✔
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,699✔
521
    {
522
        int consumed = 0;
11,699✔
523
        while (consumed < this->length()) {
265,731✔
524
            if (predicate(this->data()[consumed])) {
259,449✔
525
                break;
5,417✔
526
            }
527

528
            consumed += 1;
254,032✔
529
        }
530

531
        return std::make_pair(
23,398✔
532
            string_fragment{
533
                this->sf_string,
11,699✔
534
                this->sf_begin,
11,699✔
535
                this->sf_begin + consumed,
11,699✔
536
            },
537
            string_fragment{
538
                this->sf_string,
11,699✔
539
                this->sf_begin + consumed
11,699✔
540
                    + ((consumed == this->length()) ? 0 : 1),
11,699✔
541
                this->sf_end,
11,699✔
542
            });
23,398✔
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✔
UNCOV
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,783,250✔
576
    {
577
        if (this->empty()) {
1,783,250✔
UNCOV
578
            return std::nullopt;
×
579
        }
580

581
        auto curr = this->sf_end - 1;
1,783,250✔
582
        while (curr >= this->sf_begin) {
18,311,087✔
583
            if (predicate(this->sf_string[curr])) {
18,311,087✔
584
                return std::make_pair(
3,566,500✔
585
                    string_fragment{
586
                        this->sf_string,
1,783,250✔
587
                        this->sf_begin,
1,783,250✔
588
                        this->sf_begin + curr,
1,783,250✔
589
                    },
590
                    string_fragment{
591
                        this->sf_string,
1,783,250✔
592
                        this->sf_begin + curr + 1,
1,783,250✔
593
                        this->sf_end,
1,783,250✔
594
                    });
1,783,250✔
595
            }
596

597
            curr -= 1;
16,527,837✔
598
        }
599

UNCOV
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,820,708✔
611

612
        constexpr bool operator()(char ch) const { return this->t_value == ch; }
18,711,832✔
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,042,704✔
644
    {
645
        return {this->data(), (size_t) this->length()};
30,128,112✔
646
    }
647

648
    std::string to_unquoted_string() const;
649

650
    void clear()
30,089✔
651
    {
652
        this->sf_begin = 0;
30,089✔
653
        this->sf_end = 0;
30,089✔
654
    }
30,089✔
655

656
    constexpr void invalidate()
249,306✔
657
    {
658
        this->sf_begin = -1;
249,306✔
659
        this->sf_end = -1;
249,306✔
660
    }
249,306✔
661

662
    string_fragment trim(const char* tokens) const;
663
    string_fragment rtrim(const char* tokens) const;
664
    string_fragment trim() const;
665

666
    string_fragment prepend(const char* str, int amount) const
7,002✔
667
    {
668
        return string_fragment{
14,004✔
669
            str,
670
            this->sf_begin + amount,
7,002✔
671
            this->sf_end + amount,
7,002✔
672
        };
7,002✔
673
    }
674

675
    string_fragment erase_before(const char* str, int amount) const
2,162✔
676
    {
677
        return string_fragment{
4,324✔
678
            str,
679
            this->sf_begin - amount,
2,162✔
680
            this->sf_end - amount,
2,162✔
681
        };
2,162✔
682
    }
683

684
    string_fragment erase(const char* str, int amount) const
685
    {
686
        return string_fragment{
687
            str,
688
            this->sf_begin,
689
            this->sf_end - amount,
690
        };
691
    }
692

693
    template<typename A>
694
    const char* to_c_str(A allocator) const
96,890✔
695
    {
696
        auto* retval = allocator.allocate(this->length() + 1);
96,890✔
697
        memcpy(retval, this->data(), this->length());
96,890✔
698
        retval[this->length()] = '\0';
96,890✔
699
        return retval;
96,890✔
700
    }
701

702
    template<typename A>
703
    string_fragment to_owned(A allocator) const
96,890✔
704
    {
705
        return string_fragment{
706
            this->to_c_str(allocator),
707
            0,
708
            this->length(),
709
        };
96,890✔
710
    }
711

712
    std::string_view to_string_view() const
40,458✔
713
    {
714
        return std::string_view{
80,916✔
715
            this->data(),
716
            static_cast<std::string_view::size_type>(this->length())};
80,916✔
717
    }
718

719
    enum class case_style : uint8_t {
720
        lower,
721
        upper,
722
        camel,
723
        mixed,
724
    };
725

726
    case_style detect_text_case_style() const;
727

728
    std::string to_string_with_case_style(case_style style) const;
729

730
    unsigned long hash() const
35,759✔
731
    {
732
        return hash_str(this->data(), this->length());
35,759✔
733
    }
734

735
    const char* sf_string;
736
    int32_t sf_begin;
737
    int32_t sf_end;
738
};
739

740
inline bool
741
operator==(const std::string& left, const string_fragment& right)
862✔
742
{
743
    return right == left;
862✔
744
}
745

746
inline bool
747
operator<(const char* left, const string_fragment& right)
748
{
749
    int rc = strncmp(left, right.data(), right.length());
750
    return rc < 0;
751
}
752

753
inline void
754
operator+=(std::string& left, const string_fragment& right)
10,008✔
755
{
756
    left.append(right.data(), right.length());
10,008✔
757
}
10,008✔
758

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

765
inline std::ostream&
766
operator<<(std::ostream& os, const string_fragment& sf)
2,482✔
767
{
768
    os.write(sf.data(), sf.length());
2,482✔
769
    return os;
2,482✔
770
}
771

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

781
    Result<void, std::string> for_each(
14,394✔
782
        std::function<Result<void, std::string>(string_fragment)> cb)
783
    {
784
        while (true) {
785
            auto next_res = this->next();
37,239✔
786
            if (next_res.is<error>()) {
37,239✔
UNCOV
787
                auto err = next_res.get<error>();
×
788

789
                return Err(err.what);
×
790
            }
791

792
            if (next_res.is<eof>()) {
37,239✔
793
                break;
14,394✔
794
            }
795

796
            const auto sf = next_res.get<string_fragment>();
22,845✔
797
            auto cb_res = cb(sf);
22,845✔
798

799
            if (cb_res.isErr()) {
22,845✔
UNCOV
800
                return Err(cb_res.unwrapErr());
×
801
            }
802
        }
60,084✔
803

804
        return Ok();
14,394✔
805
    }
806

807
    virtual ~string_fragment_producer() {}
86,006✔
808

809
    virtual next_result next() = 0;
810

811
    std::string to_string();
812
};
813

814
class intern_string {
815
public:
816
    static const intern_string* lookup(const char* str, ssize_t len) noexcept;
817

818
    static const intern_string* lookup(const string_fragment& sf) noexcept;
819

820
    static const intern_string* lookup(const std::string& str) noexcept;
821

822
    const char* get() const { return this->is_str.c_str(); };
115,535,166✔
823

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

826
    size_t size() const { return this->is_str.size(); }
2,968,294✔
827

828
    std::string to_string() const { return this->is_str; }
1,334,788✔
829

830
    string_fragment to_string_fragment() const
1,532,031✔
831
    {
832
        return string_fragment{this->is_str};
1,532,031✔
833
    }
834

835
    bool startswith(const char* prefix) const;
836

837
    struct intern_table;
838
    static std::shared_ptr<intern_table> get_table_lifetime();
839

840
private:
841
    friend intern_table;
842

843
    intern_string(const char* str, ssize_t len)
2,665,949✔
844
        : is_next(nullptr), is_str(str, (size_t) len)
5,331,898✔
845
    {
846
    }
2,665,949✔
847

848
    intern_string* is_next;
849
    std::string is_str;
850
};
851

852
using intern_table_lifetime = std::shared_ptr<intern_string::intern_table>;
853

854
class intern_string_t {
855
public:
856
    using iterator = const char*;
857

858
    intern_string_t(const intern_string* is = nullptr) : ist_interned_string(is)
20,808,882✔
859
    {
860
    }
20,808,882✔
861

862
    const intern_string* unwrap() const { return this->ist_interned_string; }
863

864
    void clear() { this->ist_interned_string = nullptr; };
37,776✔
865

866
    bool empty() const { return this->ist_interned_string == nullptr; }
123,470,053✔
867

868
    const char* get() const
115,558,149✔
869
    {
870
        if (this->empty()) {
115,558,149✔
871
            return "";
31,491✔
872
        }
873
        return this->ist_interned_string->get();
115,526,658✔
874
    }
875

876
    const char* c_str() const { return this->get(); }
773,515✔
877

878
    const char* data() const { return this->get(); }
×
879

880
    iterator begin() const { return this->get(); }
881

882
    iterator end() const { return this->get() + this->size(); }
883

884
    size_t size() const
2,968,302✔
885
    {
886
        if (this->ist_interned_string == nullptr) {
2,968,302✔
887
            return 0;
8✔
888
        }
889
        return this->ist_interned_string->size();
2,968,294✔
890
    }
891

892
    size_t hash() const
3,008,082✔
893
    {
894
        auto ptr = (uintptr_t) this->ist_interned_string;
3,008,082✔
895

896
        return ptr;
3,008,082✔
897
    }
898

899
    std::string to_string() const
1,334,808✔
900
    {
901
        if (this->ist_interned_string == nullptr) {
1,334,808✔
902
            return "";
40✔
903
        }
904
        return this->ist_interned_string->to_string();
1,334,788✔
905
    }
906

907
    string_fragment to_string_fragment() const
1,532,031✔
908
    {
909
        if (this->ist_interned_string == nullptr) {
1,532,031✔
UNCOV
910
            return string_fragment{"", 0, 0};
×
911
        }
912
        return this->ist_interned_string->to_string_fragment();
1,532,031✔
913
    }
914

915
    bool operator<(const intern_string_t& rhs) const
56,060,498✔
916
    {
917
        return strcmp(this->get(), rhs.get()) < 0;
56,060,498✔
918
    }
919

920
    bool operator==(const intern_string_t& rhs) const
122,481,279✔
921
    {
922
        return this->ist_interned_string == rhs.ist_interned_string;
122,481,279✔
923
    }
924

925
    bool operator!=(const intern_string_t& rhs) const
24,633✔
926
    {
927
        return !(*this == rhs);
24,633✔
928
    }
929

930
    bool operator==(const char* rhs) const
47,579✔
931
    {
932
        return strcmp(this->get(), rhs) == 0;
47,579✔
933
    }
934

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

940
    static bool case_lt(const intern_string_t& lhs, const intern_string_t& rhs)
941
    {
UNCOV
942
        return strnatcasecmp(lhs.size(), lhs.get(), rhs.size(), rhs.get()) < 0;
×
943
    }
944

945
private:
946
    const intern_string* ist_interned_string;
947
};
948

949
namespace fmt {
950
template<>
951
struct formatter<string_fragment> : formatter<string_view> {
952
    template<typename FormatContext>
953
    auto format(const string_fragment& sf, FormatContext& ctx) const
1,906,893✔
954
    {
955
        return formatter<string_view>::format(
1,906,893✔
956
            string_view{sf.data(), (size_t) sf.length()}, ctx);
3,813,786✔
957
    }
958
};
959

960
template<>
961
struct formatter<intern_string_t> : formatter<string_view> {
962
    template<typename FormatContext>
963
    auto format(const intern_string_t& is, FormatContext& ctx)
960,413✔
964
    {
965
        return formatter<string_view>::format(
960,413✔
966
            string_view{is.get(), (size_t) is.size()}, ctx);
1,920,826✔
967
    }
968
};
969
}  // namespace fmt
970

971
namespace std {
972
template<>
973
struct hash<const intern_string_t> {
974
    std::size_t operator()(const intern_string_t& ist) const
3,008,082✔
975
    {
976
        return ist.hash();
3,008,082✔
977
    }
978
};
979
}  // namespace std
980

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

988
inline bool
989
operator<(const intern_string_t& left, const char* right)
416✔
990
{
991
    return strncmp(left.get(), right, left.size()) < 0;
416✔
992
}
993

994
inline bool
UNCOV
995
operator==(const intern_string_t& left, const string_fragment& sf)
×
996
{
UNCOV
997
    return ((int) left.size() == sf.length())
×
UNCOV
998
        && (memcmp(left.get(), sf.data(), left.size()) == 0);
×
999
}
1000

1001
inline bool
1002
operator<(const intern_string_t& left, const string_fragment& sf)
1003
{
1004
    return left.to_string_fragment() < sf;
1005
}
1006

1007
inline bool
1008
operator<(const string_fragment& lhs, const intern_string_t& rhs)
1009
{
1010
    return lhs < rhs.to_string_fragment();
1011
}
1012

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

1020
constexpr string_fragment
1021
operator"" _frag(const char* str, std::size_t len)
69,300✔
1022
{
1023
    return string_fragment{str, 0, (int) len};
69,300✔
1024
}
1025

1026
namespace std {
1027
inline string
1028
to_string(const string_fragment& s)
9,820✔
1029
{
1030
    return {s.data(), (size_t) s.length()};
29,460✔
1031
}
1032

1033
inline string
UNCOV
1034
to_string(const intern_string_t& s)
×
1035
{
UNCOV
1036
    return s.to_string();
×
1037
}
1038
}  // namespace std
1039

1040
inline string_fragment
1041
to_string_fragment(const string_fragment& s)
1042
{
1043
    return s;
1044
}
1045

1046
inline string_fragment
1047
to_string_fragment(const intern_string_t& s)
1048
{
1049
    return string_fragment(s.get(), 0, s.size());
1050
}
1051

1052
inline string_fragment
UNCOV
1053
to_string_fragment(const std::string& s)
×
1054
{
UNCOV
1055
    return string_fragment(s.c_str(), 0, s.length());
×
1056
}
1057

1058
inline string_fragment
1059
to_string_fragment(const std::string_view& sv)
1060
{
1061
    return string_fragment::from_bytes(sv.data(), sv.length());
1062
}
1063

1064
struct frag_hasher {
1065
    size_t operator()(const string_fragment& sf) const
838,454✔
1066
    {
1067
        return hash_str(sf.data(), sf.length());
838,454✔
1068
    }
1069
};
1070

1071
struct intern_hasher {
1072
    size_t operator()(const intern_string_t& is) const
768,465✔
1073
    {
1074
        return hash_str(is.c_str(), is.size());
768,465✔
1075
    }
1076
};
1077

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

© 2026 Coveralls, Inc