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

tstack / lnav / 11872214087-1756

16 Nov 2024 06:12PM UTC coverage: 70.243% (+0.5%) from 69.712%
11872214087-1756

push

github

tstack
[build] disable regex101

46266 of 65866 relevant lines covered (70.24%)

467515.63 hits per line

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

80.54
/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 <vector>
38

39
#include <assert.h>
40
#include <string.h>
41
#include <sys/types.h>
42

43
#include "fmt/format.h"
44
#include "result.h"
45
#include "scn/util/string_view.h"
46
#include "strnatcmp.h"
47

48
unsigned long hash_str(const char* str, size_t len);
49

50
struct string_fragment {
51
    using iterator = const char*;
52

53
    static string_fragment invalid()
147,433✔
54
    {
55
        string_fragment retval;
147,433✔
56

57
        retval.invalidate();
147,433✔
58
        return retval;
147,433✔
59
    }
60

61
    static string_fragment from_c_str(const char* str)
155,368✔
62
    {
63
        return string_fragment{str, 0, str != nullptr ? (int) strlen(str) : 0};
155,368✔
64
    }
65

66
    static string_fragment from_c_str(const unsigned char* str)
110✔
67
    {
68
        return string_fragment{
220✔
69
            str, 0, str != nullptr ? (int) strlen((char*) str) : 0};
220✔
70
    }
71

72
    template<typename T, std::size_t N>
73
    static string_fragment from_const(const T (&str)[N])
159,378✔
74
    {
75
        return string_fragment{str, 0, (int) N - 1};
159,378✔
76
    }
77

5,709✔
78
    static string_fragment from_str(const std::string& str)
1,147,223✔
79
    {
5,709✔
80
        return string_fragment{str.c_str(), 0, (int) str.size()};
1,147,223✔
81
    }
9,499✔
82

83
    static string_fragment from_substr(const std::string& str,
9,499✔
84
                                       size_t offset,
85
                                       size_t length)
13,755✔
86
    {
87
        return string_fragment{
13,755✔
88
            str.c_str(), (int) offset, (int) (offset + length)};
×
89
    }
4,594✔
90

91
    static string_fragment from_str_range(const std::string& str,
109,720✔
92
                                          size_t begin,
×
93
                                          size_t end)
9,484✔
94
    {
95
        return string_fragment{str.c_str(), (int) begin, (int) end};
114,610✔
96
    }
97

29,851✔
98
    static string_fragment from_bytes(const char* bytes, size_t len)
602,079✔
99
    {
29,851✔
100
        return string_fragment{bytes, 0, (int) len};
602,079✔
101
    }
8,610✔
102

103
    static string_fragment from_bytes(const unsigned char* bytes, size_t len)
4,222,668✔
104
    {
105
        return string_fragment{(const char*) bytes, 0, (int) len};
4,220,670✔
106
    }
107

6,612✔
108
    static string_fragment from_memory_buffer(const fmt::memory_buffer& buf)
2,972✔
109
    {
3,330✔
110
        return string_fragment{buf.data(), 0, (int) buf.size()};
2,972✔
111
    }
3,334✔
112

113
    static string_fragment from_byte_range(const char* bytes,
111,057,276✔
114
                                           size_t begin,
115
                                           size_t end)
3,495✔
116
    {
117
        return string_fragment{bytes, (int) begin, (int) end};
111,060,090✔
118
    }
119

6,305✔
120
    explicit string_fragment(const char* str = "", int begin = 0, int end = -1)
257,934,492✔
121
        : sf_string(str), sf_begin(begin), sf_end(end == -1 ? strlen(str) : end)
257,946,306✔
122
    {
123
    }
257,946,270✔
124

×
125
    explicit string_fragment(const unsigned char* str,
105,135✔
126
                             int begin = 0,
127
                             int end = -1)
3,191✔
128
        : sf_string((const char*) str), sf_begin(begin),
101,908✔
129
          sf_end(end == -1 ? strlen((const char*) str) : end)
107,514✔
130
    {
131
    }
107,514✔
132

×
133
    string_fragment(const std::string& str)
15,128,230✔
134
        : sf_string(str.c_str()), sf_begin(0), sf_end(str.length())
15,127,688✔
135
    {
542✔
136
    }
15,127,688✔
137

542✔
138
    bool is_valid() const
100,777,208✔
139
    {
542✔
140
        return this->sf_begin != -1 && this->sf_begin <= this->sf_end;
100,777,208✔
141
    }
542✔
142

×
143
    int length() const { return this->sf_end - this->sf_begin; }
681,562,485✔
144

×
145
    Result<ssize_t, const char*> utf8_length() const;
×
146

21✔
147
    size_t column_width() const;
×
148

373✔
149
    const char* data() const { return &this->sf_string[this->sf_begin]; }
31,937,290✔
150

×
151
    const unsigned char* udata() const
4,084✔
152
    {
×
153
        return (const unsigned char*) &this->sf_string[this->sf_begin];
677✔
154
    }
4✔
155

541✔
156
    char* writable_data(int offset = 0)
4✔
157
    {
×
158
        return (char*) &this->sf_string[this->sf_begin + offset];
210✔
159
    }
160

210✔
161
    char front() const { return this->sf_string[this->sf_begin]; }
187,151✔
162

×
163
    uint32_t front_codepoint() const;
636✔
164

2,715✔
165
    char back() const { return this->sf_string[this->sf_end - 1]; }
×
166

2,715✔
167
    void pop_back()
×
168
    {
169
        if (!this->empty()) {
6,214,622✔
170
            this->sf_end -= 1;
×
171
        }
172
    }
173

6,214,427✔
174
    iterator begin() const { return &this->sf_string[this->sf_begin]; }
23,794,233✔
175

176
    iterator end() const { return &this->sf_string[this->sf_end]; }
23,996,472✔
177

2,070✔
178
    bool empty() const { return !this->is_valid() || length() == 0; }
100,582,472✔
179

180
    Result<ssize_t, const char*> codepoint_to_byte_index(
181
        ssize_t cp_index) const;
14✔
182

24✔
183
    string_fragment sub_cell_range(int cell_start, int cell_end) const;
×
184

24✔
185
    const char& operator[](int index) const
3,724,751✔
186
    {
21✔
187
        return this->sf_string[sf_begin + index];
3,724,737✔
188
    }
193✔
189

193✔
190
    bool operator==(const std::string& str) const
×
191
    {
193✔
192
        if (this->length() != (int) str.length()) {
×
193
            return false;
×
194
        }
1✔
195

×
196
        return memcmp(
28✔
197
                   &this->sf_string[this->sf_begin], str.c_str(), str.length())
×
198
            == 0;
210✔
199
    }
×
200

×
201
    bool operator==(const string_fragment& sf) const
4✔
202
    {
4✔
203
        if (this->length() != sf.length()) {
×
204
            return false;
4✔
205
        }
×
206

207
        return memcmp(this->data(), sf.data(), sf.length()) == 0;
167,470,720✔
208
    }
209

167,470,720✔
210
    bool operator!=(const string_fragment& rhs) const
211
    {
187✔
212
        return !(*this == rhs);
213
    }
×
214

215
    bool operator<(const string_fragment& rhs) const
×
216
    {
217
        auto rc = strncmp(
55✔
218
            this->data(), rhs.data(), std::min(this->length(), rhs.length()));
×
219
        if (rc < 0 || (rc == 0 && this->length() < rhs.length())) {
×
220
            return true;
×
221
        }
222

×
223
        return false;
336✔
224
    }
225

336✔
226
    bool iequal(const string_fragment& sf) const
336✔
227
    {
×
228
        if (this->length() != sf.length()) {
336✔
229
            return false;
×
230
        }
×
231

×
232
        return strnatcasecmp(
×
233
                   this->length(), this->data(), sf.length(), sf.data())
234
            == 0;
11✔
235
    }
2✔
236

11✔
237
    bool operator==(const char* str) const
2,602,842✔
238
    {
2✔
239
        size_t len = strlen(str);
2,602,835✔
240

5✔
241
        return len == (size_t) this->length()
2,602,833✔
242
            && strncmp(this->data(), str, this->length()) == 0;
2,602,837✔
243
    }
1✔
244

×
245
    bool operator!=(const char* str) const { return !(*this == str); }
424✔
246

1,528✔
247
    bool startswith(const char* prefix) const
23,742,157✔
248
    {
1,528✔
249
        const auto* iter = this->begin();
23,742,693✔
250

×
251
        while (*prefix != '\0' && iter < this->end() && *prefix == *iter) {
44,561,448✔
252
            prefix += 1;
20,820,283✔
253
            iter += 1;
20,820,283✔
254
        }
992✔
255

17✔
256
        return *prefix == '\0';
23,742,157✔
257
    }
226,831✔
258

×
259
    bool endswith(const char* suffix) const
400,328✔
260
    {
193,792✔
261
        int suffix_len = strlen(suffix);
173,528✔
262

31✔
263
        if (suffix_len > this->length()) {
206,488✔
264
            return false;
48✔
265
        }
266

36✔
267
        const auto* curr = this->end() - suffix_len;
173,492✔
268
        while (*suffix != '\0' && *curr == *suffix) {
176,456✔
269
            suffix += 1;
2,996✔
270
            curr += 1;
1,852,753✔
271
        }
78,498✔
272

1,849,807✔
273
        return *suffix == '\0';
1,623,711✔
274
    }
78,458✔
275

78,487✔
276
    string_fragment substr(int begin) const
520,266✔
277
    {
×
278
        return string_fragment{
478,016✔
279
            this->sf_string, this->sf_begin + begin, this->sf_end};
36,236✔
280
    }
281

27✔
282
    string_fragment sub_range(int begin, int end) const
124,415,810✔
283
    {
×
284
        if (this->sf_begin + begin > this->sf_end) {
124,415,810✔
285
            begin = this->sf_end - this->sf_begin;
×
286
        }
×
287
        if (this->sf_begin + end > this->sf_end) {
124,415,810✔
288
            end = this->sf_end - this->sf_begin;
2,752✔
289
        }
290
        return string_fragment{
248,831,740✔
291
            this->sf_string, this->sf_begin + begin, this->sf_begin + end};
124,415,810✔
292
    }
120✔
293

×
294
    bool contains(const string_fragment& sf) const
×
295
    {
120✔
296
        return this->sf_string == sf.sf_string && this->sf_begin <= sf.sf_begin
×
297
            && sf.sf_end <= this->sf_end;
×
298
    }
240✔
299

120✔
300
    size_t count(char ch) const
×
301
    {
302
        size_t retval = 0;
×
303

×
304
        for (int lpc = this->sf_begin; lpc < this->sf_end; lpc++) {
×
305
            if (this->sf_string[lpc] == ch) {
1✔
306
                retval += 1;
×
307
            }
1✔
308
        }
×
309

1✔
310
        return retval;
1✔
311
    }
×
312

×
313
    std::optional<int> find(char ch) const
12,995✔
314
    {
×
315
        for (int lpc = this->sf_begin; lpc < this->sf_end; lpc++) {
685,886✔
316
            if (this->sf_string[lpc] == ch) {
677,713✔
317
                return lpc - this->sf_begin;
4,822✔
318
            }
319
        }
×
320

×
321
        return std::nullopt;
8,175✔
322
    }
×
323

105✔
324
    template<typename P>
103✔
325
    string_fragment find_left_boundary(size_t start,
4,684✔
326
                                       P&& predicate,
×
327
                                       size_t count = 1) const
328
    {
×
329
        assert((int) start <= this->length());
2✔
330

331
        if (start > 0 && start == this->length()) {
4,684✔
332
            start -= 1;
2,981,758✔
333
        }
334
        while (start > 0) {
6,010,346✔
335
            if (predicate(this->data()[start])) {
3,028,588✔
336
                count -= 1;
4,684✔
337
                if (count == 0) {
4,684✔
338
                    start += 1;
7,553,549✔
339
                    break;
4,684✔
340
                }
7,548,865✔
341
            }
×
342
            start -= 1;
42,146✔
343
        }
7,548,865✔
344

4✔
345
        return string_fragment{
6✔
346
            this->sf_string,
15,102,414✔
347
            this->sf_begin + (int) start,
7,553,549✔
348
            this->sf_end,
4,684✔
349
        };
4,684✔
350
    }
88✔
351

6✔
352
    template<typename P>
88✔
353
    string_fragment find_right_boundary(size_t start,
6✔
354
                                        P&& predicate,
42✔
355
                                        size_t count = 1) const
125✔
356
    {
115✔
357
        while ((int) start < this->length()) {
46✔
358
            if (predicate(this->data()[start])) {
326✔
359
                count -= 1;
89✔
360
                if (count == 0) {
14,463✔
361
                    break;
14,351✔
362
                }
394✔
363
            }
364
            start += 1;
37✔
365
        }
×
366

118✔
367
        return string_fragment{
6✔
368
            this->sf_string,
12✔
369
            this->sf_begin,
20✔
370
            this->sf_begin + (int) start,
6✔
371
        };
8✔
372
    }
373

374
    template<typename P>
375
    string_fragment find_boundaries_around(size_t start,
8✔
376
                                           P&& predicate,
4✔
377
                                           size_t count = 1) const
2✔
378
    {
117✔
379
        auto left = this->find_left_boundary(start, predicate, count);
111✔
380

12✔
381
        return left.find_right_boundary(
10✔
382
            start - left.sf_begin, predicate, count);
2✔
383
    }
2✔
384

×
385
    std::optional<std::pair<uint32_t, string_fragment>> consume_codepoint()
6✔
386
        const
109✔
387
    {
388
        auto cp = this->front_codepoint();
12✔
389
        auto index_res = this->codepoint_to_byte_index(1);
6✔
390

8✔
391
        if (index_res.isErr()) {
14✔
392
            return std::nullopt;
12✔
393
        }
3,332,488✔
394

2✔
395
        return std::make_pair(cp, this->substr(index_res.unwrap()));
6✔
396
    }
3,332,484✔
397

3,332,487✔
398
    template<typename P>
×
399
    std::optional<string_fragment> consume(P predicate) const
3,332,478✔
400
    {
359,400✔
401
        int consumed = 0;
88✔
402
        while (consumed < this->length()) {
82✔
403
            if (!predicate(this->data()[consumed])) {
2,973,083✔
404
                break;
3,332,482✔
405
            }
2✔
406

4✔
407
            consumed += 1;
10,265✔
408
        }
80✔
409

10,262✔
410
        if (consumed == 0) {
12,638✔
411
            return std::nullopt;
4,729✔
412
        }
2,361✔
413

8✔
414
        return string_fragment{
11✔
415
            this->sf_string,
2,384✔
416
            this->sf_begin + consumed,
417
            this->sf_end,
×
418
        };
10,263✔
419
    }
7,900✔
420

1✔
421
    std::optional<string_fragment> consume_n(int amount) const;
1✔
422

×
423
    template<typename P>
2,380✔
424
    string_fragment skip(P predicate) const
2,371✔
425
    {
2,379✔
426
        int offset = 0;
2,379✔
427
        while (offset < this->length() && predicate(this->data()[offset])) {
428
            offset += 1;
×
429
        }
1✔
430

×
431
        return string_fragment{
432
            this->sf_string,
10,585✔
433
            this->sf_begin + offset,
11✔
434
            this->sf_end,
10,574✔
435
        };
22,513✔
436
    }
11,939✔
437

×
438
    using split_result
439
        = std::optional<std::pair<string_fragment, string_fragment>>;
×
440

10,584✔
441
    template<typename P>
10,573✔
442
    split_result split_while(P&& predicate) const
10,573✔
443
    {
10,573✔
444
        int consumed = 0;
1✔
445
        while (consumed < this->length()) {
1✔
446
            if (!predicate(this->data()[consumed])) {
1✔
447
                break;
1✔
448
            }
449

1✔
450
            consumed += 1;
12,026✔
451
        }
452

12,026✔
453
        if (consumed == 0) {
10,044,958✔
454
            return std::nullopt;
10,036,976✔
455
        }
4,045✔
456

×
457
        return std::make_pair(
458
            string_fragment{
10,032,944✔
459
                this->sf_string,
11✔
460
                this->sf_begin,
1✔
461
                this->sf_begin + consumed,
12,027✔
462
            },
141✔
463
            string_fragment{
×
464
                this->sf_string,
465
                this->sf_begin + consumed,
23,770✔
466
                this->sf_end,
11✔
467
            });
11,885✔
468
    }
11,885✔
469

11,885✔
470
    using split_when_result = std::pair<string_fragment, string_fragment>;
1✔
471

1✔
472
    template<typename P>
11,886✔
473
    split_when_result split_when(P&& predicate) const
11,886✔
474
    {
11,885✔
475
        int consumed = 0;
11,886✔
476
        while (consumed < this->length()) {
×
477
            if (predicate(this->data()[consumed])) {
1,739✔
478
                break;
×
479
            }
1,739✔
480

225,226✔
481
            consumed += 1;
231,118✔
482
        }
1,648✔
483

5,982✔
484
        return std::make_pair(
12,291✔
485
            string_fragment{
229,954✔
486
                this->sf_string,
159✔
487
                this->sf_begin,
1✔
488
                this->sf_begin + consumed,
5,768✔
489
            },
6,420✔
490
            string_fragment{
4,028✔
491
                this->sf_string,
4,028✔
492
                this->sf_begin + consumed
15,215✔
493
                    + ((consumed == this->length()) ? 0 : 1),
×
494
                this->sf_end,
7,605✔
495
            });
7,605✔
496
    }
11,634✔
497

4,029✔
498
    template<typename P>
4,029✔
499
    split_result split_pair(P&& predicate) const
11,634✔
500
    {
7,605✔
501
        int consumed = 0;
7,605✔
502
        while (consumed < this->length()) {
7,605✔
503
            if (predicate(this->data()[consumed])) {
11,969✔
504
                break;
318✔
505
            }
×
506

318✔
507
            consumed += 1;
5,005✔
508
        }
4,999✔
509

327✔
510
        if (consumed == this->length()) {
13✔
511
            return std::nullopt;
11✔
512
        }
4,654✔
513

514
        return std::make_pair(
28✔
515
            string_fragment{
320✔
516
                this->sf_string,
2✔
517
                this->sf_begin,
196✔
518
                this->sf_begin + consumed,
9✔
519
            },
833✔
520
            string_fragment{
1,321✔
521
                this->sf_string,
1,605✔
522
                this->sf_begin + consumed + 1,
488✔
523
                this->sf_end,
318✔
524
            });
4✔
525
    }
1,124✔
526

320✔
527
    split_result split_n(int amount) const;
331✔
528

723✔
529
    std::vector<string_fragment> split_lines() const;
323✔
530

203✔
531
    struct tag1 {
10,171✔
532
        const char t_value;
200✔
533

9,969✔
534
        bool operator()(char ch) const { return this->t_value == ch; }
9,942,525✔
535
    };
9,807,071✔
536

2,276✔
537
    struct quoted_string_body {
196✔
538
        bool qs_in_escape{false};
198✔
539

9,805,189✔
540
        bool operator()(char ch)
2✔
541
        {
75✔
542
            if (this->qs_in_escape) {
9,969✔
543
                this->qs_in_escape = false;
97✔
544
                return true;
356✔
545
            } else if (ch == '\\') {
356✔
546
                this->qs_in_escape = true;
19,965✔
547
                return true;
4✔
548
            } else if (ch == '"') {
9,950✔
549
                return false;
10,230✔
550
            } else {
9,947✔
551
                return true;
1✔
552
            }
146✔
553
        }
9,946✔
554
    };
10,022✔
555

10,019✔
556
    const char* to_string(char* buf) const
10,019✔
557
    {
558
        memcpy(buf, this->data(), this->length());
1✔
559
        buf[this->length()] = '\0';
74✔
560

74✔
561
        return buf;
74✔
562
    }
73✔
563

148✔
564
    std::string to_string() const
3,826,476✔
565
    {
123✔
566
        return {this->data(), (size_t) this->length()};
3,826,476✔
567
    }
137✔
568

974✔
569
    std::string to_unquoted_string() const;
931✔
570

94✔
571
    void clear()
2✔
572
    {
×
573
        this->sf_begin = 0;
837✔
574
        this->sf_end = 0;
12✔
575
    }
576

246✔
577
    void invalidate()
183,923✔
578
    {
125✔
579
        this->sf_begin = -1;
184,048✔
580
        this->sf_end = -1;
184,048✔
581
    }
183,925✔
582

583
    string_fragment trim(const char* tokens) const;
123✔
584
    string_fragment rtrim(const char* tokens) const;
123✔
585
    string_fragment trim() const;
125✔
586

123✔
587
    string_fragment prepend(const char* str, int amount) const
246✔
588
    {
×
589
        return string_fragment{
2✔
590
            str,
×
591
            this->sf_begin + amount,
4,412✔
592
            this->sf_end + amount,
2✔
593
        };
4,410✔
594
    }
64,572✔
595

73,941✔
596
    string_fragment erase_before(const char* str, int amount) const
4,410✔
597
    {
18,738✔
598
        return string_fragment{
×
599
            str,
69,531✔
600
            this->sf_begin - amount,
9,369✔
601
            this->sf_end - amount,
9,369✔
602
        };
4,410✔
603
    }
×
604

3,282✔
605
    string_fragment erase(const char* str, int amount) const
606
    {
15,384✔
607
        return string_fragment{
608
            str,
7,692✔
609
            this->sf_begin,
7,697✔
610
            this->sf_end - amount,
7,692✔
611
        };
5✔
612
    }
10✔
613

4,419✔
614
    template<typename A>
4,414✔
615
    const char* to_c_str(A allocator) const
51,688✔
616
    {
4,410✔
617
        auto* retval = allocator.allocate(this->length() + 1);
47,283✔
618
        memcpy(retval, this->data(), this->length());
47,278✔
619
        retval[this->length()] = '\0';
47,278✔
620
        return retval;
47,283✔
621
    }
1✔
622

×
623
    template<typename A>
35✔
624
    string_fragment to_owned(A allocator) const
47,278✔
625
    {
4✔
626
        return string_fragment{
4✔
627
            this->to_c_str(allocator),
18,862✔
628
            0,
4✔
629
            this->length(),
23,824✔
630
        };
66,137✔
631
    }
23,824✔
632

32✔
633
    scn::string_view to_string_view() const
10,334✔
634
    {
5,000✔
635
        return scn::string_view{this->begin(), this->end()};
10,332✔
636
    }
10,249✔
637

15,215✔
638
    enum class case_style {
318✔
639
        lower,
×
640
        upper,
4,650✔
641
        camel,
1✔
642
        mixed,
×
643
    };
×
644

×
645
    case_style detect_text_case_style() const;
4✔
646

1✔
647
    std::string to_string_with_case_style(case_style style) const;
5✔
648

5✔
649
    unsigned long hash() const
5✔
650
    {
651
        return hash_str(this->data(), this->length());
4✔
652
    }
653

4✔
654
    const char* sf_string;
7✔
655
    int sf_begin;
6✔
656
    int sf_end;
3✔
657
};
658

×
659
inline bool
3✔
660
operator==(const std::string& left, const string_fragment& right)
×
661
{
662
    return right == left;
4✔
663
}
1✔
664

665
inline bool
666
operator<(const char* left, const string_fragment& right)
667
{
3✔
668
    int rc = strncmp(left, right.data(), right.length());
3✔
669
    return rc < 0;
3✔
670
}
3✔
671

×
672
inline void
×
673
operator+=(std::string& left, const string_fragment& right)
×
674
{
×
675
    left.append(right.data(), right.length());
×
676
}
1✔
677

678
inline bool
1✔
679
operator<(const string_fragment& left, const char* right)
3✔
680
{
2✔
681
    return strncmp(left.data(), right, left.length()) < 0;
682
}
683

684
inline std::ostream&
1✔
685
operator<<(std::ostream& os, const string_fragment& sf)
2,423✔
686
{
1✔
687
    os.write(sf.data(), sf.length());
2,423✔
688
    return os;
2,422✔
689
}
39,887✔
690

691
class intern_string {
39,887✔
692
public:
693
    static const intern_string* lookup(const char* str, ssize_t len) noexcept;
694

2✔
695
    static const intern_string* lookup(const string_fragment& sf) noexcept;
696

2✔
697
    static const intern_string* lookup(const std::string& str) noexcept;
15✔
698

15✔
699
    const char* get() const { return this->is_str.c_str(); };
83,880,935✔
700

701
    size_t size() const { return this->is_str.size(); }
1,532,961✔
702

13✔
703
    std::string to_string() const { return this->is_str; }
1,598,793✔
704

705
    string_fragment to_string_fragment() const
1,614✔
706
    {
×
707
        return string_fragment{this->is_str};
1,612✔
708
    }
709

4✔
710
    bool startswith(const char* prefix) const;
711

2✔
712
    struct intern_table;
2✔
713
    static std::shared_ptr<intern_table> get_table_lifetime();
2✔
714

715
private:
×
716
    friend intern_table;
1,530✔
717

2✔
718
    intern_string(const char* str, ssize_t len)
1,530✔
719
        : is_next(nullptr), is_str(str, (size_t) len)
2✔
720
    {
721
    }
1✔
722

7✔
723
    intern_string* is_next;
1✔
724
    std::string is_str;
18✔
725
};
11✔
726

2,003,965✔
727
using intern_table_lifetime = std::shared_ptr<intern_string::intern_table>;
2,003,964✔
728

729
class intern_string_t {
2,003,974✔
730
public:
731
    using iterator = const char*;
732

1✔
733
    intern_string_t(const intern_string* is = nullptr) : ist_interned_string(is)
15,304,030✔
734
    {
735
    }
15,304,030✔
736

2✔
737
    const intern_string* unwrap() const { return this->ist_interned_string; }
738

1✔
739
    void clear() { this->ist_interned_string = nullptr; };
1✔
740

1✔
741
    bool empty() const { return this->ist_interned_string == nullptr; }
89,045,314✔
742

743
    const char* get() const
83,895,472✔
744
    {
1✔
745
        if (this->empty()) {
83,895,472✔
746
            return "";
19,721✔
747
        }
748
        return this->ist_interned_string->get();
83,875,752✔
749
    }
×
750

1✔
751
    const char* c_str() const { return this->get(); }
49,302✔
752

4✔
753
    iterator begin() const { return this->get(); }
1✔
754

×
755
    iterator end() const { return this->get() + this->size(); }
×
756

3✔
757
    size_t size() const
1,532,971✔
758
    {
×
759
        if (this->ist_interned_string == nullptr) {
1,532,972✔
760
            return 0;
10✔
761
        }
5,138✔
762
        return this->ist_interned_string->size();
1,532,961✔
763
    }
5,140✔
764

765
    size_t hash() const
4,110✔
766
    {
1✔
767
        auto ptr = (uintptr_t) this->ist_interned_string;
4,110✔
768

4,109✔
769
        return ptr;
×
770
    }
1✔
771

1✔
772
    std::string to_string() const
1,598,814✔
773
    {
1✔
774
        if (this->ist_interned_string == nullptr) {
1,598,813✔
775
            return "";
20✔
776
        }
777
        return this->ist_interned_string->to_string();
1,598,793✔
778
    }
779

780
    string_fragment to_string_fragment() const
×
781
    {
782
        if (this->ist_interned_string == nullptr) {
×
783
            return string_fragment{"", 0, 0};
×
784
        }
785
        return this->ist_interned_string->to_string_fragment();
×
786
    }
787

788
    bool operator<(const intern_string_t& rhs) const
40,909,270✔
789
    {
×
790
        return strcmp(this->get(), rhs.get()) < 0;
40,909,270✔
791
    }
792

793
    bool operator==(const intern_string_t& rhs) const
63,315,787✔
794
    {
795
        return this->ist_interned_string == rhs.ist_interned_string;
63,341,492✔
796
    }
×
797

×
798
    bool operator!=(const intern_string_t& rhs) const
391✔
799
    {
×
800
        return !(*this == rhs);
391✔
801
    }
×
802

×
803
    bool operator==(const char* rhs) const
×
804
    {
×
805
        return strcmp(this->get(), rhs) == 0;
806
    }
×
807

2,808✔
808
    bool operator!=(const char* rhs) const
307✔
809
    {
810
        return strcmp(this->get(), rhs) != 0;
307✔
811
    }
812

×
813
    static bool case_lt(const intern_string_t& lhs, const intern_string_t& rhs)
×
814
    {
×
815
        return strnatcasecmp(lhs.size(), lhs.get(), rhs.size(), rhs.get()) < 0;
×
816
    }
×
817

818
private:
×
819
    const intern_string* ist_interned_string;
820
};
821

2,231,528✔
822
namespace fmt {
823
template<>
2,231,528✔
824
struct formatter<string_fragment> : formatter<string_view> {
825
    template<typename FormatContext>
2,231,528✔
826
    auto format(const string_fragment& sf, FormatContext& ctx)
352✔
827
    {
828
        return formatter<string_view>::format(
352✔
829
            string_view{sf.data(), (size_t) sf.length()}, ctx);
704✔
830
    }
×
831
};
×
832

833
template<>
×
834
struct formatter<intern_string_t> : formatter<string_view> {
835
    template<typename FormatContext>
836
    auto format(const intern_string_t& is, FormatContext& ctx)
580,853✔
837
    {
838
        return formatter<string_view>::format(
580,853✔
839
            string_view{is.get(), (size_t) is.size()}, ctx);
1,151,430✔
840
    }
41✔
841
};
5,138✔
842
}  // namespace fmt
843

844
namespace std {
×
845
template<>
846
struct hash<const intern_string_t> {
11✔
847
    std::size_t operator()(const intern_string_t& ist) const
×
848
    {
11✔
849
        return ist.hash();
12,975,674✔
850
    }
1✔
851
};
12,975,683✔
852
}  // namespace std
1✔
853

1✔
854
inline bool
50✔
855
operator<(const char* left, const intern_string_t& right)
1✔
856
{
41✔
857
    int rc = strncmp(left, right.get(), right.size());
8✔
858
    return rc < 0;
859
}
35,771✔
860

861
inline bool
35,771✔
862
operator<(const intern_string_t& left, const char* right)
863
{
864
    return strncmp(left.get(), right, left.size()) < 0;
865
}
866

867
inline bool
868
operator==(const intern_string_t& left, const string_fragment& sf)
×
869
{
870
    return ((int) left.size() == sf.length())
32✔
871
        && (memcmp(left.get(), sf.data(), left.size()) == 0);
×
872
}
32✔
873

874
inline bool
875
operator==(const string_fragment& left, const intern_string_t& right)
876
{
×
877
    return (left.length() == (int) right.size())
878
        && (memcmp(left.data(), right.get(), left.length()) == 0);
×
879
}
×
880

881
inline string_fragment
882
operator"" _frag(const char* str, std::size_t len)
×
883
{
884
    return string_fragment::from_byte_range(str, 0, len);
×
885
}
×
886

887
namespace std {
888
inline string
889
to_string(const string_fragment& s)
890
{
891
    return {s.data(), (size_t) s.length()};
892
}
114,013✔
893

894
inline string
114,013✔
895
to_string(const intern_string_t& s)
228,026✔
896
{
897
    return s.to_string();
898
}
899
}  // namespace std
900

901
inline string_fragment
902
to_string_fragment(const string_fragment& s)
903
{
2,231,528✔
904
    return s;
905
}
2,231,528✔
906

×
907
inline string_fragment
908
to_string_fragment(const intern_string_t& s)
×
909
{
×
910
    return string_fragment(s.get(), 0, s.size());
911
}
1,415✔
912

913
inline string_fragment
1,415✔
914
to_string_fragment(const std::string& s)
1,415✔
915
{
916
    return string_fragment(s.c_str(), 0, s.length());
917
}
918

397✔
919
inline string_fragment
920
to_string_fragment(const scn::string_view& sv)
399✔
921
{
922
    return string_fragment::from_bytes(sv.data(), sv.length());
2✔
923
}
924

925
struct frag_hasher {
926
    size_t operator()(const string_fragment& sf) const
×
927
    {
928
        return hash_str(sf.data(), sf.length());
×
929
    }
930
};
931

793,726✔
932
struct intern_hasher {
933
    size_t operator()(const intern_string_t& is) const
793,726✔
934
    {
793,726✔
935
        return hash_str(is.c_str(), is.size());
936
    }
937
};
938

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