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

tstack / lnav / 19833402571-2724

01 Dec 2025 06:33PM UTC coverage: 68.86% (-0.001%) from 68.861%
19833402571-2724

push

github

tstack
[breadcrumb] add thread ID to breadcrumb bar

173 of 219 new or added lines in 14 files covered. (79.0%)

4 existing lines in 3 files now uncovered.

51293 of 74489 relevant lines covered (68.86%)

435674.56 hits per line

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

64.69
/src/base/intern_string.cc
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.cc
30
 */
31

32
#include <mutex>
33

34
#include "intern_string.hh"
35

36
#include <string.h>
37

38
#include "config.h"
39
#include "fmt/ostream.h"
40
#include "lnav_log.hh"
41
#include "pcrepp/pcre2pp.hh"
42
#include "unictype.h"
43
#include "uniwidth.h"
44
#include "ww898/cp_utf8.hpp"
45
#include "xxHash/xxhash.h"
46

47
const static int TABLE_SIZE = 4095;
48

49
struct intern_string::intern_table {
50
    ~intern_table()
1,168✔
51
    {
52
        for (auto is : this->it_table) {
4,784,128✔
53
            auto curr = is;
4,782,960✔
54

55
            while (curr != nullptr) {
7,772,691✔
56
                auto next = curr->is_next;
2,989,731✔
57

58
                delete curr;
2,989,731✔
59
                curr = next;
2,989,731✔
60
            }
61
        }
62
    }
1,168✔
63

64
    intern_string* it_table[TABLE_SIZE];
65
};
66

67
intern_table_lifetime
68
intern_string::get_table_lifetime()
16,631,435✔
69
{
70
    static intern_table_lifetime retval = std::make_shared<intern_table>();
16,631,435✔
71

72
    return retval;
16,631,435✔
73
}
74

75
unsigned long
76
hash_str(const char* str, size_t len)
18,731,039✔
77
{
78
    return XXH3_64bits(str, len);
18,731,039✔
79
}
80

81
const intern_string*
82
intern_string::lookup(const char* str, ssize_t len) noexcept
16,624,086✔
83
{
84
    unsigned long h;
85
    intern_string* curr;
86

87
    if (len == -1) {
16,624,086✔
88
        len = strlen(str);
160,253✔
89
    }
90
    h = hash_str(str, len) % TABLE_SIZE;
16,624,086✔
91

92
    {
93
        static std::mutex table_mutex;
94

95
        std::lock_guard<std::mutex> lk(table_mutex);
16,624,086✔
96
        auto tab = get_table_lifetime();
16,624,086✔
97

98
        curr = tab->it_table[h];
16,624,086✔
99
        while (curr != nullptr) {
19,642,135✔
100
            if (static_cast<ssize_t>(curr->is_str.size()) == len
16,652,404✔
101
                && strncmp(curr->is_str.c_str(), str, len) == 0)
16,652,404✔
102
            {
103
                return curr;
13,634,355✔
104
            }
105
            curr = curr->is_next;
3,018,049✔
106
        }
107

108
        curr = new intern_string(str, len);
2,989,731✔
109
        curr->is_next = tab->it_table[h];
2,989,731✔
110
        tab->it_table[h] = curr;
2,989,731✔
111

112
        return curr;
2,989,731✔
113
    }
16,624,086✔
114
}
115

116
const intern_string*
117
intern_string::lookup(const string_fragment& sf) noexcept
7,349,473✔
118
{
119
    return lookup(sf.data(), sf.length());
7,349,473✔
120
}
121

122
const intern_string*
123
intern_string::lookup(const std::string& str) noexcept
5,113,873✔
124
{
125
    return lookup(str.c_str(), str.size());
5,113,873✔
126
}
127

128
bool
129
intern_string::startswith(const char* prefix) const
×
130
{
131
    const char* curr = this->is_str.data();
×
132

133
    while (*prefix != '\0' && *prefix == *curr) {
×
134
        prefix += 1;
×
135
        curr += 1;
×
136
    }
137

138
    return *prefix == '\0';
×
139
}
140

141
string_fragment
142
string_fragment::trim(const char* tokens) const
52,249✔
143
{
144
    string_fragment retval = *this;
52,249✔
145

146
    while (retval.sf_begin < retval.sf_end) {
83,162✔
147
        bool found = false;
83,001✔
148

149
        for (int lpc = 0; tokens[lpc] != '\0'; lpc++) {
272,772✔
150
            if (retval.sf_string[retval.sf_begin] == tokens[lpc]) {
220,684✔
151
                found = true;
30,913✔
152
                break;
30,913✔
153
            }
154
        }
155
        if (!found) {
83,001✔
156
            break;
52,088✔
157
        }
158

159
        retval.sf_begin += 1;
30,913✔
160
    }
161
    while (retval.sf_begin < retval.sf_end) {
53,239✔
162
        bool found = false;
53,078✔
163

164
        for (int lpc = 0; tokens[lpc] != '\0'; lpc++) {
244,960✔
165
            if (retval.sf_string[retval.sf_end - 1] == tokens[lpc]) {
192,872✔
166
                found = true;
990✔
167
                break;
990✔
168
            }
169
        }
170
        if (!found) {
53,078✔
171
            break;
52,088✔
172
        }
173

174
        retval.sf_end -= 1;
990✔
175
    }
176

177
    return retval;
52,249✔
178
}
179

180
string_fragment
181
string_fragment::trim() const
33,652✔
182
{
183
    return this->trim(" \t\r\n");
33,652✔
184
}
185

186
string_fragment
187
string_fragment::rtrim(const char* tokens) const
1,602✔
188
{
189
    string_fragment retval = *this;
1,602✔
190

191
    while (retval.sf_begin < retval.sf_end) {
3,165✔
192
        bool found = false;
3,150✔
193

194
        for (int lpc = 0; tokens[lpc] != '\0'; lpc++) {
4,737✔
195
            if (retval.sf_string[retval.sf_end - 1] == tokens[lpc]) {
3,150✔
196
                found = true;
1,563✔
197
                break;
1,563✔
198
            }
199
        }
200
        if (!found) {
3,150✔
201
            break;
1,587✔
202
        }
203

204
        retval.sf_end -= 1;
1,563✔
205
    }
206

207
    return retval;
1,602✔
208
}
209

210
std::optional<int>
211
string_fragment::rfind(char ch) const
×
212
{
213
    if (this->empty()) {
×
214
        return std::nullopt;
×
215
    }
216

217
    for (auto index = this->sf_end - 1; index >= this->sf_begin; index--) {
×
218
        if (this->sf_string[index] == ch) {
×
219
            return index;
×
220
        }
221
    }
222

223
    return std::nullopt;
×
224
}
225

226
std::optional<string_fragment>
227
string_fragment::consume_n(int amount) const
504✔
228
{
229
    if (amount > this->length()) {
504✔
230
        return std::nullopt;
×
231
    }
232

233
    return string_fragment{
504✔
234
        this->sf_string,
504✔
235
        this->sf_begin + amount,
504✔
236
        this->sf_end,
504✔
237
    };
504✔
238
}
239

240
string_fragment::split_result
241
string_fragment::split_n(int amount) const
260,611✔
242
{
243
    if (amount > this->length()) {
260,611✔
244
        return std::nullopt;
×
245
    }
246

247
    return std::make_pair(
260,611✔
248
        string_fragment{
×
249
            this->sf_string,
260,611✔
250
            this->sf_begin,
260,611✔
251
            this->sf_begin + amount,
260,611✔
252
        },
253
        string_fragment{
260,611✔
254
            this->sf_string,
260,611✔
255
            this->sf_begin + amount,
260,611✔
256
            this->sf_end,
260,611✔
257
        });
260,611✔
258
}
259

260
std::vector<string_fragment>
261
string_fragment::split_lines() const
369,167✔
262
{
263
    std::vector<string_fragment> retval;
369,167✔
264
    int start = this->sf_begin;
369,167✔
265

266
    for (auto index = start; index < this->sf_end; index++) {
58,546,634✔
267
        if (this->sf_string[index] == '\n') {
58,177,467✔
268
            retval.emplace_back(this->sf_string, start, index + 1);
23,009✔
269
            start = index + 1;
23,009✔
270
        }
271
    }
272
    if (retval.empty() || start < this->sf_end) {
369,167✔
273
        retval.emplace_back(this->sf_string, start, this->sf_end);
368,734✔
274
    }
275

276
    return retval;
738,334✔
277
}
×
278

279
Result<ssize_t, const char*>
280
string_fragment::utf8_length() const
×
281
{
282
    ssize_t retval = 0;
×
283

284
    for (ssize_t byte_index = this->sf_begin; byte_index < this->sf_end;) {
×
285
        auto ch_size = TRY(ww898::utf::utf8::char_size([this, byte_index]() {
×
286
            return std::make_pair(this->sf_string[byte_index],
287
                                  this->sf_end - byte_index);
288
        }));
289
        byte_index += ch_size;
×
290
        retval += 1;
×
291
    }
292

293
    return Ok(retval);
×
294
}
295

296
string_fragment::case_style
297
string_fragment::detect_text_case_style() const
72✔
298
{
299
    static const auto LOWER_RE
300
        = lnav::pcre2pp::code::from_const(R"(^[^A-Z]+$)");
72✔
301
    static const auto UPPER_RE
302
        = lnav::pcre2pp::code::from_const(R"(^[^a-z]+$)");
72✔
303
    static const auto CAMEL_RE
304
        = lnav::pcre2pp::code::from_const(R"(^(?:[A-Z][a-z0-9]+)+$)");
72✔
305

306
    if (LOWER_RE.find_in(*this).ignore_error().has_value()) {
72✔
307
        return case_style::lower;
41✔
308
    }
309
    if (UPPER_RE.find_in(*this).ignore_error().has_value()) {
31✔
310
        return case_style::upper;
3✔
311
    }
312
    if (CAMEL_RE.find_in(*this).ignore_error().has_value()) {
28✔
313
        return case_style::camel;
15✔
314
    }
315

316
    return case_style::mixed;
13✔
317
}
318

319
std::string
320
string_fragment::to_string_with_case_style(case_style style) const
241✔
321
{
322
    std::string retval;
241✔
323

324
    switch (style) {
241✔
325
        case case_style::lower: {
41✔
326
            for (auto ch : *this) {
344✔
327
                retval.append(1, std::tolower(ch));
303✔
328
            }
329
            break;
41✔
330
        }
331
        case case_style::upper: {
172✔
332
            for (auto ch : *this) {
969✔
333
                retval.append(1, std::toupper(ch));
797✔
334
            }
335
            break;
172✔
336
        }
337
        case case_style::camel: {
15✔
338
            retval = this->to_string();
15✔
339
            if (!this->empty()) {
15✔
340
                retval[0] = toupper(retval[0]);
15✔
341
            }
342
            break;
15✔
343
        }
344
        case case_style::mixed: {
13✔
345
            return this->to_string();
13✔
346
        }
347
    }
348

349
    return retval;
228✔
350
}
241✔
351

352
uint64_t
353
string_fragment::bloom_bits() const
14,019✔
354
{
355
    auto a = XXH3_64bits(this->data(), this->length());
14,019✔
356
    auto b = a >> 8;
14,019✔
357
    if ((b & 0x3f) == (a & 0x3f)) {
14,019✔
358
        b = b >> 8;
118✔
359
    }
360
    auto c = b >> 8;
14,019✔
361
    if ((c & 0x3f) == (a & 0x3f) || (c & 0x3f) == (b & 0x3f)) {
14,019✔
362
        c = c >> 8;
504✔
363
    }
364

365
    uint64_t retval = 0;
14,019✔
366
    retval |= 1ULL << (a % 56);
14,019✔
367
    retval |= 1ULL << (b % 56);
14,019✔
368
    retval |= 1ULL << (c % 56);
14,019✔
369

370
    return retval;
14,019✔
371
}
372

373
std::string
374
string_fragment::to_unquoted_string() const
1,130✔
375
{
376
    auto sub_sf = *this;
1,130✔
377

378
    if (sub_sf.startswith("r") || sub_sf.startswith("u")) {
1,130✔
379
        sub_sf = sub_sf.consume_n(1).value();
24✔
380
    }
381
    if (sub_sf.length() >= 2
1,130✔
382
        && ((sub_sf.startswith("\"") && sub_sf.endswith("\""))
1,972✔
383
            || (sub_sf.startswith("'") && sub_sf.endswith("'"))))
842✔
384
    {
385
        std::string retval;
304✔
386

387
        sub_sf.sf_begin += 1;
304✔
388
        sub_sf.sf_end -= 1;
304✔
389
        retval.reserve(this->length());
304✔
390

391
        auto in_escape = false;
304✔
392
        for (auto ch : sub_sf) {
2,220✔
393
            if (in_escape) {
1,916✔
394
                switch (ch) {
×
395
                    case 'n':
×
396
                        retval.push_back('\n');
×
397
                        break;
×
398
                    case 't':
×
399
                        retval.push_back('\t');
×
400
                        break;
×
401
                    case 'r':
×
402
                        retval.push_back('\r');
×
403
                        break;
×
404
                    default:
×
405
                        retval.push_back(ch);
×
406
                        break;
×
407
                }
408
                in_escape = false;
×
409
            } else if (ch == '\\') {
1,916✔
410
                in_escape = true;
×
411
            } else {
412
                retval.push_back(ch);
1,916✔
413
            }
414
        }
415

416
        return retval;
304✔
417
    }
304✔
418

419
    return this->to_string();
826✔
420
}
421

422
uint32_t
423
string_fragment::front_codepoint() const
4,014,076✔
424
{
425
    size_t index = 0;
4,014,076✔
426
    auto read_res = ww898::utf::utf8::read(
427
        [this, &index]() { return this->data()[index++]; });
8,028,164✔
428
    if (read_res.isErr()) {
4,014,076✔
429
        return this->data()[0];
×
430
    }
431
    return read_res.unwrap();
4,014,076✔
432
}
4,014,076✔
433

434
Result<ssize_t, const char*>
435
string_fragment::codepoint_to_byte_index(ssize_t cp_index) const
4,023,883✔
436
{
437
    ssize_t retval = 0;
4,023,883✔
438

439
    while (cp_index > 0) {
7,623,699✔
440
        if (retval >= this->length()) {
4,023,883✔
441
            return Err("index is beyond the end of the string");
424,066✔
442
        }
443
        auto ch_len = TRY(ww898::utf::utf8::char_size([this, retval]() {
7,199,634✔
444
            return std::make_pair(this->data()[retval],
445
                                  this->length() - retval - 1);
446
        }));
447

448
        retval += ch_len;
3,599,816✔
449
        cp_index -= 1;
3,599,816✔
450
    }
451

452
    return Ok(retval);
3,599,816✔
453
}
454

455
string_fragment
456
string_fragment::sub_cell_range(int cell_start, int cell_end) const
3✔
457
{
458
    int byte_index = this->sf_begin;
3✔
459
    std::optional<int> byte_start;
3✔
460
    std::optional<int> byte_end;
3✔
461
    int cell_index = 0;
3✔
462

463
    while (byte_index < this->sf_end) {
9✔
464
        if (cell_start == cell_index) {
6✔
465
            byte_start = byte_index;
3✔
466
        }
467
        if (!byte_end && cell_index >= cell_end) {
6✔
468
            byte_end = byte_index;
×
469
            break;
×
470
        }
471
        auto read_res = ww898::utf::utf8::read(
472
            [this, &byte_index]() { return this->sf_string[byte_index++]; });
12✔
473
        if (read_res.isErr()) {
6✔
474
            byte_index += 1;
×
475
        } else {
476
            auto ch = read_res.unwrap();
6✔
477

478
            switch (ch) {
6✔
479
                case '\t':
×
480
                    do {
481
                        cell_index += 1;
×
482
                    } while (cell_index % 8);
×
483
                    break;
×
484
                default: {
6✔
485
                    auto wcw_res = uc_width(read_res.unwrap(), "UTF-8");
6✔
486
                    if (wcw_res < 0) {
6✔
487
                        wcw_res = 1;
×
488
                    }
489
                    cell_index += wcw_res;
6✔
490
                    break;
6✔
491
                }
492
            }
493
        }
494
    }
6✔
495
    if (cell_start == cell_index) {
3✔
496
        byte_start = byte_index;
×
497
    }
498
    if (!byte_end) {
3✔
499
        byte_end = byte_index;
3✔
500
    }
501

502
    if (byte_start && byte_end) {
3✔
503
        return this->sub_range(byte_start.value(), byte_end.value());
3✔
504
    }
505

506
    return string_fragment{};
×
507
}
508

509
size_t
510
string_fragment::column_to_byte_index(const size_t col) const
324✔
511
{
512
    auto index = this->sf_begin;
324✔
513
    size_t curr_col = 0;
324✔
514

515
    while (curr_col < col && index < this->sf_end) {
651✔
516
        auto read_res = ww898::utf::utf8::read(
517
            [this, &index]() { return this->sf_string[index++]; });
1,559✔
518
        if (read_res.isErr()) {
327✔
519
            curr_col += 1;
×
520
        } else {
521
            auto ch = read_res.unwrap();
327✔
522

523
            switch (ch) {
327✔
524
                case '\t':
×
525
                    do {
526
                        curr_col += 1;
×
527
                    } while (curr_col % 8);
×
528
                    break;
×
529
                default: {
327✔
530
                    auto wcw_res = uc_width(read_res.unwrap(), "UTF-8");
327✔
531
                    if (wcw_res < 0) {
327✔
532
                        wcw_res = 1;
×
533
                    }
534

535
                    curr_col += wcw_res;
327✔
536
                    break;
327✔
537
                }
538
            }
539
        }
540
    }
327✔
541

542
    return index - this->sf_begin;
324✔
543
}
544

545
size_t
546
string_fragment::byte_to_column_index(const size_t byte_index) const
×
547
{
548
    auto index = this->sf_begin;
×
549
    size_t curr_col = 0;
×
550

551
    while (index < this->sf_end && index < (ssize_t) byte_index) {
×
552
        auto read_res = ww898::utf::utf8::read(
553
            [this, &index]() { return this->sf_string[index++]; });
×
554
        if (read_res.isErr()) {
×
555
            curr_col += 1;
×
556
        } else {
557
            auto ch = read_res.unwrap();
×
558

559
            switch (ch) {
×
560
                case '\t':
×
561
                    do {
562
                        curr_col += 1;
×
563
                    } while (curr_col % 8);
×
NEW
564
                    break;
×
565
                default: {
×
566
                    auto wcw_res = uc_width(read_res.unwrap(), "UTF-8");
×
567
                    if (wcw_res < 0) {
×
568
                        wcw_res = 1;
×
569
                    }
570

571
                    curr_col += wcw_res;
×
572
                    break;
×
573
                }
574
            }
575
        }
576
    }
577

578
    return curr_col;
×
579
}
580

581
static bool
582
iswordbreak(wchar_t wchar)
×
583
{
584
    static constexpr uint32_t mask
585
        = UC_CATEGORY_MASK_L | UC_CATEGORY_MASK_N | UC_CATEGORY_MASK_Pc;
586
    return !uc_is_general_category_withtable(wchar, mask);
×
587
}
588

589
std::optional<int>
590
string_fragment::next_word(const int start_col) const
×
591
{
592
    auto index = this->sf_begin;
×
593
    int curr_col = 0;
×
594
    auto in_word = false;
×
595

596
    while (index < this->sf_end) {
×
597
        auto read_res = ww898::utf::utf8::read(
598
            [this, &index]() { return this->sf_string[index++]; });
×
599
        if (read_res.isErr()) {
×
600
            curr_col += 1;
×
601
        } else {
602
            auto ch = read_res.unwrap();
×
603

604
            switch (ch) {
×
605
                case '\t':
×
606
                    do {
607
                        curr_col += 1;
×
608
                    } while (curr_col % 8);
×
609
                    break;
×
610
                default: {
×
611
                    auto wcw_res = uc_width(read_res.unwrap(), "UTF-8");
×
612
                    if (wcw_res < 0) {
×
613
                        wcw_res = 1;
×
614
                    }
615

616
                    if (curr_col == start_col) {
×
617
                        in_word = !iswordbreak(ch);
×
618
                    } else if (curr_col > start_col) {
×
619
                        if (in_word) {
×
620
                            if (iswordbreak(ch)) {
×
621
                                in_word = false;
×
622
                            }
623
                        } else if (!iswordbreak(ch)) {
×
624
                            return curr_col;
×
625
                        }
626
                    }
627
                    curr_col += wcw_res;
×
628
                    break;
×
629
                }
630
            }
631
        }
632
    }
633

634
    return std::nullopt;
×
635
}
636

637
std::optional<int>
638
string_fragment::prev_word(const int start_col) const
×
639
{
640
    auto index = this->sf_begin;
×
641
    int curr_col = 0;
×
642
    auto in_word = false;
×
643
    std::optional<int> last_word_col;
×
644

645
    while (index < this->sf_end) {
×
646
        auto read_res = ww898::utf::utf8::read(
647
            [this, &index]() { return this->sf_string[index++]; });
×
648
        if (read_res.isErr()) {
×
649
            curr_col += 1;
×
650
        } else {
651
            auto ch = read_res.unwrap();
×
652

653
            switch (ch) {
×
654
                case '\t':
×
655
                    do {
656
                        curr_col += 1;
×
657
                    } while (curr_col % 8);
×
658
                    break;
×
659
                default: {
×
660
                    auto wcw_res = uc_width(read_res.unwrap(), "UTF-8");
×
661
                    if (wcw_res < 0) {
×
662
                        wcw_res = 1;
×
663
                    }
664

665
                    if (curr_col == start_col) {
×
666
                        return last_word_col;
×
667
                    }
668
                    if (iswordbreak(ch)) {
×
669
                        in_word = false;
×
670
                    } else {
671
                        if (!in_word) {
×
672
                            last_word_col = curr_col;
×
673
                        }
674
                        in_word = true;
×
675
                    }
676
                    curr_col += wcw_res;
×
677
                    break;
×
678
                }
679
            }
680
        }
681
    }
682

683
    return last_word_col;
×
684
}
685

686
size_t
687
string_fragment::column_width() const
277,341✔
688
{
689
    auto index = this->sf_begin;
277,341✔
690
    size_t retval = 0;
277,341✔
691

692
    while (index < this->sf_end) {
2,348,870✔
693
        auto read_res = ww898::utf::utf8::read(
694
            [this, &index]() { return this->sf_string[index++]; });
4,199,756✔
695
        if (read_res.isErr()) {
2,071,529✔
696
            retval += 1;
1✔
697
        } else {
698
            auto ch = read_res.unwrap();
2,071,528✔
699

700
            switch (ch) {
2,071,528✔
701
                case '\t':
34,226✔
702
                    do {
703
                        retval += 1;
34,226✔
704
                    } while (retval % 8);
34,226✔
705
                    break;
6,140✔
706
                default: {
2,065,388✔
707
                    auto wcw_res = uc_width(read_res.unwrap(), "UTF-8");
2,065,388✔
708
                    if (wcw_res < 0) {
2,065,388✔
709
                        wcw_res = 1;
2,581✔
710
                    }
711
                    retval += wcw_res;
2,065,388✔
712
                    break;
2,065,388✔
713
                }
714
            }
715
        }
716
    }
2,071,529✔
717

718
    return retval;
277,341✔
719
}
720

721
struct single_producer : string_fragment_producer {
722
    explicit single_producer(const string_fragment& sf) : sp_frag(sf) {}
1,905✔
723

724
    next_result next() override
1,362✔
725
    {
726
        auto retval = std::exchange(this->sp_frag, std::nullopt);
1,362✔
727
        if (retval) {
1,362✔
728
            return retval.value();
681✔
729
        }
730

731
        return eof{};
681✔
732
    }
733

734
    std::optional<string_fragment> sp_frag;
735
};
736

737
std::unique_ptr<string_fragment_producer>
738
string_fragment_producer::from(string_fragment sf)
1,905✔
739
{
740
    return std::make_unique<single_producer>(sf);
1,905✔
741
}
742

743
std::string
744
string_fragment_producer::to_string()
14,184✔
745
{
746
    auto retval = std::string{};
14,184✔
747

748
    retval.reserve(this->estimated_size());
14,184✔
749
    auto for_res = this->for_each(
750
        [&retval](string_fragment sf) -> Result<void, std::string> {
×
751
            retval.append(sf.data(), sf.length());
19,056✔
752
            return Ok();
19,056✔
753
        });
14,184✔
754

755
    return retval;
28,368✔
756
}
14,184✔
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