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

tstack / lnav / 25348852825-3024

04 May 2026 11:18PM UTC coverage: 69.963% (+0.7%) from 69.226%
25348852825-3024

push

github

tstack
[ui] horizontal scroll should work on columns

Related to #1685

7 of 141 new or added lines in 5 files covered. (4.96%)

7760 existing lines in 84 files now uncovered.

57014 of 81492 relevant lines covered (69.96%)

622491.44 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 <functional>
36
#include <optional>
37
#include <ostream>
38
#include <string>
39
#include <string_view>
40
#include <system_error>
41
#include <tuple>
42
#include <vector>
43

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

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

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

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

58
    static constexpr string_fragment invalid()
257,875✔
59
    {
60
        string_fragment retval;
257,875✔
61

62
        retval.invalidate();
257,875✔
63
        return retval;
257,875✔
64
    }
65

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

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

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

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

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

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

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

108
    static string_fragment from_bytes(const char* bytes, size_t len)
35,239,158✔
109
    {
110
        return string_fragment{bytes, 0, (int) len};
35,239,158✔
111
    }
112

113
    static string_fragment from_bytes(const unsigned char* bytes, size_t len)
9,351,268✔
114
    {
115
        return string_fragment{(const char*) bytes, 0, (int) len};
9,351,268✔
116
    }
117

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

123
    static string_fragment from_byte_range(const char* bytes,
249,485,452✔
124
                                           size_t begin,
125
                                           size_t end)
126
    {
127
        return string_fragment{bytes, (int) begin, (int) end};
249,485,452✔
128
    }
129

130
    constexpr string_fragment() : sf_string(nullptr), sf_begin(0), sf_end(0) {}
27,262,612✔
131

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

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

150
    string_fragment(const std::string& str)
455,343,338✔
151
        : sf_string(str.c_str()), sf_begin(0), sf_end(str.length())
455,343,338✔
152
    {
153
    }
455,343,338✔
154

155
    constexpr bool is_valid() const
230,465,858✔
156
    {
157
        return this->sf_begin != -1 && this->sf_begin <= this->sf_end;
230,465,858✔
158
    }
159

160
    constexpr int length() const { return this->sf_end - this->sf_begin; }
1,524,807,300✔
161

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

164
    size_t column_to_byte_index(size_t col) const;
165

166
    size_t byte_to_column_index(size_t byte_index) const;
167

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

177
    size_t column_width() const;
178

179
    constexpr const char* data() const
74,486,237✔
180
    {
181
        return &this->sf_string[this->sf_begin];
74,486,237✔
182
    }
183

184
    const unsigned char* udata() const
415,716,679✔
185
    {
186
        return (const unsigned char*) &this->sf_string[this->sf_begin];
415,716,679✔
187
    }
188

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

194
    constexpr char front() const { return this->sf_string[this->sf_begin]; }
174,572✔
195

196
    uint32_t front_codepoint() const;
197

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

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

207
    iterator begin() const { return &this->sf_string[this->sf_begin]; }
99,683,432✔
208

209
    iterator end() const { return &this->sf_string[this->sf_end]; }
100,242,262✔
210

211
    constexpr bool empty() const { return !this->is_valid() || length() == 0; }
219,575,168✔
212

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

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

218
    constexpr const char& operator[](size_t index) const
3,715,327✔
219
    {
220
        return this->sf_string[sf_begin + index];
3,715,327✔
221
    }
222

223
    bool operator==(const std::string& str) const
11,438✔
224
    {
225
        if (this->length() != (int) str.length()) {
11,438✔
226
            return false;
9,507✔
227
        }
228

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

234
    bool operator==(const string_fragment& sf) const
2,449,413✔
235
    {
236
        if (this->length() != sf.length()) {
2,449,413✔
237
            return false;
1,216,316✔
238
        }
239

240
        return memcmp(this->data(), sf.data(), sf.length()) == 0;
1,233,097✔
241
    }
242

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

248
    bool operator<(const string_fragment& rhs) const
4,516,947✔
249
    {
250
        auto rc = strncmp(
4,516,947✔
251
            this->data(), rhs.data(), std::min(this->length(), rhs.length()));
4,516,947✔
252
        if (rc < 0 || (rc == 0 && this->length() < rhs.length())) {
4,516,947✔
253
            return true;
2,729,415✔
254
        }
255

256
        return false;
1,787,532✔
257
    }
258

259
    bool iequal(const string_fragment& sf) const
398,553,693✔
260
    {
261
        if (this->length() != sf.length()) {
398,553,693✔
262
            return false;
397,322,664✔
263
        }
264

265
        return strnatcasecmp(
1,231,029✔
266
                   this->length(), this->data(), sf.length(), sf.data())
267
            == 0;
1,231,029✔
268
    }
269

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

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

279
    template<typename... Args>
280
    bool is_one_of(Args... args) const
9,110✔
281
    {
282
        return (this->operator==(args) || ...);
10,928✔
283
    }
284

285
    bool startswith(const char* prefix) const
51,809,819✔
286
    {
287
        const auto* iter = this->begin();
51,809,819✔
288

289
        while (*prefix != '\0' && iter < this->end() && *prefix == *iter) {
95,802,783✔
290
            prefix += 1;
43,992,964✔
291
            iter += 1;
43,992,964✔
292
        }
293

294
        return *prefix == '\0';
51,809,819✔
295
    }
296

297
    bool endswith(const char* suffix) const
392,829✔
298
    {
299
        int suffix_len = strlen(suffix);
392,829✔
300

301
        if (suffix_len > this->length()) {
392,829✔
302
            return false;
7,399✔
303
        }
304

305
        const auto* curr = this->end() - suffix_len;
385,430✔
306
        while (*suffix != '\0' && *curr == *suffix) {
389,667✔
307
            suffix += 1;
4,237✔
308
            curr += 1;
4,237✔
309
        }
310

311
        return *suffix == '\0';
385,430✔
312
    }
313

314
    constexpr string_fragment substr(int begin) const
4,506,417✔
315
    {
316
        return string_fragment{
9,012,834✔
317
            this->sf_string, this->sf_begin + begin, this->sf_end};
4,506,417✔
318
    }
319

320
    string_fragment sub_range(int begin, int end) const
286,414,357✔
321
    {
322
        if (this->sf_begin + begin > this->sf_end) {
286,414,357✔
UNCOV
323
            begin = this->sf_end - this->sf_begin;
×
324
        }
325
        if (this->sf_begin + end > this->sf_end) {
286,414,357✔
326
            end = this->sf_end - this->sf_begin;
4,186✔
327
        }
328
        return string_fragment{
572,828,714✔
329
            this->sf_string, this->sf_begin + begin, this->sf_begin + end};
286,414,357✔
330
    }
331

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

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

342
        for (int lpc = this->sf_begin; lpc < this->sf_end; lpc++) {
24,717✔
343
            if (this->sf_string[lpc] == ch) {
24,518✔
344
                retval += 1;
589✔
345
            }
346
        }
347

348
        return retval;
199✔
349
    }
350

351
    std::optional<int> find(char ch) const
18,044✔
352
    {
353
        for (int lpc = this->sf_begin; lpc < this->sf_end; lpc++) {
717,438✔
354
            if (this->sf_string[lpc] == ch) {
704,511✔
355
                return lpc - this->sf_begin;
5,117✔
356
            }
357
        }
358

359
        return std::nullopt;
12,927✔
360
    }
361

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

364
    std::optional<int> next_word(int start_col) const;
365
    std::optional<int> prev_word(int start_col) const;
366
    std::optional<int> curr_word(int start_col) const;
367

368
    std::string transform_codepoints(
369
        const std::function<uint32_t(uint32_t)>& xform) const;
370

371
    template<typename P>
372
    string_fragment find_left_boundary(size_t start,
16,119✔
373
                                       P&& predicate,
374
                                       size_t count = 1) const
375
    {
376
        assert((int) start <= this->length());
377

378
        if (this->empty()) {
16,119✔
UNCOV
379
            return *this;
×
380
        }
381

382
        if (start > 0 && start == static_cast<size_t>(this->length())) {
16,119✔
383
            start -= 1;
12✔
384
        }
385
        while (true) {
386
            if (predicate(this->data()[start])) {
96,363✔
387
                count -= 1;
16,067✔
388
                if (count == 0) {
16,067✔
389
                    start += 1;
16,063✔
390
                    break;
16,063✔
391
                }
392
            } else if (start == 0) {
80,296✔
393
                break;
56✔
394
            }
395
            start -= 1;
80,244✔
396
        }
397

398
        return string_fragment{
399
            this->sf_string,
16,119✔
400
            this->sf_begin + (int) start,
16,119✔
401
            this->sf_end,
16,119✔
402
        };
16,119✔
403
    }
404

405
    template<typename P>
406
    string_fragment find_right_boundary(size_t start,
23✔
407
                                        P&& predicate,
408
                                        size_t count = 1) const
409
    {
410
        while ((int) start < this->length()) {
203✔
411
            if (predicate(this->data()[start])) {
188✔
412
                count -= 1;
12✔
413
                if (count == 0) {
12✔
414
                    break;
8✔
415
                }
416
            }
417
            start += 1;
180✔
418
        }
419

420
        return string_fragment{
421
            this->sf_string,
23✔
422
            this->sf_begin,
23✔
423
            this->sf_begin + (int) start,
23✔
424
        };
23✔
425
    }
426

427
    template<typename P>
428
    string_fragment find_boundaries_around(size_t start,
11✔
429
                                           P&& predicate,
430
                                           size_t count = 1) const
431
    {
432
        auto left = this->find_left_boundary(start, predicate, count);
11✔
433

434
        return left.find_right_boundary(
11✔
435
            start - left.sf_begin, predicate, count);
22✔
436
    }
437

438
    std::optional<std::pair<uint32_t, string_fragment>> consume_codepoint()
4,965,107✔
439
        const
440
    {
441
        auto cp = this->front_codepoint();
4,965,107✔
442
        auto index_res = this->codepoint_to_byte_index(1);
4,965,107✔
443

444
        if (index_res.isErr()) {
4,965,107✔
445
            return std::nullopt;
517,281✔
446
        }
447

448
        return std::make_pair(cp, this->substr(index_res.unwrap()));
4,447,826✔
449
    }
4,965,107✔
450

451
    template<typename P>
452
    std::optional<string_fragment> consume(P predicate) const
399✔
453
    {
454
        int consumed = 0;
399✔
455
        while (consumed < this->length()) {
796✔
456
            if (!predicate(this->data()[consumed])) {
776✔
457
                break;
379✔
458
            }
459

460
            consumed += 1;
397✔
461
        }
462

463
        if (consumed == 0) {
399✔
464
            return std::nullopt;
3✔
465
        }
466

467
        return string_fragment{
468
            this->sf_string,
396✔
469
            this->sf_begin + consumed,
396✔
470
            this->sf_end,
396✔
471
        };
396✔
472
    }
473

474
    std::optional<string_fragment> consume_n(int amount) const;
475

476
    template<typename P>
477
    string_fragment skip(P predicate) const
41,441✔
478
    {
479
        int offset = 0;
41,441✔
480
        while (offset < this->length() && predicate(this->data()[offset])) {
53,461✔
481
            offset += 1;
12,020✔
482
        }
483

484
        return string_fragment{
485
            this->sf_string,
41,441✔
486
            this->sf_begin + offset,
41,441✔
487
            this->sf_end,
41,441✔
488
        };
41,441✔
489
    }
490

491
    using split_result
492
        = std::optional<std::pair<string_fragment, string_fragment>>;
493

494
    template<typename P>
495
    split_result split_while(P&& predicate) const
4,417✔
496
    {
497
        int consumed = 0;
4,417✔
498
        while (consumed < this->length()) {
581,702✔
499
            if (!predicate(this->data()[consumed])) {
581,456✔
500
                break;
4,171✔
501
            }
502

503
            consumed += 1;
577,285✔
504
        }
505

506
        if (consumed == 0) {
4,417✔
507
            return std::nullopt;
26✔
508
        }
509

510
        return std::make_pair(
8,782✔
511
            string_fragment{
512
                this->sf_string,
4,391✔
513
                this->sf_begin,
4,391✔
514
                this->sf_begin + consumed,
4,391✔
515
            },
516
            string_fragment{
517
                this->sf_string,
4,391✔
518
                this->sf_begin + consumed,
4,391✔
519
                this->sf_end,
4,391✔
520
            });
4,391✔
521
    }
522

523
    using split_when_result = std::pair<string_fragment, string_fragment>;
524

525
    template<typename P>
526
    split_when_result split_when(P&& predicate) const
41,258✔
527
    {
528
        int consumed = 0;
41,258✔
529
        while (consumed < this->length()) {
3,466,936✔
530
            if (predicate(this->data()[consumed])) {
3,442,978✔
531
                break;
17,300✔
532
            }
533

534
            consumed += 1;
3,425,678✔
535
        }
536

537
        return std::make_pair(
82,516✔
538
            string_fragment{
539
                this->sf_string,
41,258✔
540
                this->sf_begin,
41,258✔
541
                this->sf_begin + consumed,
41,258✔
542
            },
543
            string_fragment{
544
                this->sf_string,
41,258✔
545
                this->sf_begin + consumed
41,258✔
546
                    + ((consumed == this->length()) ? 0 : 1),
41,258✔
547
                this->sf_end,
41,258✔
548
            });
82,516✔
549
    }
550

551
    template<typename P>
552
    split_result split_pair(P&& predicate) const
6,460✔
553
    {
554
        int consumed = 0;
6,460✔
555
        while (consumed < this->length()) {
97,115✔
556
            if (predicate(this->data()[consumed])) {
97,115✔
557
                break;
6,460✔
558
            }
559

560
            consumed += 1;
90,655✔
561
        }
562

563
        if (consumed == this->length()) {
6,460✔
UNCOV
564
            return std::nullopt;
×
565
        }
566

567
        return std::make_pair(
12,920✔
568
            string_fragment{
569
                this->sf_string,
6,460✔
570
                this->sf_begin,
6,460✔
571
                this->sf_begin + consumed,
6,460✔
572
            },
573
            string_fragment{
574
                this->sf_string,
6,460✔
575
                this->sf_begin + consumed + 1,
6,460✔
576
                this->sf_end,
6,460✔
577
            });
6,460✔
578
    }
579

580
    template<typename P>
581
    split_result rsplit_pair(P&& predicate) const
2,743,819✔
582
    {
583
        if (this->empty()) {
2,743,819✔
UNCOV
584
            return std::nullopt;
×
585
        }
586

587
        auto curr = this->sf_end - 1;
2,743,819✔
588
        while (curr >= this->sf_begin) {
28,898,846✔
589
            if (predicate(this->sf_string[curr])) {
28,898,846✔
590
                return std::make_pair(
5,487,638✔
591
                    string_fragment{
592
                        this->sf_string,
2,743,819✔
593
                        this->sf_begin,
2,743,819✔
594
                        curr,
595
                    },
596
                    string_fragment{
597
                        this->sf_string,
2,743,819✔
598
                        curr + 1,
599
                        this->sf_end,
2,743,819✔
600
                    });
2,743,819✔
601
            }
602

603
            curr -= 1;
26,155,027✔
604
        }
605

UNCOV
606
        return std::nullopt;
×
607
    }
608

609
    template<typename P>
610
    split_when_result rsplit_when(P&& predicate) const
8,478✔
611
    {
612
        if (this->empty()) {
8,478✔
UNCOV
613
            return std::make_pair(string_fragment{}, string_fragment{});
×
614
        }
615

616
        auto curr = this->sf_end - 1;
8,478✔
617
        while (curr >= this->sf_begin) {
44,809✔
618
            if (predicate(this->sf_string[curr])) {
40,394✔
619
                return std::make_pair(
8,126✔
620
                    string_fragment{
621
                        this->sf_string,
4,063✔
622
                        this->sf_begin,
4,063✔
623
                        curr + 1,
624
                    },
625
                    string_fragment{
626
                        this->sf_string,
4,063✔
627
                        curr + 1,
628
                        this->sf_end,
4,063✔
629
                    });
4,063✔
630
            }
631

632
            curr -= 1;
36,331✔
633
        }
634

635
        return std::make_pair(string_fragment{}, *this);
4,415✔
636
    }
637

638
    using split_n_result = std::pair<string_fragment, string_fragment>;
639

640
    split_n_result split_n(int amount) const;
641

642
    std::vector<string_fragment> split_lines() const;
643

644
    struct tag1 {
645
        const char t_value;
646

647
        constexpr explicit tag1(const char value) : t_value(value) {}
2,798,776✔
648

649
        constexpr bool operator()(char ch) const { return this->t_value == ch; }
32,517,465✔
650
    };
651

652
    struct quoted_string_body {
653
        bool qs_in_escape{false};
654

655
        bool operator()(char ch)
7,374✔
656
        {
657
            if (this->qs_in_escape) {
7,374✔
658
                this->qs_in_escape = false;
1✔
659
                return true;
1✔
660
            }
661
            if (ch == '\\') {
7,373✔
662
                this->qs_in_escape = true;
1✔
663
                return true;
1✔
664
            }
665
            if (ch == '"') {
7,372✔
666
                return false;
395✔
667
            }
668
            return true;
6,977✔
669
        }
670
    };
671

672
    const char* to_string(char* buf) const
673
    {
674
        memcpy(buf, this->data(), this->length());
675
        buf[this->length()] = '\0';
676

677
        return buf;
678
    }
679

680
    std::string to_string() const
15,601,197✔
681
    {
682
        if (!this->is_valid()) {
15,601,197✔
UNCOV
683
            return "<invalid>";
×
684
        }
685

686
        return {this->data(), (size_t) this->length()};
46,803,591✔
687
    }
688

689
    template<typename OutputIt>
690
    void to_hex_string(OutputIt out) const
691
    {
692
        for (const auto ch : *this) {
693
            fmt::format_to(out, FMT_STRING("{:02x}"), ch);
694
        }
695
    }
696

697
    std::string to_unquoted_string() const;
698

699
    void clear()
31,059✔
700
    {
701
        this->sf_begin = 0;
31,059✔
702
        this->sf_end = 0;
31,059✔
703
    }
31,059✔
704

705
    constexpr void invalidate()
336,490✔
706
    {
707
        this->sf_begin = -1;
336,490✔
708
        this->sf_end = -1;
336,490✔
709
    }
336,490✔
710

711
    string_fragment trim(const char* tokens) const;
712
    string_fragment rtrim(const char* tokens) const;
713
    string_fragment trim() const;
714

715
    string_fragment prepend(const char* str, int amount) const
9,305✔
716
    {
717
        return string_fragment{
18,610✔
718
            str,
719
            this->sf_begin + amount,
9,305✔
720
            this->sf_end + amount,
9,305✔
721
        };
9,305✔
722
    }
723

724
    string_fragment erase_before(const char* str, int amount) const
2,980✔
725
    {
726
        return string_fragment{
5,960✔
727
            str,
728
            this->sf_begin - amount,
2,980✔
729
            this->sf_end - amount,
2,980✔
730
        };
2,980✔
731
    }
732

733
    string_fragment erase(const char* str, int amount) const
734
    {
735
        return string_fragment{
736
            str,
737
            this->sf_begin,
738
            this->sf_end - amount,
739
        };
740
    }
741

742
    template<typename A>
743
    const char* to_c_str(A allocator) const
150,972✔
744
    {
745
        auto* retval = allocator.allocate(this->length() + 1);
150,972✔
746
        memcpy(retval, this->data(), this->length());
150,972✔
747
        retval[this->length()] = '\0';
150,972✔
748
        return retval;
150,972✔
749
    }
750

751
    template<typename A>
752
    string_fragment to_owned(A allocator) const
150,972✔
753
    {
754
        return string_fragment{
755
            this->to_c_str(allocator),
756
            0,
757
            this->length(),
758
        };
150,972✔
759
    }
760

761
    std::string_view to_string_view() const
55,839✔
762
    {
763
        return std::string_view{
111,678✔
764
            this->data(),
765
            static_cast<std::string_view::size_type>(this->length())};
111,678✔
766
    }
767

768
    enum class case_style : uint8_t {
769
        lower,
770
        upper,
771
        camel,
772
        mixed,
773
    };
774

775
    case_style detect_text_case_style() const;
776

777
    std::string to_string_with_case_style(case_style style) const;
778

779
    unsigned long hash() const
780
    {
781
        return hash_str(this->data(), this->length());
782
    }
783

784
    uint64_t bloom_bits() const;
785

786
    class cursor_impl {
787
    public:
788
        std::optional<uint32_t> lookbehind() const
120,134✔
789
        {
790
            return this->ci_lookbehind;
120,134✔
791
        }
792

793
        std::optional<uint32_t> lookahead() const;
794

795
        std::optional<uint32_t> next();
796

797
    private:
798
        friend string_fragment;
799

800
        explicit cursor_impl(const string_fragment& parent)
1,360✔
801
            : ci_string(parent.sf_string),
1,360✔
802
              ci_end(parent.sf_end),
1,360✔
803
              ci_next_index(parent.sf_begin)
1,360✔
804
        {
805
        }
1,360✔
806

807
        const char* ci_string;
808
        int32_t ci_end;
809
        int32_t ci_next_index;
810
        std::optional<uint32_t> ci_lookbehind;
811
        std::optional<uint32_t> ci_next_lookbehind;
812
    };
813

814
    cursor_impl cursor() const { return cursor_impl(*this); }
1,360✔
815

816
    const char* sf_string;
817
    int32_t sf_begin;
818
    int32_t sf_end;
819
};
820

821
inline bool
822
operator==(const std::string& left, const string_fragment& right)
1,106✔
823
{
824
    return right == left;
1,106✔
825
}
826

827
inline bool
828
operator<(const char* left, const string_fragment& right)
829
{
830
    int rc = strncmp(left, right.data(), right.length());
831
    return rc < 0;
832
}
833

834
inline void
835
operator+=(std::string& left, const string_fragment& right)
75,998✔
836
{
837
    left.append(right.data(), right.length());
75,998✔
838
}
75,998✔
839

840
inline bool
841
operator<(const string_fragment& left, const char* right)
842
{
843
    return strncmp(left.data(), right, left.length()) < 0;
844
}
845

846
inline std::ostream&
847
operator<<(std::ostream& os, const string_fragment& sf)
2,421✔
848
{
849
    os.write(sf.data(), sf.length());
2,421✔
850
    return os;
2,421✔
851
}
852

853
class string_fragment_producer {
854
public:
855
    struct eof {};
856
    struct error {
857
        std::string what;
858
    };
859
    using next_result = mapbox::util::variant<eof, string_fragment, error>;
860
    static std::unique_ptr<string_fragment_producer> from(string_fragment sf);
861

862
    Result<void, std::string> for_each(
25,911✔
863
        std::function<Result<void, std::string>(string_fragment)> cb)
864
    {
865
        while (true) {
866
            auto next_res = this->next();
65,099✔
867
            if (next_res.is<error>()) {
65,099✔
UNCOV
868
                auto err = next_res.get<error>();
×
869

UNCOV
870
                return Err(err.what);
×
871
            }
872

873
            if (next_res.is<eof>()) {
65,099✔
874
                break;
25,911✔
875
            }
876

877
            const auto sf = next_res.get<string_fragment>();
39,188✔
878
            auto cb_res = cb(sf);
39,188✔
879

880
            if (cb_res.isErr()) {
39,188✔
UNCOV
881
                return Err(cb_res.unwrapErr());
×
882
            }
883
        }
104,287✔
884

885
        return Ok();
25,911✔
886
    }
887

888
    virtual ~string_fragment_producer() {}
126,471✔
889

UNCOV
890
    virtual size_t estimated_size() const { return 1024; }
×
891

892
    virtual next_result next() = 0;
893

894
    std::string to_string();
895
};
896

897
class intern_string {
898
public:
899
    static const intern_string* lookup(const char* str, ssize_t len) noexcept;
900

901
    static const intern_string* lookup(const string_fragment& sf) noexcept;
902

903
    static const intern_string* lookup(const std::string& str) noexcept;
904

905
    const char* get() const { return this->is_str.c_str(); };
184,179,820✔
906

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

909
    size_t size() const { return this->is_str.size(); }
3,839,543✔
910

911
    std::string to_string() const { return this->is_str; }
2,093,147✔
912

913
    string_fragment to_string_fragment() const
2,380,218✔
914
    {
915
        return string_fragment{this->is_str};
2,380,218✔
916
    }
917

918
    bool startswith(const char* prefix) const;
919

920
    struct intern_table;
921
    static std::shared_ptr<intern_table> get_table_lifetime();
922

923
private:
924
    friend intern_table;
925

926
    intern_string(const char* str, ssize_t len)
4,035,464✔
927
        : is_next(nullptr), is_str(str, (size_t) len)
8,070,928✔
928
    {
929
    }
4,035,464✔
930

931
    intern_string* is_next;
932
    std::string is_str;
933
};
934

935
using intern_table_lifetime = std::shared_ptr<intern_string::intern_table>;
936

937
class intern_string_t {
938
public:
939
    using iterator = const char*;
940

941
    intern_string_t(const intern_string* is = nullptr) : ist_interned_string(is)
42,382,383✔
942
    {
943
    }
42,382,383✔
944

945
    const intern_string* unwrap() const { return this->ist_interned_string; }
946

947
    void clear() { this->ist_interned_string = nullptr; };
53,394✔
948

949
    bool empty() const { return this->ist_interned_string == nullptr; }
197,859,230✔
950

951
    const char* get() const
184,216,162✔
952
    {
953
        if (this->empty()) {
184,216,162✔
954
            return "";
50,227✔
955
        }
956
        return this->ist_interned_string->get();
184,165,935✔
957
    }
958

959
    const char* c_str() const { return this->get(); }
1,258,417✔
960

961
    const char* data() const { return this->get(); }
214✔
962

963
    iterator begin() const { return this->get(); }
964

965
    iterator end() const { return this->get() + this->size(); }
966

967
    size_t size() const
3,839,564✔
968
    {
969
        if (this->ist_interned_string == nullptr) {
3,839,564✔
970
            return 0;
21✔
971
        }
972
        return this->ist_interned_string->size();
3,839,543✔
973
    }
974

975
    size_t hash() const
6,571,034✔
976
    {
977
        auto ptr = (uintptr_t) this->ist_interned_string;
6,571,034✔
978

979
        return ptr;
6,571,034✔
980
    }
981

982
    std::string to_string() const
2,093,295✔
983
    {
984
        if (this->ist_interned_string == nullptr) {
2,093,295✔
985
            return "";
296✔
986
        }
987
        return this->ist_interned_string->to_string();
2,093,147✔
988
    }
989

990
    string_fragment to_string_fragment() const
2,380,218✔
991
    {
992
        if (this->ist_interned_string == nullptr) {
2,380,218✔
UNCOV
993
            return string_fragment{"", 0, 0};
×
994
        }
995
        return this->ist_interned_string->to_string_fragment();
2,380,218✔
996
    }
997

998
    bool operator<(const intern_string_t& rhs) const
89,126,850✔
999
    {
1000
        return strcmp(this->get(), rhs.get()) < 0;
89,126,850✔
1001
    }
1002

1003
    bool operator==(const intern_string_t& rhs) const
210,137,657✔
1004
    {
1005
        return this->ist_interned_string == rhs.ist_interned_string;
210,137,657✔
1006
    }
1007

1008
    bool operator!=(const intern_string_t& rhs) const
32,186✔
1009
    {
1010
        return !(*this == rhs);
32,186✔
1011
    }
1012

1013
    bool operator==(const char* rhs) const
71,820✔
1014
    {
1015
        return strcmp(this->get(), rhs) == 0;
71,820✔
1016
    }
1017

1018
    bool operator!=(const char* rhs) const
6,923✔
1019
    {
1020
        return strcmp(this->get(), rhs) != 0;
6,923✔
1021
    }
1022

1023
    static bool case_lt(const intern_string_t& lhs, const intern_string_t& rhs)
1024
    {
UNCOV
1025
        return strnatcasecmp(lhs.size(), lhs.get(), rhs.size(), rhs.get()) < 0;
×
1026
    }
1027

1028
private:
1029
    const intern_string* ist_interned_string;
1030
};
1031

1032
namespace fmt {
1033
template<>
1034
struct formatter<string_fragment> : formatter<string_view> {
1035
    template<typename FormatContext>
1036
    auto format(const string_fragment& sf, FormatContext& ctx) const
17,891,568✔
1037
    {
1038
        return formatter<string_view>::format(
17,891,568✔
1039
            string_view{sf.data(), (size_t) sf.length()}, ctx);
35,783,136✔
1040
    }
1041
};
1042

1043
template<>
1044
struct formatter<intern_string_t> : formatter<string_view> {
1045
    template<typename FormatContext>
1046
    auto format(const intern_string_t& is, FormatContext& ctx)
1,435,979✔
1047
    {
1048
        return formatter<string_view>::format(string_view{is.get(), is.size()},
2,871,958✔
1049
                                              ctx);
2,871,958✔
1050
    }
1051
};
1052

1053
template<>
1054
struct formatter<std::error_code> : formatter<string_view> {
1055
    template<typename FormatContext>
1056
    auto format(const std::error_code& ec, FormatContext& ctx)
19,033✔
1057
    {
1058
        return formatter<string_view>::format(ec.message(), ctx);
38,066✔
1059
    }
1060
};
1061

1062
template<typename I>
1063
struct formatter<std::optional<I>> : formatter<I> {
1064
    template<typename FormatContext>
UNCOV
1065
    auto format(const std::optional<I>& opt, FormatContext& ctx) const
×
1066
    {
UNCOV
1067
        if (!opt) {
×
UNCOV
1068
            return formatter<string_view>::format("\u2205", ctx);
×
1069
        }
1070

UNCOV
1071
        return formatter<I>::format(opt.value(), ctx);
×
1072
    }
1073
};
1074

1075
}  // namespace fmt
1076

1077
namespace std {
1078
template<>
1079
struct hash<const intern_string_t> {
1080
    std::size_t operator()(const intern_string_t& ist) const noexcept
6,571,034✔
1081
    {
1082
        return ist.hash();
6,571,034✔
1083
    }
1084
};
1085
}  // namespace std
1086

1087
inline bool
1088
operator<(const char* left, const intern_string_t& right)
1,691✔
1089
{
1090
    int rc = strncmp(left, right.get(), right.size());
1,691✔
1091
    return rc < 0;
1,691✔
1092
}
1093

1094
inline bool
1095
operator<(const intern_string_t& left, const char* right)
473✔
1096
{
1097
    return strncmp(left.get(), right, left.size()) < 0;
473✔
1098
}
1099

1100
inline bool
1101
operator==(const intern_string_t& left, const string_fragment& sf)
2✔
1102
{
1103
    return ((int) left.size() == sf.length())
2✔
1104
        && (memcmp(left.get(), sf.data(), left.size()) == 0);
2✔
1105
}
1106

1107
inline bool
1108
operator<(const intern_string_t& left, const string_fragment& sf)
1109
{
1110
    return left.to_string_fragment() < sf;
1111
}
1112

1113
inline bool
1114
operator<(const string_fragment& lhs, const intern_string_t& rhs)
1115
{
1116
    return lhs < rhs.to_string_fragment();
1117
}
1118

1119
inline bool
1120
operator==(const string_fragment& left, const intern_string_t& right)
1,273,702✔
1121
{
1122
    return (left.length() == (int) right.size())
1,273,702✔
1123
        && (memcmp(left.data(), right.get(), left.length()) == 0);
1,273,702✔
1124
}
1125

1126
constexpr string_fragment
1127
operator""_frag(const char* str, std::size_t len)
166,162✔
1128
{
1129
    return string_fragment{str, 0, (int) len};
166,162✔
1130
}
1131

1132
namespace std {
1133
inline string
1134
to_string(const string_fragment& s)
12,971✔
1135
{
1136
    return {s.data(), (size_t) s.length()};
38,913✔
1137
}
1138

1139
inline string
UNCOV
1140
to_string(const intern_string_t& s)
×
1141
{
UNCOV
1142
    return s.to_string();
×
1143
}
1144
}  // namespace std
1145

1146
inline string_fragment
1147
to_string_fragment(const string_fragment& s)
1148
{
1149
    return s;
1150
}
1151

1152
inline string_fragment
1153
to_string_fragment(const intern_string_t& s)
1154
{
1155
    return string_fragment(s.get(), 0, s.size());
1156
}
1157

1158
inline string_fragment
UNCOV
1159
to_string_fragment(const std::string& s)
×
1160
{
UNCOV
1161
    return string_fragment(s.c_str(), 0, s.length());
×
1162
}
1163

1164
inline string_fragment
1165
to_string_fragment(const std::string_view& sv)
1166
{
1167
    return string_fragment::from_bytes(sv.data(), sv.length());
1168
}
1169

1170
template<typename A>
1171
std::optional<string_fragment>
1172
to_owned(std::optional<string_fragment> sf, A allocator)
10,959✔
1173
{
1174
    if (!sf) {
10,959✔
1175
        return sf;
10,535✔
1176
    }
1177

1178
    return sf->to_owned(allocator);
424✔
1179
}
1180

1181
struct frag_hasher {
1182
    size_t operator()(const string_fragment& sf) const
1,647,333✔
1183
    {
1184
        return hash_str(sf.data(), sf.length());
1,647,333✔
1185
    }
1186
};
1187

1188
struct intern_hasher {
1189
    size_t operator()(const intern_string_t& is) const
1,124,228✔
1190
    {
1191
        return hash_str(is.c_str(), is.size());
1,124,228✔
1192
    }
1193
};
1194

1195
namespace lnav {
1196
inline std::error_code
1197
from_errno()
19,937✔
1198
{
1199
    return std::error_code{errno, std::generic_category()};
19,937✔
1200
}
1201
}  // namespace lnav
1202

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