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

gansm / finalcut / #742

23 Nov 2025 07:08PM UTC coverage: 68.454%. Remained the same
#742

push

travis-ci

gansm
Refactoring of FString::toLong() and FString::toULong()

107 of 109 new or added lines in 2 files covered. (98.17%)

20 existing lines in 2 files now uncovered.

37103 of 54201 relevant lines covered (68.45%)

234.19 hits per line

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

96.42
/final/util/fstring.cpp
1
/***********************************************************************
2
* fstring.cpp - Unicode string class with UTF-8 support                *
3
*                                                                      *
4
* This file is part of the FINAL CUT widget toolkit                    *
5
*                                                                      *
6
* Copyright 2012-2025 Markus Gans                                      *
7
*                                                                      *
8
* FINAL CUT is free software; you can redistribute it and/or modify    *
9
* it under the terms of the GNU Lesser General Public License as       *
10
* published by the Free Software Foundation; either version 3 of       *
11
* the License, or (at your option) any later version.                  *
12
*                                                                      *
13
* FINAL CUT is distributed in the hope that it will be useful, but     *
14
* WITHOUT ANY WARRANTY; without even the implied warranty of           *
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
16
* GNU Lesser General Public License for more details.                  *
17
*                                                                      *
18
* You should have received a copy of the GNU Lesser General Public     *
19
* License along with this program.  If not, see                        *
20
* <http://www.gnu.org/licenses/>.                                      *
21
***********************************************************************/
22

23
#include <algorithm>
24
#include <string>
25
#include <utility>
26
#include <vector>
27

28
#include "final/fapplication.h"
29
#include "final/util/flog.h"
30
#include "final/util/fstring.h"
31

32
namespace finalcut
33
{
34

35
// Herper functions
36
template <typename T>
37
constexpr auto isExactlyEqual (T rhs, T lhs) noexcept -> bool
5✔
38
{
39
#if defined(__clang__)
40
  #pragma clang diagnostic push
41
  #pragma clang diagnostic ignored "-Wfloat-equal"
42
#endif
43

44
  return rhs == lhs;
5✔
45

46
#if defined(__clang__)
47
  #pragma clang diagnostic pop
48
#endif
49
}
50

51
// static class attributes
52
wchar_t       FString::null_char{L'\0'};
53
const wchar_t FString::const_null_char{L'\0'};
54

55
//----------------------------------------------------------------------
56
// class FString
57
//----------------------------------------------------------------------
58

59
// constructors and destructor
60
//----------------------------------------------------------------------
61
FString::FString (int len)
11✔
62
{
63
  if ( len > 0 )
11✔
64
    string = std::wstring(size_type(len), L'\0');
14✔
65
}
11✔
66

67
//----------------------------------------------------------------------
68
FString::FString (size_type len)
1✔
69
  : string{std::wstring(len, L'\0')}
3✔
70
{ }
1✔
71

72
//----------------------------------------------------------------------
73
FString::FString (size_type len, wchar_t c)
14✔
74
  : string{std::wstring(len, c)}
42✔
75
{ }
14✔
76

77
//----------------------------------------------------------------------
78
FString::FString (size_type len, const UniChar& c)
2✔
79
  : FString(len, wchar_t(c))
2✔
80
{ }
2✔
81

82
//----------------------------------------------------------------------
83
FString::FString (const FString& s)  // copy constructor
733✔
84
  : string{s.string}
733✔
85
{ }
733✔
86

87
//----------------------------------------------------------------------
88
FString::FString (FString&& s) noexcept  // move constructor
2,663✔
89
  : string{std::move(s.string)}
2,663✔
90
{ }
2,663✔
91

92
//----------------------------------------------------------------------
93
FString::FString (const std::wstring& s)
865✔
94
{
95
  internal_assign(std::wstring{s});
865✔
96
}
865✔
97

98
//----------------------------------------------------------------------
99
#if __cplusplus >= 201703L
100
FString::FString (const std::wstring_view& s)
101
{
102
  if ( ! s.empty() )
103
    internal_assign(std::wstring{s});
104
}
105
#endif
106

107
//----------------------------------------------------------------------
108
FString::FString (std::wstring&& s)
2,706✔
109
  : string{std::move(s)}
2,706✔
110
{ }
2,706✔
111

112
//----------------------------------------------------------------------
113
FString::FString (const wchar_t s[])
3,455✔
114
{
115
  if ( s )
3,455✔
116
    internal_assign(std::wstring{s});
10,350✔
117
}
3,455✔
118

119
//----------------------------------------------------------------------
120
FString::FString (const std::string& s)
58✔
121
{
122
  if ( ! s.empty() )
58✔
123
    internal_assign(internal_toWideString(s.c_str()));
55✔
124
}
58✔
125

126
//----------------------------------------------------------------------
127
#if __cplusplus >= 201703L
128
FString::FString (const std::string_view& s)
129
{
130
  if ( ! s.empty() )
131
    internal_assign(internal_toWideString(s.data()));
132
}
133
#endif
134

135
//----------------------------------------------------------------------
136
FString::FString (const char s[])
2,799✔
137
{
138
  if ( s )
2,799✔
139
    internal_assign(internal_toWideString(s));
2,460✔
140
}
2,799✔
141

142
//----------------------------------------------------------------------
143
FString::FString (const UniChar& c)
1✔
144
{
145
  internal_assign(std::wstring{static_cast<wchar_t>(c)});
2✔
146
}
1✔
147

148
//----------------------------------------------------------------------
149
FString::FString (const wchar_t c)
290✔
150
{
151
  if ( c )
290✔
152
    internal_assign(std::wstring{c});
861✔
153
}
290✔
154

155
//----------------------------------------------------------------------
156
FString::FString (const char c)
129✔
157
{
158
  if ( c )
129✔
159
    internal_assign(std::wstring{wchar_t(uChar(c))});
366✔
160
}
129✔
161

162
//----------------------------------------------------------------------
163
FString::~FString() noexcept = default;  // destructor
19,300✔
164

165

166
// FString operators
167
//----------------------------------------------------------------------
168
auto FString::operator = (const FString& s) -> FString&
98✔
169
{
170
  if ( &s != this )
98✔
171
    internal_assign(std::wstring{s.string});
98✔
172

173
  return *this;
98✔
174
}
175

176
//----------------------------------------------------------------------
177
auto FString::operator = (FString&& s) noexcept -> FString&
778✔
178
{
179
  if ( &s != this )
778✔
180
    string = s.string;
778✔
181

182
  return *this;
778✔
183
}
184

185
//----------------------------------------------------------------------
186
auto FString::operator += (const FString& s) -> const FString&
18✔
187
{
188
  string.append(s.string);
18✔
189
  return *this;
18✔
190
}
191

192
//----------------------------------------------------------------------
193
auto FString::operator << (const FString& s) -> FString&
8✔
194
{
195
  string.append(s.string);
8✔
196
  return *this;
8✔
197
}
198

199
//----------------------------------------------------------------------
200
auto FString::operator << (const UniChar& c) -> FString&
1✔
201
{
202
  FString s{static_cast<wchar_t>(c)};
1✔
203
  string.append(s.string);
1✔
204
  return *this;
1✔
205
}
1✔
206

207
//----------------------------------------------------------------------
208
auto FString::operator << (const wchar_t c) -> FString&
1✔
209
{
210
  FString s{c};
1✔
211
  string.append(s.string);
1✔
212
  return *this;
1✔
213
}
1✔
214

215
//----------------------------------------------------------------------
216
auto FString::operator << (const char c) -> FString&
1✔
217
{
218
  FString s{c};
1✔
219
  string.append(s.string);
1✔
220
  return *this;
1✔
221
}
1✔
222

223
//----------------------------------------------------------------------
224
FString::operator bool () const noexcept
83✔
225
{
226
  return ! isEmpty();
83✔
227
}
228

229
//----------------------------------------------------------------------
230
auto FString::operator () () const noexcept -> const FString&
3✔
231
{
232
  return *this;
3✔
233
}
234

235

236
// public methods of FString
237
//----------------------------------------------------------------------
238
auto FString::clear() noexcept -> FString&
45✔
239
{
240
  string.clear();
45✔
241
  return *this;
45✔
242
}
243

244
//----------------------------------------------------------------------
245
auto FString::reserve (size_type new_capacity) -> FString&
1✔
246
{
247
  string.reserve(new_capacity);
1✔
248
  return *this;
1✔
249
}
250

251
//----------------------------------------------------------------------
252
auto FString::shrink_to_fit() -> FString&
1✔
253
{
254
  string.shrink_to_fit();
1✔
255
  return *this;
1✔
256
}
257

258
//----------------------------------------------------------------------
259
auto FString::resize (size_type count) -> FString&
1✔
260
{
261
  string.resize(count);
1✔
262
  return *this;
1✔
263
}
264

265
//----------------------------------------------------------------------
266
auto FString::resize (size_type count, wchar_t c) -> FString&
1✔
267
{
268
  string.resize(count, c);
1✔
269
  return *this;
1✔
270
}
271

272
//----------------------------------------------------------------------
273
auto FString::wc_str() const noexcept -> const wchar_t*
73✔
274
{
275
  // Returns a constant wide character string
276

277
  return string.c_str();
73✔
278
}
279

280
//----------------------------------------------------------------------
281
auto FString::wc_str() noexcept -> wchar_t*
63✔
282
{
283
  // Returns a wide character string
284

285
  return const_cast<wchar_t*>(string.c_str());
63✔
286
}
287

288
//----------------------------------------------------------------------
289
auto FString::c_str() const -> const char*
14✔
290
{
291
  // Returns a constant c-string
292

293
  if ( isEmpty() )
14✔
294
    return "";
3✔
295

296
  char_string = internal_toCharString(string);
11✔
297
  return char_string.c_str();
11✔
298
}
299

300
//----------------------------------------------------------------------
301
auto FString::c_str() -> char*
87✔
302
{
303
  // Returns a c-string
304

305
  if ( isEmpty() )
87✔
306
    return const_cast<char*>("");
7✔
307

308
  char_string = internal_toCharString(string);
80✔
309
  return const_cast<char*>(char_string.c_str());
80✔
310
}
311

312
//----------------------------------------------------------------------
313
auto FString::toWString() const -> std::wstring
3,221✔
314
{
315
  return string;
3,221✔
316
}
317

318
//----------------------------------------------------------------------
319
auto FString::toString() const -> std::string
13✔
320
{
321
  return internal_toCharString(string);
13✔
322
}
323

324
//----------------------------------------------------------------------
325
auto FString::toLower() const -> FString
1✔
326
{
327
  FString str;
1✔
328
  str.string.reserve(string.length());
1✔
329
  constexpr wchar_t offset = L'a' - L'A';
1✔
330

331
  for (wchar_t c : string)
4✔
332
  {
333
    if ( c < 128 )  // Fast path for ASCII
3✔
334
    {
335
      const bool is_upper = (c >= L'A') && (c <= L'Z');
3✔
336
      str.string.push_back(is_upper ? c + offset : c);
3✔
337
    }
338
    else  // Slow path for non-ASCII (uses locale)
339
    {
UNCOV
340
      str.string.push_back(wchar_t(std::towupper(std::wint_t(c))));
×
341
    }
342
  }
343

344
  return str;
1✔
UNCOV
345
}
×
346

347
//----------------------------------------------------------------------
348
auto FString::toUpper() const -> FString
1✔
349
{
350
  FString str;
1✔
351
  str.string.reserve(string.length());
1✔
352
  constexpr wchar_t offset = L'a' - L'A';
1✔
353

354
  for (wchar_t c : string)
4✔
355
  {
356
    if ( c < 128 )  // Fast path for ASCII
3✔
357
    {
358
      const bool is_lower = (c >= L'a') && (c <= L'z');
3✔
359
      str.string.push_back(is_lower ? c - offset : c);
3✔
360
    }
361
    else  // Slow path for non-ASCII (uses locale)
362
    {
UNCOV
363
      str.string.push_back(wchar_t(std::towupper(std::wint_t(c))));
×
364
    }
365
  }
366

367
  return str;
1✔
UNCOV
368
}
×
369

370
//----------------------------------------------------------------------
371
auto FString::toShort() const -> sInt16
4✔
372
{
373
  const long value = toLong();
4✔
374

375
  if ( value > SHRT_MAX )
4✔
376
    throw std::overflow_error ("overflow");
1✔
377

378
  if ( value < SHRT_MIN )
3✔
379
    throw std::underflow_error ("underflow");
1✔
380

381
  return static_cast<sInt16>(value);
2✔
382
}
383

384
//----------------------------------------------------------------------
385
auto FString::toUShort() const -> uInt16
4✔
386
{
387
  const uLong value = toULong();
4✔
388

389
  if ( value > USHRT_MAX )
3✔
390
    throw std::overflow_error ("overflow");
1✔
391

392
  return static_cast<uInt16>(value);
2✔
393
}
394

395
//----------------------------------------------------------------------
396
auto FString::toInt() const -> int
95✔
397
{
398
  const long value = toLong();
95✔
399

400
  if ( value > INT_MAX )
95✔
401
    throw std::overflow_error ("overflow");
1✔
402

403
  if ( value < INT_MIN )
94✔
404
    throw std::underflow_error ("underflow");
1✔
405

406
  return static_cast<int>(value);
93✔
407
}
408

409
//----------------------------------------------------------------------
410
auto FString::toUInt() const -> uInt
12✔
411
{
412
  const uLong value = toULong();
12✔
413

414
  if ( value > UINT_MAX )
7✔
415
    throw std::overflow_error ("overflow");
1✔
416

417
  return static_cast<uInt>(value);
6✔
418
}
419

420
//----------------------------------------------------------------------
421
auto FString::toLong() const -> long
107✔
422
{
423
  // Find actual start and end (skip whitespace)
424
  const wchar_t* s_ptr{string.data()};
107✔
425
  const wchar_t* end{s_ptr + string.length()};
107✔
426

427
  internal_skipLeadingWs(s_ptr, end);   // Skip leading whitespace
107✔
428
  internal_skipTrailingWs(s_ptr, end);  // Skip trailing whitespace
107✔
429

430
  if ( s_ptr == end )
107✔
431
    throw std::invalid_argument("empty value");
2✔
432

433
  const Sign sign = internal_parseSign(s_ptr);
105✔
434

435
  if ( s_ptr == end )
105✔
UNCOV
436
    throw std::invalid_argument ("no valid number");
×
437

438
  const long value = internal_parseDigits(s_ptr, end, sign);
105✔
439

440
  if ( s_ptr != end )
103✔
441
    throw std::invalid_argument("no valid number");
1✔
442

443
  return sign == Sign::Negative ? (~value) + 1u : value;
204✔
444
}
445

446
//----------------------------------------------------------------------
447
auto FString::toULong() const -> uLong
27✔
448
{
449
  // Find actual start and end (skip whitespace)
450
  const wchar_t* s_ptr{string.data()};
27✔
451
  const wchar_t* end{s_ptr + string.length()};
27✔
452

453
  internal_skipLeadingWs(s_ptr, end);   // Skip leading whitespace
27✔
454
  internal_skipTrailingWs(s_ptr, end);  // Skip trailing whitespace
27✔
455

456
  if ( s_ptr == end )
27✔
457
    throw std::invalid_argument("empty value");
4✔
458

459
  if ( internal_parseSign(s_ptr) == Sign::Negative )  // Handle '-' sign
23✔
460
    throw std::underflow_error ("underflow");
5✔
461

462
  if ( s_ptr == end  )
18✔
UNCOV
463
    throw std::invalid_argument("no valid number");
×
464

465
  const uLong value = internal_parseDigits(s_ptr, end);
18✔
466

467
  if ( s_ptr == string.data() + ((*string.data() == L'+') ? 1 : 0) )
15✔
468
    throw std::invalid_argument("no valid number");  // No digits parsed
2✔
469

470
  if ( s_ptr != end )
13✔
UNCOV
471
    throw std::invalid_argument("no valid number");  // Trailing non-digits
×
472

473
  return value;
13✔
474
}
475

476
//----------------------------------------------------------------------
477
auto FString::toFloat() const -> float
6✔
478
{
479
  const double value{toDouble()};
6✔
480

481
  if ( value > double(FLT_MAX) || value < double(-FLT_MAX) )
6✔
482
    throw std::overflow_error ("overflow");
2✔
483

484
  if ( std::fabs(value) < double(FLT_EPSILON) )  // value == 0.0f
4✔
485
    throw std::underflow_error ("underflow");
1✔
486

487
  return static_cast<float>(value);
3✔
488
}
489

490
//----------------------------------------------------------------------
491
auto FString::toDouble() const -> double
15✔
492
{
493
  if ( isEmpty() )
15✔
494
    throw std::invalid_argument ("empty value");
2✔
495

496
  errno = 0;
13✔
497
  wchar_t* p{nullptr};
13✔
498
  const double ret = std::wcstod(string.c_str(), &p);
13✔
499

500
  if ( p == string.c_str() )
13✔
501
    throw std::invalid_argument("no valid floating point value");
1✔
502

503
  if ( p != nullptr && *p != L'\0' )
12✔
UNCOV
504
    throw std::invalid_argument ("no valid floating point value");
×
505

506
  if ( errno == ERANGE )
12✔
507
  {
508
    if ( isExactlyEqual(ret, HUGE_VAL) || isExactlyEqual(ret, -HUGE_VAL) )
3✔
509
      throw std::overflow_error ("overflow");
2✔
510

511
    if ( std::fabs(ret) < DBL_EPSILON )  // ret == 0.0l
1✔
512
      throw std::underflow_error ("underflow");
1✔
513
  }
514

515
  return ret;
9✔
516
}
517

518
//----------------------------------------------------------------------
519
auto FString::ltrim() const -> FString
20✔
520
{
521
  if ( isEmpty() )  // Handle empty string
20✔
522
    return {};
3✔
523

524
  // Find last non-whitespace character
525
  const auto pos = string.find_first_not_of(L" \t\n\r\f\v");
17✔
526

527
  if ( pos == npos )  // All whitespace
17✔
528
    return {};
7✔
529

530
  return string.substr(pos);
10✔
531
}
532

533
//----------------------------------------------------------------------
534
auto FString::rtrim() const -> FString
21✔
535
{
536
  if ( isEmpty() )  // Handle empty string
21✔
537
    return {};
4✔
538

539
  // Find last non-whitespace character
540
  const auto pos = string.find_last_not_of(L" \t\n\r\f\v");
17✔
541

542
  if ( pos == npos )  // All whitespace
17✔
543
    return {};
7✔
544

545
  return string.substr(0, pos + 1);
10✔
546
}
547

548
//----------------------------------------------------------------------
549
auto FString::trim() const -> FString
13✔
550
{
551
  if ( isEmpty() )  // Handle empty string
13✔
552
    return {};
3✔
553

554
  const auto first = string.find_first_not_of(L" \t\n\r\f\v");
10✔
555

556
  if ( first == npos )  // All whitespace
10✔
UNCOV
557
    return {};
×
558

559
  const auto last = string.find_last_not_of(L" \t\n\r\f\v");
10✔
560
  return string.substr(first, last - first + 1);
10✔
561
}
562

563
//----------------------------------------------------------------------
564
auto FString::left (size_type len) const -> FString
18✔
565
{
566
  // Handle empty and too long strings
567
  if ( isEmpty() || len > getLength() )
18✔
568
    return *this;
7✔
569

570
  return string.substr(0, len);
11✔
571
}
572

573
//----------------------------------------------------------------------
574
auto FString::right (size_type len) const -> FString
43✔
575
{
576
  // Handle empty and too long strings
577
  if ( isEmpty() || len > getLength() )
43✔
578
    return *this;
7✔
579

580
  return string.substr(string.size() - len, len);
36✔
581
}
582

583
//----------------------------------------------------------------------
584
auto FString::mid (size_type pos, size_type len) const -> FString
23✔
585
{
586
  // Handle empty string
587
  if ( isEmpty() || len == 0 )
23✔
588
    return {};
7✔
589

590
  if ( pos == 0 )
16✔
591
    pos = 1;
2✔
592

593
  const size_type start_index{pos - 1};
16✔
594
  const auto length{string.length()};
16✔
595

596
  if ( start_index >= length )
16✔
597
    return {};
2✔
598

599
  FString str{};
14✔
600
  len = std::min(len, length - start_index);
14✔
601
  str.string.reserve (len);
14✔
602
  str.string.assign (string, start_index, len);
14✔
603
  return str;
14✔
604
}
14✔
605

606
//----------------------------------------------------------------------
607
auto FString::split (const FString& delimiter) const -> FStringList
35✔
608
{
609
  if ( isEmpty() )
35✔
610
    return {};
2✔
611

612
  if ( delimiter.isEmpty() )
33✔
613
    return {*this};
3✔
614

615
  FStringList string_list{};
32✔
616
  const auto delimiter_length{delimiter.getLength()};
32✔
617
  std::wstring::size_type start{0};
32✔
618
  std::wstring::size_type pos{0};
32✔
619

620
  // Calculate result size and reserve
621
  size_type count{1};
32✔
622

623
  while ( (pos = string.find(delimiter.string, pos)) != npos )
101✔
624
  {
625
    count++;
69✔
626
    pos += delimiter_length;
69✔
627
  }
628

629
  if ( count == 1 )  // 'delimiter' not found
32✔
630
  {
631
    string_list.emplace_back(std::wstring(string));
1✔
632
    return string_list;
1✔
633
  }
634

635
  string_list.reserve(count);
31✔
636

637
  // Perform split
638
  while ( (pos = string.find(delimiter.string, start)) != npos )
100✔
639
  {
640
    string_list.emplace_back(std::wstring(string, start, pos - start));
69✔
641
    start = pos + delimiter_length;
69✔
642
  }
643

644
  string_list.emplace_back(std::wstring(string, start));
31✔
645
  return string_list;
31✔
646
}
33✔
647

648
//----------------------------------------------------------------------
649
auto FString::setString (const FString& s) -> FString&
68✔
650
{
651
  std::wstring str{s.string};
68✔
652
  internal_assign (std::move(str));
68✔
653
  return *this;
68✔
654
}
68✔
655

656
//----------------------------------------------------------------------
657
auto FString::setNumber (sInt64 value) -> FString&
13✔
658
{
659
  std::array<wchar_t, NUMBER_BUFFER_SIZE> buf{};
13✔
660
  wchar_t* s = &buf[NUMBER_BUFFER_LENGTH];  // Pointer to the last character
13✔
661
  const auto is_negative{ bool( value < 0 ) };
13✔
662
  auto abs_value = is_negative ? ~static_cast<uInt64>(value) + 1
13✔
663
                               : static_cast<uInt64>(value);
664
  *s = '\0';
13✔
665

666
  do
667
  {
668
    *--s = L'0' + wchar_t(abs_value % 10);
109✔
669
    abs_value /= 10;
109✔
670
  }
671
  while ( abs_value );
109✔
672

673
  if ( is_negative )
13✔
674
    *--s = '-';
12✔
675

676
  std::wstring str{s};
26✔
677
  internal_assign (std::move(str));
13✔
678
  return *this;
13✔
679
}
13✔
680

681
//----------------------------------------------------------------------
682
auto FString::setNumber (uInt64 value) -> FString&
21✔
683
{
684
  std::array<wchar_t, NUMBER_BUFFER_SIZE> buf{};
21✔
685
  wchar_t* s = &buf[NUMBER_BUFFER_LENGTH];  // Pointer to the last character
21✔
686
  *s = '\0';
21✔
687

688
  do
689
  {
690
    *--s = L'0' + wchar_t(value % 10);
154✔
691
    value /= 10;
154✔
692
  }
693
  while ( value );
154✔
694

695
  std::wstring str{s};
42✔
696
  internal_assign (std::move(str));
21✔
697
  return *this;
21✔
698
}
21✔
699

700
//----------------------------------------------------------------------
701
auto FString::setNumber (lDouble f_value, int precision) -> FString&
12✔
702
{
703
  // The precision can not have more than 2 digits
704
  precision = std::min(precision, 99);
12✔
705

706
  std::array<wchar_t, 8> format{};  // = "%.<precision>Lg"
12✔
707
  wchar_t* s = &format[0];
12✔
708
  *s++ = L'%';
12✔
709
  *s++ = L'.';
12✔
710

711
  if ( precision >= 10 )
12✔
712
  {
713
    // The precision value is 2 digits long
714
    *s++ = precision / 10 + L'0';
7✔
715
    *s++ = precision % 10 + L'0';
7✔
716
  }
717
  else
718
  {
719
    // The precision value has only 1 digit
720
    *s++ = precision + L'0';
5✔
721
  }
722

723
  *s++ = L'L';
12✔
724
  *s++ = L'g';
12✔
725
  *s = L'\0';
12✔
726

727
  return sprintf(format.data(), f_value);
12✔
728
}
729

730
//----------------------------------------------------------------------
731
auto FString::setFormatedNumber (sInt64 value, FString separator) -> FString&
5✔
732
{
733
  int n{0};
5✔
734
  std::array<wchar_t, NUMBER_BUFFER_SIZE> buf{};
5✔
735
  wchar_t* s = &buf[NUMBER_BUFFER_LENGTH];  // Pointer to the last character
5✔
736
  auto abs_value{uInt64(value)};
5✔
737

738
  if ( separator[0] == 0 )
5✔
739
    separator = L" ";
3✔
740

741
  if ( value < 0 )
5✔
742
    abs_value = uInt64(-value);
3✔
743

744
  *s = L'\0';
5✔
745

746
  do
747
  {
748
    *--s = L'0' + wchar_t(abs_value % 10);
64✔
749
    abs_value /= 10;
64✔
750
    n++;
64✔
751

752
    if ( abs_value && n % 3 == 0 )
64✔
753
      *--s = separator[0];
19✔
754
  }
755
  while ( abs_value );
64✔
756

757
  if ( value < 0 )
5✔
758
    *--s = '-';
3✔
759

760
  std::wstring str{s};
10✔
761
  internal_assign (std::move(str));
5✔
762
  return *this;
5✔
763
}
5✔
764

765
//----------------------------------------------------------------------
766
auto FString::setFormatedNumber (uInt64 value, FString separator) -> FString&
6✔
767
{
768
  int n{0};
6✔
769
  std::array<wchar_t, NUMBER_BUFFER_SIZE> buf{};
6✔
770
  wchar_t* s = &buf[NUMBER_BUFFER_LENGTH];  // Pointer to the last character
6✔
771
  *s = L'\0';
6✔
772

773
  if ( separator[0] == 0 )
6✔
774
    separator = L" ";
4✔
775

776
  do
777
  {
778
    *--s = L'0' + wchar_t(value % 10);
85✔
779
    value /= 10;
85✔
780
    n++;
85✔
781

782
    if ( value && n % 3 == 0 )
85✔
783
      *--s = separator[0];
25✔
784
  }
785
  while ( value );
85✔
786

787
  std::wstring str{s};
12✔
788
  internal_assign (std::move(str));
6✔
789
  return *this;
6✔
790
}
6✔
791

792
// FString operators
793
//----------------------------------------------------------------------
794
auto FString::insert (const FString& s, int pos) -> const FString&
23✔
795
{
796
  if ( isNegative(pos) || uInt(pos) > string.length() )
23✔
797
    throw std::out_of_range("FString::insert index out of range");
4✔
798

799
  string.insert(uInt(pos), s.string, 0, s.getLength());
19✔
800
  return *this;
19✔
801
}
802

803
//----------------------------------------------------------------------
804
auto FString::insert (const FString& s, size_type pos) -> const FString&
1✔
805
{
806
  if ( pos > string.length() )
1✔
UNCOV
807
    throw std::out_of_range("");
×
808

809
  string.insert(uInt(pos), s.string, 0, s.getLength());
1✔
810
  return *this;
1✔
811
}
812

813
//----------------------------------------------------------------------
814
auto FString::replace (const FString& from, const FString& to) const -> FString
78✔
815
{
816
  // Handle empty strings
817
  if ( isEmpty() || from.isEmpty() )
78✔
818
    return *this;
7✔
819

820
  // Count occurrences to reserve exact capacity
821
  size_type count{0};
71✔
822
  std::wstring::size_type pos{0};
71✔
823

824
  while ( (pos = string.find(from.string, pos)) != npos )
136✔
825
  {
826
    count++;
65✔
827
    pos += from.getLength();
65✔
828
  }
829

830
  if ( count == 0 )  //  String 'from' not found
71✔
831
    return *this;
12✔
832

833
  // Calculate result size and reserve
834
  const size_type from_len{from.getLength()};
59✔
835
  const size_type to_len{to.getLength()};
59✔
836
  const size_type new_size{string.length() + count * (to_len - from_len)};
59✔
837

838
  FString str{};
59✔
839
  str.string.reserve(new_size);
59✔
840
  size_type last_pos{0};
59✔
841
  pos = 0;
59✔
842

843
  while ( (pos = string.find(from.string, pos)) != npos )
124✔
844
  {
845
    str.string.append (string, last_pos, pos - last_pos);  // Before
65✔
846
    str.string.append (to.string);  // The string replacement
65✔
847
    pos += from_len;
65✔
848
    last_pos = pos;
65✔
849
  }
850

851
  str.string.append (string, last_pos, npos);  // After
59✔
852
  return str;
59✔
853
}
59✔
854

855
//----------------------------------------------------------------------
856
auto FString::replaceControlCodes() const -> FString
20✔
857
{
858
  FString str{};
20✔
859
  str.string.reserve(string.length());
20✔
860

861
  for (auto c : string)
192✔
862
  {
863
    if ( c <= L'\x1f' )
172✔
864
    {
865
      str.string.push_back(c + L'\x2400');
72✔
866
    }
867
    else if ( c == L'\x7f' )
100✔
868
    {
869
      str.string.push_back(L'\x2421');
4✔
870
    }
871
    else if ( (c >= L'\x80' && c <= L'\x9f') || ! isPrintable(c) )
96✔
872
    {
873
      str.string.push_back(L' ');
36✔
874
    }
875
    else
876
      str.string.push_back(c);
60✔
877
  }
878

879
  return str;
20✔
UNCOV
880
}
×
881

882
//----------------------------------------------------------------------
883
auto FString::expandTabs (int tabstop) const -> FString
50✔
884
{
885
  if (tabstop <= 0)
50✔
886
    return *this;
2✔
887

888
  FString str{};
48✔
889
  auto tab_stop = size_type(tabstop);
48✔
890
  auto tab_count = size_type(std::count(string.begin(), string.end(), L'\t'));
48✔
891
  str.string.reserve(string.length() + (tab_count * tab_stop));
48✔
892
  size_type column{0};
48✔
893

894
  for (wchar_t c : string)
564✔
895
  {
896
    if ( c == L'\t' )
516✔
897
    {
898
      // Calculate spaces needed to reach next tab stop
899
      const size_type spaces{tab_stop - (column % tab_stop)};
76✔
900
      str.string.append(spaces, L' ');
76✔
901
      column += spaces;
76✔
902
    }
903
    else if ( c == L'\n' || c == L'\r' )
440✔
904
    {
UNCOV
905
      str.string.push_back(c);
×
UNCOV
906
      column = 0;  // Reset column on newline
×
907
    }
908
    else
909
    {
910
      str.string.push_back(c);
440✔
911
      ++column;
440✔
912
    }
913
  }
914

915
  return str;
48✔
916
}
48✔
917

918
//----------------------------------------------------------------------
919
auto FString::removeDel() const -> FString
23✔
920
{
921
  FString str{};
23✔
922
  str.string.reserve(string.length());
23✔
923
  size_type del_count{0};
23✔
924

925
  for (const auto c : string)
151✔
926
  {
927
    if ( c == L'\x7f' )
128✔
928
    {
929
      del_count++;
48✔
930
    }
931
    else if ( del_count > 0 )
80✔
932
    {
933
      del_count--;
33✔
934
    }
935
    else  // del_count == 0
936
    {
937
      str.string.push_back(c);
47✔
938
    }
939
  }
940

941
  return str;
23✔
UNCOV
942
}
×
943

944

945
//----------------------------------------------------------------------
946
auto FString::removeBackspaces() const -> FString
27✔
947
{
948
  FString str{};
27✔
949
  str.string.reserve(string.length());
27✔
950

951
  for (const auto c : string)
159✔
952
  {
953
    if ( c != L'\b' )
132✔
954
    {
955
      str.string.push_back(c);
78✔
956
    }
957
    else if ( ! str.string.empty() )
54✔
958
      str.string.pop_back();
30✔
959
  }
960

961
  return str;
27✔
UNCOV
962
}
×
963

964
//----------------------------------------------------------------------
965
auto FString::overwrite (const FString& s, int pos) -> FString&
14✔
966
{
967
  if ( pos < 0 )
14✔
968
    return overwrite (s, 0);
3✔
969

970
  return overwrite (s, size_type(pos));
11✔
971
}
972

973
//----------------------------------------------------------------------
974
auto FString::overwrite (const FString& s, size_type pos) -> FString&
16✔
975
{
976
  const auto length{string.length()};
16✔
977

978
  if ( pos > length )
16✔
979
    pos = length;
3✔
980

981
  string.replace(pos, s.getLength(), s.string);
16✔
982
  return *this;
16✔
983
}
984

985
//----------------------------------------------------------------------
986
auto FString::remove (size_type pos, size_type len) -> FString&
33✔
987
{
988
  const auto length{string.length()};
33✔
989

990
  if ( pos > length )
33✔
991
    return *this;
1✔
992

993
  if ( pos + len > length )
32✔
994
    len = length - pos;
3✔
995

996
  string.erase (pos, len);
32✔
997
  return *this;
32✔
998
}
999

1000
//----------------------------------------------------------------------
1001
auto FString::erase (size_type pos, size_type len) -> FString&
5✔
1002
{
1003
  string.erase (pos, len);
5✔
1004
  return *this;
5✔
1005
}
1006

1007
//----------------------------------------------------------------------
1008
auto FString::includes (const FString& s) const noexcept -> bool
60✔
1009
{
1010
  if ( s.isEmpty() )
60✔
1011
    return false;
8✔
1012

1013
  return string.find(s.string) != npos;
52✔
1014
}
1015

1016
//----------------------------------------------------------------------
1017
auto FString::contains (const FString& s) const noexcept -> bool
4✔
1018
{
1019
  return includes(s);
4✔
1020
}
1021

1022
//----------------------------------------------------------------------
1023
auto FString::find (const FString& s, size_type pos) const noexcept -> size_type
5✔
1024
{
1025
  return string.find(s.string, pos);
5✔
1026
}
1027

1028
//----------------------------------------------------------------------
1029
auto FString::rfind (const FString& s, size_type pos) const noexcept -> size_type
5✔
1030
{
1031
  return string.rfind(s.string, pos);
5✔
1032
}
1033

1034

1035
// private methods of FString
1036
//----------------------------------------------------------------------
1037
auto FString::internal_toCharString (const std::wstring& s) const -> std::string
1,050✔
1038
{
1039
  if ( s.empty() )
1,050✔
1040
    return {};
157✔
1041

1042
  auto state{std::mbstate_t()};
893✔
1043
  const wchar_t* src = s.c_str();
893✔
1044
  const auto size = std::wcsrtombs(nullptr, &src, 0, &state);
893✔
1045

1046
  if ( size == MALFORMED_STRING )
893✔
UNCOV
1047
    return {};
×
1048

1049
  std::string dest{};
893✔
1050
  dest.resize(size);
893✔
1051
  auto dest_data = const_cast<char*>(dest.data());
893✔
1052
  const auto mblength = std::wcsrtombs (dest_data, &src, size + 1, &state);
893✔
1053

1054
  if ( mblength == MALFORMED_STRING && errno != EILSEQ )
893✔
UNCOV
1055
    return {};
×
1056

1057
  return dest;
893✔
1058
}
893✔
1059

1060
//----------------------------------------------------------------------
1061
auto FString::internal_toWideString (const char src[]) const -> std::wstring
2,516✔
1062
{
1063
  if ( ! src || *src == '\0' )
2,516✔
1064
    return {};
64✔
1065

1066
  auto state{std::mbstate_t()};
2,452✔
1067
  auto size = std::mbsrtowcs(nullptr, &src, 0, &state);
2,452✔
1068

1069
  if ( size == MALFORMED_STRING )
2,452✔
UNCOV
1070
    return {};
×
1071

1072
  std::wstring dest(size, L'\0');
2,452✔
1073
  auto dest_data = const_cast<wchar_t*>(dest.data());
2,452✔
1074
  const auto wide_length = std::mbsrtowcs (dest_data, &src, size + 1, &state);
2,452✔
1075

1076
  if ( wide_length == MALFORMED_STRING )
2,452✔
UNCOV
1077
    return {};
×
1078

1079
  return dest;
2,452✔
1080
}
2,452✔
1081

1082
//----------------------------------------------------------------------
1083
inline void FString::internal_skipLeadingWs ( const wchar_t*& p
134✔
1084
                                            , const wchar_t* end ) const noexcept
1085
{
1086
  // Skip over leading whitespace in a string
1087

1088
  while ( p != end && std::iswspace(static_cast<std::wint_t>(*p)) )
134✔
NEW
1089
    ++p;
×
1090
}
134✔
1091

1092
//----------------------------------------------------------------------
1093
inline void FString::internal_skipTrailingWs ( const wchar_t* begin
134✔
1094
                                             , const wchar_t*& p ) const noexcept
1095
{
1096
  // Skip over trailing whitespace in a string
1097

1098
  while ( p > begin && std::iswspace(static_cast<std::wint_t>(*(p - 1))) )
134✔
NEW
1099
    --p;
×
1100
}
134✔
1101

1102
//----------------------------------------------------------------------
1103
inline auto FString::internal_parseSign (const wchar_t*& s) const noexcept -> Sign
128✔
1104
{
1105
  // Extract sign and increment pointer
1106

1107
  if ( *s == L'-' )  // Handle '-' sign
128✔
1108
  {
1109
    ++s;
14✔
1110
    return Sign::Negative;
14✔
1111
  }
1112

1113
  if ( *s == L'+' )  // Handle '+' sign
114✔
1114
    ++s;
2✔
1115

1116
  return Sign::Positive;
114✔
1117
}
1118

1119
//----------------------------------------------------------------------
1120
inline auto FString::internal_parseDigits ( const wchar_t*& s_ptr
105✔
1121
                                          , const wchar_t* end
1122
                                          , Sign sign ) const -> long
1123
{
1124
  // Parse digits and detect overflow/underflow
1125

1126
  long value{0};
105✔
1127

1128
  while ( s_ptr != end )
392✔
1129
  {
1130
    const wchar_t c{*s_ptr};
290✔
1131

1132
    if ( c < L'0' || c > L'9' )  // Digit check
290✔
1133
      break;
1134

1135
    const auto digit{long(c - L'0')};
289✔
1136

1137
    if ( internal_isOverflowed(sign, value, digit) )
289✔
1138
    {
1139
      if ( sign == Sign::Negative )
2✔
1140
        throw std::underflow_error("underflow");
1✔
1141

1142
      throw std::overflow_error("overflow");
1✔
1143
    }
1144

1145
    value = (value * 10) + digit;
287✔
1146
    ++s_ptr;
287✔
1147
  }
1148

1149
  return value;
103✔
1150
}
1151

1152
//----------------------------------------------------------------------
1153
inline auto FString::internal_parseDigits ( const wchar_t*& s_ptr
18✔
1154
                                          , const wchar_t* end ) const -> uLong
1155
{
1156
  // Parse digits and detect overflow/underflow
1157

1158
  uLong value{0};
18✔
1159

1160
  while ( s_ptr != end )
170✔
1161
  {
1162
    const wchar_t c{*s_ptr};
157✔
1163

1164
    if ( c < L'0' || c > L'9' )  // Digit check
157✔
1165
      break;
1166

1167
    const auto digit{uLong(c - L'0')};
155✔
1168

1169
    if ( internal_isOverflowed(value, digit) )
155✔
1170
      throw std::overflow_error("overflow");
3✔
1171

1172
    value = (value * 10) + digit;
152✔
1173
    ++s_ptr;
152✔
1174
  }
1175

1176
  return value;
15✔
1177
}
1178

1179
//----------------------------------------------------------------------
1180
inline auto FString::internal_isOverflowed ( Sign sign
289✔
1181
                                           , long value
1182
                                           , long digit ) const noexcept -> bool
1183
{
1184
  const long limit_digit = ( sign == Sign::Negative ) ? LONG_MIN_LIMIT_DIGIT
289✔
1185
                                                      : LONG_MAX_LIMIT_DIGIT;
1186
  return ( value > LONG_LIMIT )
1187
      || ( value == LONG_LIMIT && digit > limit_digit );
289✔
1188
};
1189

1190
//----------------------------------------------------------------------
1191
inline auto FString::internal_isOverflowed ( uLong value
155✔
1192
                                           , uLong digit ) const noexcept -> bool
1193
{
1194
  return ( value > ULONG_LIMIT )
1195
      || ( value == ULONG_LIMIT && digit > ULONG_LIMIT_DIGIT );
155✔
1196
};
1197

1198

1199
// FString non-member operators
1200
//----------------------------------------------------------------------
1201
auto FStringCaseCompare (const FString& s1, const FString& s2) -> int
31✔
1202
{
1203
  if ( &s1 == &s2 )
31✔
1204
    return 0;
1✔
1205

1206
  auto iter1 = s1.cbegin();
30✔
1207
  auto iter2 = s2.cbegin();
30✔
1208
  const auto end1 = s1.cend();
30✔
1209
  const auto end2 = s2.cend();
30✔
1210

1211
  while ( iter1 != end1 && iter2 != end2 )
127✔
1212
  {
1213
    auto c1 = *iter1;
113✔
1214
    auto c2 = *iter2;
113✔
1215

1216
    // Convert to lowercase
1217
    if ( c1 >= L'A' && c1 <= L'Z' )
113✔
1218
      c1 += 32;
28✔
1219

1220
    if ( c2 >= L'A' && c2 <= L'Z' )
113✔
1221
      c2 += 32;
15✔
1222

1223
    if ( c1 != c2 )
113✔
1224
      return c1 - c2;
16✔
1225

1226
    ++iter1;
97✔
1227
    ++iter2;
97✔
1228
  }
1229

1230
  // Handle remaining characters
1231
  if ( iter1 != end1 )
14✔
1232
    return 1;  // s1 is longer
4✔
1233

1234
  if ( iter2 != end2 )
10✔
1235
    return -1; // s2 is longer
4✔
1236

1237
  return 0;    // Equal
6✔
1238
}
1239

1240

1241
}  // namespace finalcut
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