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

tstack / lnav / 21043099841-2763

15 Jan 2026 06:11AM UTC coverage: 68.949% (+0.02%) from 68.934%
21043099841-2763

push

github

tstack
[logfmt] parse spans that are not kv-pairs

84 of 94 new or added lines in 5 files covered. (89.36%)

2 existing lines in 1 file now uncovered.

51802 of 75131 relevant lines covered (68.95%)

434450.69 hits per line

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

93.61
/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 <system_error>
40
#include <tuple>
41
#include <vector>
42

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

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

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

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

57
    static constexpr string_fragment invalid()
202,900✔
58
    {
59
        string_fragment retval;
202,900✔
60

61
        retval.invalidate();
202,900✔
62
        return retval;
202,900✔
63
    }
64

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

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

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

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

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

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

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

107
    static string_fragment from_bytes(const char* bytes, size_t len)
14,009,513✔
108
    {
109
        return string_fragment{bytes, 0, (int) len};
14,009,513✔
110
    }
111

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

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

122
    static string_fragment from_byte_range(const char* bytes,
173,891,870✔
123
                                           size_t begin,
124
                                           size_t end)
125
    {
126
        return string_fragment{bytes, (int) begin, (int) end};
173,891,870✔
127
    }
128

129
    constexpr string_fragment() : sf_string(nullptr), sf_begin(0), sf_end(0) {}
9,063,871✔
130

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

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

149
    string_fragment(const std::string& str)
273,002,878✔
150
        : sf_string(str.c_str()), sf_begin(0), sf_end(str.length())
273,002,878✔
151
    {
152
    }
273,002,878✔
153

154
    constexpr bool is_valid() const
160,286,815✔
155
    {
156
        return this->sf_begin != -1 && this->sf_begin <= this->sf_end;
160,286,815✔
157
    }
158

159
    constexpr int length() const { return this->sf_end - this->sf_begin; }
968,818,742✔
160

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

163
    size_t column_to_byte_index(size_t col) const;
164

165
    size_t byte_to_column_index(size_t byte_index) const;
166

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

176
    size_t column_width() const;
177

178
    constexpr const char* data() const
44,015,136✔
179
    {
180
        return &this->sf_string[this->sf_begin];
44,015,136✔
181
    }
182

183
    const unsigned char* udata() const
280,611,241✔
184
    {
185
        return (const unsigned char*) &this->sf_string[this->sf_begin];
280,611,241✔
186
    }
187

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

193
    constexpr char front() const { return this->sf_string[this->sf_begin]; }
138,202✔
194

195
    uint32_t front_codepoint() const;
196

197
    constexpr char back() const { return this->sf_string[this->sf_end - 1]; }
3,229✔
198

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

206
    iterator begin() const { return &this->sf_string[this->sf_begin]; }
50,355,099✔
207

208
    iterator end() const { return &this->sf_string[this->sf_end]; }
50,659,506✔
209

210
    constexpr bool empty() const { return !this->is_valid() || length() == 0; }
151,821,078✔
211

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

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

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

222
    bool operator==(const std::string& str) const
10,093✔
223
    {
224
        if (this->length() != (int) str.length()) {
10,093✔
225
            return false;
8,301✔
226
        }
227

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

233
    bool operator==(const string_fragment& sf) const
1,845,711✔
234
    {
235
        if (this->length() != sf.length()) {
1,845,711✔
236
            return false;
915,393✔
237
        }
238

239
        return memcmp(this->data(), sf.data(), sf.length()) == 0;
930,318✔
240
    }
241

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

247
    bool operator<(const string_fragment& rhs) const
3,385,202✔
248
    {
249
        auto rc = strncmp(
3,385,202✔
250
            this->data(), rhs.data(), std::min(this->length(), rhs.length()));
3,385,202✔
251
        if (rc < 0 || (rc == 0 && this->length() < rhs.length())) {
3,385,202✔
252
            return true;
2,034,264✔
253
        }
254

255
        return false;
1,350,938✔
256
    }
257

258
    bool iequal(const string_fragment& sf) const
247,172,860✔
259
    {
260
        if (this->length() != sf.length()) {
247,172,860✔
261
            return false;
246,284,727✔
262
        }
263

264
        return strnatcasecmp(
888,133✔
265
                   this->length(), this->data(), sf.length(), sf.data())
266
            == 0;
888,133✔
267
    }
268

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

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

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

284
    bool startswith(const char* prefix) const
38,093,985✔
285
    {
286
        const auto* iter = this->begin();
38,093,985✔
287

288
        while (*prefix != '\0' && iter < this->end() && *prefix == *iter) {
70,529,282✔
289
            prefix += 1;
32,435,297✔
290
            iter += 1;
32,435,297✔
291
        }
292

293
        return *prefix == '\0';
38,093,985✔
294
    }
295

296
    bool endswith(const char* suffix) const
139,543✔
297
    {
298
        int suffix_len = strlen(suffix);
139,543✔
299

300
        if (suffix_len > this->length()) {
139,543✔
301
            return false;
66✔
302
        }
303

304
        const auto* curr = this->end() - suffix_len;
139,477✔
305
        while (*suffix != '\0' && *curr == *suffix) {
142,279✔
306
            suffix += 1;
2,802✔
307
            curr += 1;
2,802✔
308
        }
309

310
        return *suffix == '\0';
139,477✔
311
    }
312

313
    constexpr string_fragment substr(int begin) const
3,658,699✔
314
    {
315
        return string_fragment{
7,317,398✔
316
            this->sf_string, this->sf_begin + begin, this->sf_end};
3,658,699✔
317
    }
318

319
    string_fragment sub_range(int begin, int end) const
195,451,563✔
320
    {
321
        if (this->sf_begin + begin > this->sf_end) {
195,451,563✔
322
            begin = this->sf_end - this->sf_begin;
×
323
        }
324
        if (this->sf_begin + end > this->sf_end) {
195,451,563✔
325
            end = this->sf_end - this->sf_begin;
3,148✔
326
        }
327
        return string_fragment{
390,903,126✔
328
            this->sf_string, this->sf_begin + begin, this->sf_begin + end};
195,451,563✔
329
    }
330

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

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

341
        for (int lpc = this->sf_begin; lpc < this->sf_end; lpc++) {
24,087✔
342
            if (this->sf_string[lpc] == ch) {
23,890✔
343
                retval += 1;
583✔
344
            }
345
        }
346

347
        return retval;
197✔
348
    }
349

350
    std::optional<int> find(char ch) const
12,214✔
351
    {
352
        for (int lpc = this->sf_begin; lpc < this->sf_end; lpc++) {
537,571✔
353
            if (this->sf_string[lpc] == ch) {
529,691✔
354
                return lpc - this->sf_begin;
4,334✔
355
            }
356
        }
357

358
        return std::nullopt;
7,880✔
359
    }
360

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

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

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

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

377
        if (start > 0 && start == static_cast<size_t>(this->length())) {
11,994✔
378
            start -= 1;
12✔
379
        }
380
        while (true) {
381
            if (predicate(this->data()[start])) {
73,713✔
382
                count -= 1;
11,947✔
383
                if (count == 0) {
11,947✔
384
                    start += 1;
11,943✔
385
                    break;
11,943✔
386
                }
387
            } else if (start == 0) {
61,766✔
388
                break;
51✔
389
            }
390
            start -= 1;
61,719✔
391
        }
392

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

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

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

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

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

433
    std::optional<std::pair<uint32_t, string_fragment>> consume_codepoint()
4,053,724✔
434
        const
435
    {
436
        auto cp = this->front_codepoint();
4,053,724✔
437
        auto index_res = this->codepoint_to_byte_index(1);
4,053,724✔
438

439
        if (index_res.isErr()) {
4,053,724✔
440
            return std::nullopt;
429,043✔
441
        }
442

443
        return std::make_pair(cp, this->substr(index_res.unwrap()));
3,624,681✔
444
    }
4,053,724✔
445

446
    template<typename P>
447
    std::optional<string_fragment> consume(P predicate) const
372✔
448
    {
449
        int consumed = 0;
372✔
450
        while (consumed < this->length()) {
742✔
451
            if (!predicate(this->data()[consumed])) {
727✔
452
                break;
357✔
453
            }
454

455
            consumed += 1;
370✔
456
        }
457

458
        if (consumed == 0) {
372✔
459
            return std::nullopt;
3✔
460
        }
461

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

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

471
    template<typename P>
472
    string_fragment skip(P predicate) const
35,043✔
473
    {
474
        int offset = 0;
35,043✔
475
        while (offset < this->length() && predicate(this->data()[offset])) {
46,109✔
476
            offset += 1;
11,066✔
477
        }
478

479
        return string_fragment{
480
            this->sf_string,
35,043✔
481
            this->sf_begin + offset,
35,043✔
482
            this->sf_end,
35,043✔
483
        };
35,043✔
484
    }
485

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

489
    template<typename P>
490
    split_result split_while(P&& predicate) const
3,975✔
491
    {
492
        int consumed = 0;
3,975✔
493
        while (consumed < this->length()) {
501,563✔
494
            if (!predicate(this->data()[consumed])) {
501,398✔
495
                break;
3,810✔
496
            }
497

498
            consumed += 1;
497,588✔
499
        }
500

501
        if (consumed == 0) {
3,975✔
502
            return std::nullopt;
26✔
503
        }
504

505
        return std::make_pair(
7,898✔
506
            string_fragment{
507
                this->sf_string,
3,949✔
508
                this->sf_begin,
3,949✔
509
                this->sf_begin + consumed,
3,949✔
510
            },
511
            string_fragment{
512
                this->sf_string,
3,949✔
513
                this->sf_begin + consumed,
3,949✔
514
                this->sf_end,
3,949✔
515
            });
3,949✔
516
    }
517

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

520
    template<typename P>
521
    split_when_result split_when(P&& predicate) const
33,739✔
522
    {
523
        int consumed = 0;
33,739✔
524
        while (consumed < this->length()) {
3,033,622✔
525
            if (predicate(this->data()[consumed])) {
3,014,876✔
526
                break;
14,993✔
527
            }
528

529
            consumed += 1;
2,999,883✔
530
        }
531

532
        return std::make_pair(
67,478✔
533
            string_fragment{
534
                this->sf_string,
33,739✔
535
                this->sf_begin,
33,739✔
536
                this->sf_begin + consumed,
33,739✔
537
            },
538
            string_fragment{
539
                this->sf_string,
33,739✔
540
                this->sf_begin + consumed
33,739✔
541
                    + ((consumed == this->length()) ? 0 : 1),
33,739✔
542
                this->sf_end,
33,739✔
543
            });
67,478✔
544
    }
545

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

555
            consumed += 1;
81,260✔
556
        }
557

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

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

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

582
        auto curr = this->sf_end - 1;
2,036,161✔
583
        while (curr >= this->sf_begin) {
21,379,203✔
584
            if (predicate(this->sf_string[curr])) {
21,379,202✔
585
                return std::make_pair(
4,072,320✔
586
                    string_fragment{
587
                        this->sf_string,
2,036,160✔
588
                        this->sf_begin,
2,036,160✔
589
                        curr,
590
                    },
591
                    string_fragment{
592
                        this->sf_string,
2,036,160✔
593
                        curr + 1,
594
                        this->sf_end,
2,036,160✔
595
                    });
2,036,160✔
596
            }
597

598
            curr -= 1;
19,343,042✔
599
        }
600

601
        return std::nullopt;
1✔
602
    }
603

604
    template<typename P>
605
    split_when_result rsplit_when(P&& predicate) const
7,579✔
606
    {
607
        if (this->empty()) {
7,579✔
NEW
608
            return std::make_pair(string_fragment{}, string_fragment{});
×
609
        }
610

611
        auto curr = this->sf_end - 1;
7,579✔
612
        while (curr >= this->sf_begin) {
40,363✔
613
            if (predicate(this->sf_string[curr])) {
36,390✔
614
                return std::make_pair(
7,212✔
615
                    string_fragment{
616
                        this->sf_string,
3,606✔
617
                        this->sf_begin,
3,606✔
618
                        curr + 1,
619
                    },
620
                    string_fragment{
621
                        this->sf_string,
3,606✔
622
                        curr + 1,
623
                        this->sf_end,
3,606✔
624
                    });
3,606✔
625
            }
626

627
            curr -= 1;
32,784✔
628
        }
629

630
        return std::make_pair(string_fragment{}, *this);
3,973✔
631
    }
632

633
    using split_n_result = std::pair<string_fragment, string_fragment>;
634

635
    split_n_result split_n(int amount) const;
636

637
    std::vector<string_fragment> split_lines() const;
638

639
    struct tag1 {
640
        const char t_value;
641

642
        constexpr explicit tag1(const char value) : t_value(value) {}
2,081,414✔
643

644
        constexpr bool operator()(char ch) const { return this->t_value == ch; }
24,541,689✔
645
    };
646

647
    struct quoted_string_body {
648
        bool qs_in_escape{false};
649

650
        bool operator()(char ch)
6,922✔
651
        {
652
            if (this->qs_in_escape) {
6,922✔
653
                this->qs_in_escape = false;
1✔
654
                return true;
1✔
655
            }
656
            if (ch == '\\') {
6,921✔
657
                this->qs_in_escape = true;
1✔
658
                return true;
1✔
659
            }
660
            if (ch == '"') {
6,920✔
661
                return false;
368✔
662
            }
663
            return true;
6,552✔
664
        }
665
    };
666

667
    const char* to_string(char* buf) const
668
    {
669
        memcpy(buf, this->data(), this->length());
670
        buf[this->length()] = '\0';
671

672
        return buf;
673
    }
674

675
    std::string to_string() const
11,380,537✔
676
    {
677
        if (!this->is_valid()) {
11,380,537✔
678
            return "<invalid>";
×
679
        }
680

681
        return {this->data(), (size_t) this->length()};
34,141,611✔
682
    }
683

684
    template<typename OutputIt>
685
    void to_hex_string(OutputIt out) const
686
    {
687
        for (const auto ch : *this) {
688
            fmt::format_to(out, FMT_STRING("{:02x}"), ch);
689
        }
690
    }
691

692
    std::string to_unquoted_string() const;
693

694
    void clear()
30,745✔
695
    {
696
        this->sf_begin = 0;
30,745✔
697
        this->sf_end = 0;
30,745✔
698
    }
30,745✔
699

700
    constexpr void invalidate()
281,515✔
701
    {
702
        this->sf_begin = -1;
281,515✔
703
        this->sf_end = -1;
281,515✔
704
    }
281,515✔
705

706
    string_fragment trim(const char* tokens) const;
707
    string_fragment rtrim(const char* tokens) const;
708
    string_fragment trim() const;
709

710
    string_fragment prepend(const char* str, int amount) const
7,530✔
711
    {
712
        return string_fragment{
15,060✔
713
            str,
714
            this->sf_begin + amount,
7,530✔
715
            this->sf_end + amount,
7,530✔
716
        };
7,530✔
717
    }
718

719
    string_fragment erase_before(const char* str, int amount) const
2,287✔
720
    {
721
        return string_fragment{
4,574✔
722
            str,
723
            this->sf_begin - amount,
2,287✔
724
            this->sf_end - amount,
2,287✔
725
        };
2,287✔
726
    }
727

728
    string_fragment erase(const char* str, int amount) const
729
    {
730
        return string_fragment{
731
            str,
732
            this->sf_begin,
733
            this->sf_end - amount,
734
        };
735
    }
736

737
    template<typename A>
738
    const char* to_c_str(A allocator) const
108,730✔
739
    {
740
        auto* retval = allocator.allocate(this->length() + 1);
108,730✔
741
        memcpy(retval, this->data(), this->length());
108,730✔
742
        retval[this->length()] = '\0';
108,730✔
743
        return retval;
108,730✔
744
    }
745

746
    template<typename A>
747
    string_fragment to_owned(A allocator) const
108,730✔
748
    {
749
        return string_fragment{
750
            this->to_c_str(allocator),
751
            0,
752
            this->length(),
753
        };
108,730✔
754
    }
755

756
    std::string_view to_string_view() const
51,768✔
757
    {
758
        return std::string_view{
103,536✔
759
            this->data(),
760
            static_cast<std::string_view::size_type>(this->length())};
103,536✔
761
    }
762

763
    enum class case_style : uint8_t {
764
        lower,
765
        upper,
766
        camel,
767
        mixed,
768
    };
769

770
    case_style detect_text_case_style() const;
771

772
    std::string to_string_with_case_style(case_style style) const;
773

774
    unsigned long hash() const
775
    {
776
        return hash_str(this->data(), this->length());
777
    }
778

779
    uint64_t bloom_bits() const;
780

781
    const char* sf_string;
782
    int32_t sf_begin;
783
    int32_t sf_end;
784
};
785

786
inline bool
787
operator==(const std::string& left, const string_fragment& right)
1,059✔
788
{
789
    return right == left;
1,059✔
790
}
791

792
inline bool
793
operator<(const char* left, const string_fragment& right)
794
{
795
    int rc = strncmp(left, right.data(), right.length());
796
    return rc < 0;
797
}
798

799
inline void
800
operator+=(std::string& left, const string_fragment& right)
56,794✔
801
{
802
    left.append(right.data(), right.length());
56,794✔
803
}
56,794✔
804

805
inline bool
806
operator<(const string_fragment& left, const char* right)
807
{
808
    return strncmp(left.data(), right, left.length()) < 0;
809
}
810

811
inline std::ostream&
812
operator<<(std::ostream& os, const string_fragment& sf)
2,409✔
813
{
814
    os.write(sf.data(), sf.length());
2,409✔
815
    return os;
2,409✔
816
}
817

818
class string_fragment_producer {
819
public:
820
    struct eof {};
821
    struct error {
822
        std::string what;
823
    };
824
    using next_result = mapbox::util::variant<eof, string_fragment, error>;
825
    static std::unique_ptr<string_fragment_producer> from(string_fragment sf);
826

827
    Result<void, std::string> for_each(
18,389✔
828
        std::function<Result<void, std::string>(string_fragment)> cb)
829
    {
830
        while (true) {
831
            auto next_res = this->next();
46,487✔
832
            if (next_res.is<error>()) {
46,487✔
833
                auto err = next_res.get<error>();
×
834

835
                return Err(err.what);
×
836
            }
837

838
            if (next_res.is<eof>()) {
46,487✔
839
                break;
18,389✔
840
            }
841

842
            const auto sf = next_res.get<string_fragment>();
28,098✔
843
            auto cb_res = cb(sf);
28,098✔
844

845
            if (cb_res.isErr()) {
28,098✔
846
                return Err(cb_res.unwrapErr());
×
847
            }
848
        }
74,585✔
849

850
        return Ok();
18,389✔
851
    }
852

853
    virtual ~string_fragment_producer() {}
97,661✔
854

855
    virtual size_t estimated_size() const { return 1024; }
×
856

857
    virtual next_result next() = 0;
858

859
    std::string to_string();
860
};
861

862
class intern_string {
863
public:
864
    static const intern_string* lookup(const char* str, ssize_t len) noexcept;
865

866
    static const intern_string* lookup(const string_fragment& sf) noexcept;
867

868
    static const intern_string* lookup(const std::string& str) noexcept;
869

870
    const char* get() const { return this->is_str.c_str(); };
132,833,960✔
871

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

874
    size_t size() const { return this->is_str.size(); }
3,315,771✔
875

876
    std::string to_string() const { return this->is_str; }
1,580,571✔
877

878
    string_fragment to_string_fragment() const
1,750,466✔
879
    {
880
        return string_fragment{this->is_str};
1,750,466✔
881
    }
882

883
    bool startswith(const char* prefix) const;
884

885
    struct intern_table;
886
    static std::shared_ptr<intern_table> get_table_lifetime();
887

888
private:
889
    friend intern_table;
890

891
    intern_string(const char* str, ssize_t len)
3,009,696✔
892
        : is_next(nullptr), is_str(str, (size_t) len)
6,019,392✔
893
    {
894
    }
3,009,696✔
895

896
    intern_string* is_next;
897
    std::string is_str;
898
};
899

900
using intern_table_lifetime = std::shared_ptr<intern_string::intern_table>;
901

902
class intern_string_t {
903
public:
904
    using iterator = const char*;
905

906
    intern_string_t(const intern_string* is = nullptr) : ist_interned_string(is)
24,290,254✔
907
    {
908
    }
24,290,254✔
909

910
    const intern_string* unwrap() const { return this->ist_interned_string; }
911

912
    void clear() { this->ist_interned_string = nullptr; };
41,512✔
913

914
    bool empty() const { return this->ist_interned_string == nullptr; }
143,050,674✔
915

916
    const char* get() const
132,861,026✔
917
    {
918
        if (this->empty()) {
132,861,026✔
919
            return "";
36,274✔
920
        }
921
        return this->ist_interned_string->get();
132,824,752✔
922
    }
923

924
    const char* c_str() const { return this->get(); }
1,066,278✔
925

926
    const char* data() const { return this->get(); }
×
927

928
    iterator begin() const { return this->get(); }
929

930
    iterator end() const { return this->get() + this->size(); }
931

932
    size_t size() const
3,315,786✔
933
    {
934
        if (this->ist_interned_string == nullptr) {
3,315,786✔
935
            return 0;
15✔
936
        }
937
        return this->ist_interned_string->size();
3,315,771✔
938
    }
939

940
    size_t hash() const
4,979,507✔
941
    {
942
        auto ptr = (uintptr_t) this->ist_interned_string;
4,979,507✔
943

944
        return ptr;
4,979,507✔
945
    }
946

947
    std::string to_string() const
1,580,618✔
948
    {
949
        if (this->ist_interned_string == nullptr) {
1,580,618✔
950
            return "";
94✔
951
        }
952
        return this->ist_interned_string->to_string();
1,580,571✔
953
    }
954

955
    string_fragment to_string_fragment() const
1,750,466✔
956
    {
957
        if (this->ist_interned_string == nullptr) {
1,750,466✔
958
            return string_fragment{"", 0, 0};
×
959
        }
960
        return this->ist_interned_string->to_string_fragment();
1,750,466✔
961
    }
962

963
    bool operator<(const intern_string_t& rhs) const
64,415,075✔
964
    {
965
        return strcmp(this->get(), rhs.get()) < 0;
64,415,075✔
966
    }
967

968
    bool operator==(const intern_string_t& rhs) const
148,271,401✔
969
    {
970
        return this->ist_interned_string == rhs.ist_interned_string;
148,271,401✔
971
    }
972

973
    bool operator!=(const intern_string_t& rhs) const
27,077✔
974
    {
975
        return !(*this == rhs);
27,077✔
976
    }
977

978
    bool operator==(const char* rhs) const
53,521✔
979
    {
980
        return strcmp(this->get(), rhs) == 0;
53,521✔
981
    }
982

983
    bool operator!=(const char* rhs) const
6,931✔
984
    {
985
        return strcmp(this->get(), rhs) != 0;
6,931✔
986
    }
987

988
    static bool case_lt(const intern_string_t& lhs, const intern_string_t& rhs)
989
    {
990
        return strnatcasecmp(lhs.size(), lhs.get(), rhs.size(), rhs.get()) < 0;
×
991
    }
992

993
private:
994
    const intern_string* ist_interned_string;
995
};
996

997
namespace fmt {
998
template<>
999
struct formatter<string_fragment> : formatter<string_view> {
1000
    template<typename FormatContext>
1001
    auto format(const string_fragment& sf, FormatContext& ctx) const
2,227,556✔
1002
    {
1003
        return formatter<string_view>::format(
2,227,556✔
1004
            string_view{sf.data(), (size_t) sf.length()}, ctx);
4,455,112✔
1005
    }
1006
};
1007

1008
template<>
1009
struct formatter<intern_string_t> : formatter<string_view> {
1010
    template<typename FormatContext>
1011
    auto format(const intern_string_t& is, FormatContext& ctx)
1,080,066✔
1012
    {
1013
        return formatter<string_view>::format(string_view{is.get(), is.size()},
2,160,132✔
1014
                                              ctx);
2,160,132✔
1015
    }
1016
};
1017

1018
template<>
1019
struct formatter<std::error_code> : formatter<string_view> {
1020
    template<typename FormatContext>
1021
    auto format(const std::error_code& ec, FormatContext& ctx)
16,352✔
1022
    {
1023
        return formatter<string_view>::format(ec.message(), ctx);
32,704✔
1024
    }
1025
};
1026
}  // namespace fmt
1027

1028
namespace std {
1029
template<>
1030
struct hash<const intern_string_t> {
1031
    std::size_t operator()(const intern_string_t& ist) const noexcept
4,979,507✔
1032
    {
1033
        return ist.hash();
4,979,507✔
1034
    }
1035
};
1036
}  // namespace std
1037

1038
inline bool
1039
operator<(const char* left, const intern_string_t& right)
1,622✔
1040
{
1041
    int rc = strncmp(left, right.get(), right.size());
1,622✔
1042
    return rc < 0;
1,622✔
1043
}
1044

1045
inline bool
1046
operator<(const intern_string_t& left, const char* right)
454✔
1047
{
1048
    return strncmp(left.get(), right, left.size()) < 0;
454✔
1049
}
1050

1051
inline bool
1052
operator==(const intern_string_t& left, const string_fragment& sf)
×
1053
{
1054
    return ((int) left.size() == sf.length())
×
1055
        && (memcmp(left.get(), sf.data(), left.size()) == 0);
×
1056
}
1057

1058
inline bool
1059
operator<(const intern_string_t& left, const string_fragment& sf)
1060
{
1061
    return left.to_string_fragment() < sf;
1062
}
1063

1064
inline bool
1065
operator<(const string_fragment& lhs, const intern_string_t& rhs)
1066
{
1067
    return lhs < rhs.to_string_fragment();
1068
}
1069

1070
inline bool
1071
operator==(const string_fragment& left, const intern_string_t& right)
1,273,752✔
1072
{
1073
    return (left.length() == (int) right.size())
1,273,752✔
1074
        && (memcmp(left.data(), right.get(), left.length()) == 0);
1,273,752✔
1075
}
1076

1077
constexpr string_fragment
1078
operator""_frag(const char* str, std::size_t len)
124,188✔
1079
{
1080
    return string_fragment{str, 0, (int) len};
124,188✔
1081
}
1082

1083
namespace std {
1084
inline string
1085
to_string(const string_fragment& s)
10,554✔
1086
{
1087
    return {s.data(), (size_t) s.length()};
31,662✔
1088
}
1089

1090
inline string
1091
to_string(const intern_string_t& s)
×
1092
{
1093
    return s.to_string();
×
1094
}
1095
}  // namespace std
1096

1097
inline string_fragment
1098
to_string_fragment(const string_fragment& s)
1099
{
1100
    return s;
1101
}
1102

1103
inline string_fragment
1104
to_string_fragment(const intern_string_t& s)
1105
{
1106
    return string_fragment(s.get(), 0, s.size());
1107
}
1108

1109
inline string_fragment
1110
to_string_fragment(const std::string& s)
×
1111
{
1112
    return string_fragment(s.c_str(), 0, s.length());
×
1113
}
1114

1115
inline string_fragment
1116
to_string_fragment(const std::string_view& sv)
1117
{
1118
    return string_fragment::from_bytes(sv.data(), sv.length());
1119
}
1120

1121
struct frag_hasher {
1122
    size_t operator()(const string_fragment& sf) const
1,136,017✔
1123
    {
1124
        return hash_str(sf.data(), sf.length());
1,136,017✔
1125
    }
1126
};
1127

1128
struct intern_hasher {
1129
    size_t operator()(const intern_string_t& is) const
958,306✔
1130
    {
1131
        return hash_str(is.c_str(), is.size());
958,306✔
1132
    }
1133
};
1134

1135
namespace lnav {
1136
inline std::error_code
1137
from_errno()
17,109✔
1138
{
1139
    return std::error_code{errno, std::generic_category()};
17,109✔
1140
}
1141
}  // namespace lnav
1142

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