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

polserver / polserver / 21100551564

17 Jan 2026 08:40PM UTC coverage: 60.504% (+0.01%) from 60.492%
21100551564

Pull #857

github

turleypol
fixed scope
Pull Request #857: ClangTidy readability-else-after-return

837 of 1874 new or added lines in 151 files covered. (44.66%)

48 existing lines in 26 files now uncovered.

44445 of 73458 relevant lines covered (60.5%)

515341.61 hits per line

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

80.54
/pol-core/bscript/str.cpp
1
/** @file
2
 *
3
 * @par History
4
 * - 2007/12/09 Shinigami: removed ( is.peek() != EOF ) check from String::unpackWithLen()
5
 *                         will not work with Strings in Arrays, Dicts, etc.
6
 * - 2008/02/08 Turley:    String::unpackWithLen() will accept zero length Strings
7
 * - 2009/09/12 MuadDib:   Disabled 4244 in this file due to it being on a string iter. Makes no
8
 * sense.
9
 */
10

11
#include <cstdlib>
12
#include <ctype.h>
13
#include <cwctype>
14
#include <string>
15
#include <utf8cpp/utf8.h>
16

17
#include "../clib/clib_endian.h"
18
#include "../clib/stlutil.h"
19
#include "../clib/strutil.h"
20
#include "berror.h"
21
#include "bobject.h"
22
#include "contiter.h"
23
#include "executor.h"
24
#include "impstr.h"
25
#include "objmethods.h"
26
#include "regexp.h"
27
#include "str.h"
28

29
#ifdef __GNUG__
30
#include <streambuf>
31
#endif
32

33
#ifdef _MSC_VER
34
#include "../clib/Header_Windows.h"
35
#include <codecvt>
36
#endif
37

38

39
namespace Pol::Bscript
40
{
41
String::String( BObjectImp& objimp ) : BObjectImp( OTString ), value_( objimp.getStringRep() ) {}
×
42

43
String::String( const char* s, size_t len, Tainted san ) : BObjectImp( OTString ), value_( s, len )
19,823✔
44
{
45
  if ( san == Tainted::YES )
19,823✔
46
    Clib::sanitizeUnicodeWithIso( &value_ );
×
47
}
19,823✔
48

49
String::String( const std::string& str, std::string::size_type pos, std::string::size_type n )
6,821✔
50
    : BObjectImp( OTString ), value_( str, pos, n )
6,821✔
51
{
52
}
6,821✔
53

54
String::String( const char* str, Tainted san ) : BObjectImp( OTString ), value_( str )
110,025✔
55
{
56
  if ( san == Tainted::YES )
110,025✔
57
    Clib::sanitizeUnicodeWithIso( &value_ );
71✔
58
}
110,025✔
59

60
String::String( const std::string& str, Tainted san ) : BObjectImp( OTString ), value_( str )
62,986✔
61
{
62
  if ( san == Tainted::YES )
62,986✔
63
    Clib::sanitizeUnicodeWithIso( &value_ );
27,572✔
64
}
62,986✔
65

66
String::String( const std::string_view& str, Tainted san ) : BObjectImp( OTString ), value_( str )
10✔
67
{
68
  if ( san == Tainted::YES )
10✔
69
    Clib::sanitizeUnicodeWithIso( &value_ );
10✔
70
}
10✔
71

72
String::String( const std::wstring& str, Tainted san ) : BObjectImp( OTString )
147✔
73
{
74
  for ( const auto& c : str )
348✔
75
  {
76
    utf8::unchecked::append( c, std::back_inserter( value_ ) );
201✔
77
  }
78

79
  if ( san == Tainted::YES )
147✔
80
    Clib::sanitizeUnicodeWithIso( &value_ );
×
81
}
147✔
82

83
String* String::StrStr( int begin, int len ) const
39✔
84
{
85
  auto itr = value_.cbegin();
39✔
86
  --begin;
39✔
87
  size_t startpos = getBytePosition( &itr, begin );
39✔
88
  size_t endpos = getBytePosition( &itr, len );
39✔
89
  if ( startpos != std::string::npos )
39✔
90
    return new String( value_.substr( startpos, endpos - startpos ) );
36✔
91
  return new String( value_ );
3✔
92
}
93

94
size_t String::length() const
7,637✔
95
{
96
  return utf8::unchecked::distance( value_.begin(), value_.end() );
7,637✔
97
}
98

99
String* String::ETrim( const char* CRSet, int type ) const
6✔
100
{
101
  std::string tmp = value_;
6✔
102

103
  if ( type == 1 )  // This is for Leading Only.
6✔
104
  {
105
    // Find the first character position after excluding leading blank spaces
106
    size_t startpos = tmp.find_first_not_of( CRSet );
×
107
    if ( std::string::npos != startpos )
×
108
      tmp = tmp.substr( startpos );
×
109
    else
110
      tmp = "";
×
111
    return new String( tmp );
×
112
  }
113
  if ( type == 2 )  // This is for Trailing Only.
6✔
114
  {
115
    // Find the first character position from reverse
116
    size_t endpos = tmp.find_last_not_of( CRSet );
×
117
    if ( std::string::npos != endpos )
×
118
      tmp = tmp.substr( 0, endpos + 1 );
×
119
    else
120
      tmp = "";
×
121
    return new String( tmp );
×
122
  }
123
  if ( type == 3 )
6✔
124
  {
125
    // Find the first character position after excluding leading blank spaces
126
    size_t startpos = tmp.find_first_not_of( CRSet );
6✔
127
    // Find the first character position from reverse af
128
    size_t endpos = tmp.find_last_not_of( CRSet );
6✔
129

130
    // if all spaces or empty return on empty string
131
    if ( ( std::string::npos == startpos ) || ( std::string::npos == endpos ) )
6✔
132
      tmp = "";
×
133
    else
134
      tmp = tmp.substr( startpos, endpos - startpos + 1 );
6✔
135
    return new String( tmp );
6✔
136
  }
NEW
137
  return new String( tmp );
×
138
}
6✔
139

140
void String::EStrReplace( String* str1, String* str2 )
9✔
141
{
142
  std::string::size_type valpos = 0;
9✔
143
  while ( std::string::npos != ( valpos = value_.find( str1->value_, valpos ) ) )
15✔
144
  {
145
    value_.replace( valpos, str1->value_.size(), str2->value_ );
6✔
146
    valpos += str2->value_.size();
6✔
147
  }
148
}
9✔
149

150
void String::ESubStrReplace( String* replace_with, unsigned int index, unsigned int len )
6✔
151
{
152
  auto itr = value_.cbegin();
6✔
153
  size_t begin = getBytePosition( &itr, index - 1 );
6✔
154
  size_t end = getBytePosition( &itr, len );
6✔
155
  if ( begin != std::string::npos )
6✔
156
    value_.replace( begin, end - begin, replace_with->value_ );
6✔
157
}
6✔
158

159
std::string String::pack() const
23✔
160
{
161
  return "s" + value_;
23✔
162
}
163

164
void String::packonto( std::ostream& os ) const
48✔
165
{
166
  os << "S" << value_.size() << ":" << value_;
48✔
167
}
48✔
168
void String::packonto( std::ostream& os, const std::string& value )
15✔
169
{
170
  os << "S" << value.size() << ":" << value;
15✔
171
}
15✔
172

173
BObjectImp* String::unpack( std::istream& is )
25✔
174
{
175
  std::string tmp;
25✔
176
  getline( is, tmp );
25✔
177

178
  return new String( tmp );
50✔
179
}
25✔
180

181
BObjectImp* String::unpackWithLen( std::istream& is )
34✔
182
{
183
  unsigned len;
184
  char colon;
185
  if ( !( is >> len >> colon ) )
34✔
186
  {
187
    return new BError( "Unable to unpack string length." );
×
188
  }
189
  if ( (int)len < 0 )
34✔
190
  {
191
    return new BError( "Unable to unpack string length. Invalid length!" );
×
192
  }
193
  if ( colon != ':' )
34✔
194
  {
195
    return new BError( "Unable to unpack string length. Bad format. Colon not found!" );
×
196
  }
197

198
  is.unsetf( std::ios::skipws );
34✔
199
  std::string tmp;
34✔
200
  tmp.reserve( len );
34✔
201
  while ( len-- )
191✔
202
  {
203
    char ch = '\0';
157✔
204
    if ( !( is >> ch ) || ch == '\0' )
157✔
205
    {
206
      return new BError( "Unable to unpack string length. String length excessive." );
×
207
    }
208
    tmp += ch;
157✔
209
  }
210

211
  is.setf( std::ios::skipws );
34✔
212
  return new String( tmp );
34✔
213
}
34✔
214

215
size_t String::sizeEstimate() const
1,638✔
216
{
217
  return sizeof( String ) + value_.capacity();
1,638✔
218
}
219

220
/*
221
    0-based string find
222
    find( "str srch", 2, "srch"):
223
    01^-- start
224
    */
225
int String::find( int begin, const char* target ) const
19,843✔
226
{
227
  // returns -1 when begin is out of range for string
228
  auto itr = value_.cbegin();
19,843✔
229
  size_t pos = getBytePosition( &itr, begin );
19,843✔
230
  pos = value_.find( target, pos );
19,843✔
231
  if ( pos == std::string::npos )
19,843✔
232
    return -1;
16,297✔
233

234
  pos = utf8::unchecked::distance( value_.cbegin(), std::next( value_.cbegin(), pos ) );
3,546✔
235
  return static_cast<int>( pos );
3,546✔
236
}
237

238
unsigned int String::SafeCharAmt() const
24✔
239
{
240
  unsigned int strlen = static_cast<unsigned int>( length() );
24✔
241
  for ( unsigned int i = 0; i < strlen; ++i )
279✔
242
  {
243
    unsigned char tmp = value_[i];
255✔
244
    if ( tmp >= 0x80 )  // Ascii range
255✔
245
      return i;
×
246
    if ( isalnum( tmp ) )  // a-z A-Z 0-9
255✔
247
      continue;
237✔
248
    if ( ispunct( tmp ) )  // !"#$%&'()*+,-./:;<=>?@{|}~
18✔
249
    {
250
      if ( tmp == '{' || tmp == '}' )
18✔
251
        return i;
×
252
      continue;
18✔
253
    }
254

NEW
255
    return i;
×
256
  }
257
  return strlen;
24✔
258
}
259

260
BObjectImp* String::selfPlusObjImp( const BObjectImp& objimp ) const
7,420✔
261
{
262
  return objimp.selfPlusObj( *this );
7,420✔
263
}
264
BObjectImp* String::selfPlusObj( const BObjectImp& objimp ) const
1,151✔
265
{
266
  return new String( value_ + objimp.getStringRep() );
1,151✔
267
}
268
BObjectImp* String::selfPlusObj( const BLong& objimp ) const
2,030✔
269
{
270
  return new String( value_ + objimp.getStringRep() );
2,030✔
271
}
272
BObjectImp* String::selfPlusObj( const Double& objimp ) const
366✔
273
{
274
  return new String( value_ + objimp.getStringRep() );
366✔
275
}
276
BObjectImp* String::selfPlusObj( const String& objimp ) const
7,342✔
277
{
278
  return new String( value_ + objimp.getStringRep() );
7,342✔
279
}
280
BObjectImp* String::selfPlusObj( const ObjArray& objimp ) const
183✔
281
{
282
  return new String( value_ + objimp.getStringRep() );
183✔
283
}
284
void String::selfPlusObjImp( BObjectImp& objimp, BObject& obj )
138✔
285
{
286
  objimp.selfPlusObj( *this, obj );
138✔
287
}
138✔
288
void String::selfPlusObj( BObjectImp& objimp, BObject& /*obj*/ )
24✔
289
{
290
  value_ += objimp.getStringRep();
24✔
291
}
24✔
292
void String::selfPlusObj( BLong& objimp, BObject& /*obj*/ )
15✔
293
{
294
  value_ += objimp.getStringRep();
15✔
295
}
15✔
296
void String::selfPlusObj( Double& objimp, BObject& /*obj*/ )
12✔
297
{
298
  value_ += objimp.getStringRep();
12✔
299
}
12✔
300
void String::selfPlusObj( String& objimp, BObject& /*obj*/ )
120✔
301
{
302
  value_ += objimp.getStringRep();
120✔
303
}
120✔
304
void String::selfPlusObj( ObjArray& objimp, BObject& /*obj*/ )
9✔
305
{
306
  value_ += objimp.getStringRep();
9✔
307
}
9✔
308

309

310
void String::remove( const std::string& rm )
291✔
311
{
312
  auto pos = value_.find( rm );
291✔
313
  if ( pos != std::string::npos )
291✔
314
    value_.erase( pos, rm.size() );
60✔
315
}
291✔
316

317
BObjectImp* String::selfMinusObjImp( const BObjectImp& objimp ) const
165✔
318
{
319
  return objimp.selfMinusObj( *this );
165✔
320
}
321
BObjectImp* String::selfMinusObj( const BObjectImp& objimp ) const
33✔
322
{
323
  String* tmp = (String*)copy();
33✔
324
  tmp->remove( objimp.getStringRep() );
33✔
325
  return tmp;
33✔
326
}
327
BObjectImp* String::selfMinusObj( const BLong& objimp ) const
12✔
328
{
329
  String* tmp = (String*)copy();
12✔
330
  tmp->remove( objimp.getStringRep() );
12✔
331
  return tmp;
12✔
332
}
333
BObjectImp* String::selfMinusObj( const Double& objimp ) const
12✔
334
{
335
  String* tmp = (String*)copy();
12✔
336
  tmp->remove( objimp.getStringRep() );
12✔
337
  return tmp;
12✔
338
}
339
BObjectImp* String::selfMinusObj( const String& objimp ) const
138✔
340
{
341
  String* tmp = (String*)copy();
138✔
342
  tmp->remove( objimp.value_ );
138✔
343
  return tmp;
138✔
344
}
345
BObjectImp* String::selfMinusObj( const ObjArray& objimp ) const
×
346
{
347
  String* tmp = (String*)copy();
×
348
  tmp->remove( objimp.getStringRep() );
×
349
  return tmp;
×
350
}
351
void String::selfMinusObjImp( BObjectImp& objimp, BObject& obj )
57✔
352
{
353
  objimp.selfMinusObj( *this, obj );
57✔
354
}
57✔
355
void String::selfMinusObj( BObjectImp& objimp, BObject& /*obj*/ )
33✔
356
{
357
  remove( objimp.getStringRep() );
33✔
358
}
33✔
359
void String::selfMinusObj( BLong& objimp, BObject& /*obj*/ )
12✔
360
{
361
  remove( objimp.getStringRep() );
12✔
362
}
12✔
363
void String::selfMinusObj( Double& objimp, BObject& /*obj*/ )
12✔
364
{
365
  remove( objimp.getStringRep() );
12✔
366
}
12✔
367
void String::selfMinusObj( String& objimp, BObject& /*obj*/ )
39✔
368
{
369
  remove( objimp.value_ );
39✔
370
}
39✔
371
void String::selfMinusObj( ObjArray& objimp, BObject& /*obj*/ )
×
372
{
373
  remove( objimp.getStringRep() );
×
374
}
×
375

376
bool String::operator==( const BObjectImp& objimp ) const
26,859✔
377
{
378
  if ( objimp.isa( OTString ) )
26,859✔
379
    return ( value_ == static_cast<const String&>( objimp ).value_ );
26,689✔
380

381
  if ( objimp.isa( OTBoolean ) )
170✔
382
    return isTrue() == static_cast<const BBoolean&>( objimp ).isTrue();
3✔
383
  return base::operator==( objimp );
167✔
384
}
385

386
bool String::operator<( const BObjectImp& objimp ) const
2,487✔
387
{
388
  if ( objimp.isa( OTString ) )
2,487✔
389
    return ( value_ < static_cast<const String&>( objimp ).value_ );
2,270✔
390

391
  return base::operator<( objimp );
217✔
392
}
393

394
void String::toUpper()
42✔
395
{
396
  if ( !hasUTF8Characters() )
42✔
397
  {
398
    Clib::mkupperASCII( value_ );
33✔
399
    return;
33✔
400
  }
401
#ifndef WINDOWS
402
  auto wstr = Clib::to_wstring( value_ );
9✔
403
  value_.clear();
9✔
404
  for ( const auto& c : wstr )
237✔
405
  {
406
    utf8::unchecked::append( std::towupper( c ), std::back_inserter( value_ ) );
228✔
407
  }
408
#else
409
  std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
410
  std::wstring str = converter.from_bytes( value_ );
411

412
  int len = LCMapStringW( LOCALE_USER_DEFAULT, LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING, &str[0],
413
                          static_cast<int>( str.size() ), 0, 0 );
414
  if ( !len )
415
    return;
416
  else if ( len == static_cast<int>( str.size() ) )
417
  {
418
    LCMapStringW( LOCALE_USER_DEFAULT, LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING, &str[0],
419
                  static_cast<int>( str.size() ), &str[0], static_cast<int>( str.size() ) );
420
    value_ = converter.to_bytes( str );
421
  }
422
  else
423
  {
424
    std::wstring buf;
425
    buf.reserve( len );
426
    LCMapStringW( LOCALE_USER_DEFAULT, LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING, &str[0],
427
                  static_cast<int>( str.size() ), &buf[0], static_cast<int>( buf.size() ) );
428
    value_ = converter.to_bytes( buf );
429
  }
430
#endif
431
}
9✔
432

433
void String::toLower()
27✔
434
{
435
  if ( !hasUTF8Characters() )
27✔
436
  {
437
    Clib::mklowerASCII( value_ );
18✔
438
    return;
18✔
439
  }
440
#ifndef WINDOWS
441
  auto wstr = Clib::to_wstring( value_ );
9✔
442
  value_.clear();
9✔
443
  for ( const auto& c : wstr )
237✔
444
  {
445
    utf8::unchecked::append( std::towlower( c ), std::back_inserter( value_ ) );
228✔
446
  }
447
#else
448
  std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
449
  std::wstring str = converter.from_bytes( value_ );
450

451
  int len = LCMapStringW( LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_LINGUISTIC_CASING, &str[0],
452
                          static_cast<int>( str.size() ), 0, 0 );
453
  if ( !len )
454
    return;
455
  else if ( len == static_cast<int>( str.size() ) )
456
  {
457
    LCMapStringW( LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_LINGUISTIC_CASING, &str[0],
458
                  static_cast<int>( str.size() ), &str[0], static_cast<int>( str.size() ) );
459
    value_ = converter.to_bytes( str );
460
  }
461
  else
462
  {
463
    std::wstring buf;
464
    buf.reserve( len );
465
    LCMapStringW( LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_LINGUISTIC_CASING, &str[0],
466
                  static_cast<int>( str.size() ), &buf[0], static_cast<int>( buf.size() ) );
467
    value_ = converter.to_bytes( buf );
468
  }
469
#endif
470
}
9✔
471

472
size_t String::getBytePosition( std::string::const_iterator* itr, size_t codeindex ) const
52,959✔
473
{
474
  auto itr_end = value_.cend();
52,959✔
475
  for ( size_t i = 0; i < codeindex && *itr != itr_end; ++i )
111,802✔
476
    utf8::unchecked::next( *itr );
58,843✔
477

478
  if ( *itr != itr_end )
52,959✔
479
  {
480
    return std::distance( value_.cbegin(), *itr );
51,300✔
481
  }
482
  return std::string::npos;
1,659✔
483
}
484

485
BObjectImp* String::array_assign( BObjectImp* idx, BObjectImp* target, bool /*copy*/ )
15✔
486
{
487
  std::string::size_type pos, len;
488

489
  // first, determine position and length.
490
  if ( idx->isa( OTString ) )
15✔
491
  {
492
    String& rtstr = (String&)*idx;
12✔
493
    pos = value_.find( rtstr.value_ );
12✔
494
    len = rtstr.value_.size();
12✔
495
  }
496
  else if ( idx->isa( OTLong ) )
3✔
497
  {
498
    BLong& lng = (BLong&)*idx;
3✔
499
    len = 1;
3✔
500
    pos = lng.value() - 1;
3✔
501
    auto itr = value_.cbegin();
3✔
502
    pos = getBytePosition( &itr, pos );
3✔
503
    if ( pos != std::string::npos )
3✔
504
    {
505
      utf8::unchecked::next( itr );
3✔
506
      len = std::distance( value_.cbegin(), itr ) - pos;
3✔
507
    }
508
    else
509
      pos = std::string::npos;
×
510
  }
511
  else if ( idx->isa( OTDouble ) )
×
512
  {
513
    Double& dbl = (Double&)*idx;
×
514
    pos = static_cast<std::string::size_type>( dbl.value() ) - 1;
×
515
    len = 1;
×
516
    auto itr = value_.cbegin();
×
517
    pos = getBytePosition( &itr, pos );
×
518
    if ( pos != std::string::npos )
×
519
    {
520
      utf8::unchecked::next( itr );
×
521
      len = std::distance( value_.cbegin(), itr ) - pos;
×
522
    }
523
    else
524
      pos = std::string::npos;
×
525
  }
526
  else
527
  {
528
    return UninitObject::create();
×
529
  }
530

531
  if ( pos != std::string::npos )
15✔
532
  {
533
    if ( target->isa( OTString ) )
15✔
534
    {
535
      String* target_str = (String*)target;
15✔
536
      value_.replace( pos, len, target_str->value_ );
15✔
537
    }
538
    return this;
15✔
539
  }
540

NEW
541
  return UninitObject::create();
×
542
}
543

544
BObjectRef String::OperMultiSubscriptAssign( std::stack<BObjectRef>& indices, BObjectImp* target )
6✔
545
{
546
  BObjectRef start_ref = indices.top();
6✔
547
  indices.pop();
6✔
548
  BObjectRef length_ref = indices.top();
6✔
549
  indices.pop();
6✔
550

551
  BObject& length_obj = *length_ref;
6✔
552
  BObject& start_obj = *start_ref;
6✔
553

554
  BObjectImp& length = length_obj.impref();
6✔
555
  BObjectImp& start = start_obj.impref();
6✔
556

557
  // first deal with the start position.
558
  size_t index;
559
  if ( start.isa( OTLong ) )
6✔
560
  {
561
    BLong& lng = (BLong&)start;
6✔
562
    index = (size_t)lng.value();
6✔
563
    if ( index == 0 || index > value_.size() )
6✔
564
      return BObjectRef( new BError( "Subscript out of range" ) );
×
565
    --index;
6✔
566
    auto itr = value_.cbegin();
6✔
567
    index = getBytePosition( &itr, index );
6✔
568
    if ( index == std::string::npos )
6✔
569
      return BObjectRef( new BError( "Subscript out of range" ) );
×
570
  }
571
  else if ( start.isa( OTString ) )
×
572
  {
573
    String& rtstr = (String&)start;
×
574
    std::string::size_type pos = value_.find( rtstr.value_ );
×
575
    if ( pos != std::string::npos )
×
576
      index = static_cast<size_t>( pos );
×
577
    else
578
      return BObjectRef( new UninitObject );
×
579
  }
580
  else
581
  {
582
    return BObjectRef( copy() );
×
583
  }
584

585
  // now deal with the length.
586
  size_t len;
587
  if ( length.isa( OTLong ) )
6✔
588
  {
589
    BLong& lng = (BLong&)length;
6✔
590

591
    len = (size_t)lng.value();
6✔
592
  }
593
  else if ( length.isa( OTDouble ) )
×
594
  {
595
    Double& dbl = (Double&)length;
×
596

597
    len = (size_t)dbl.value();
×
598
  }
599
  else
600
  {
601
    return BObjectRef( copy() );
×
602
  }
603
  auto itr = value_.cbegin();
6✔
604
  std::advance( itr, index );
6✔
605
  size_t index_len = getBytePosition( &itr, len );
6✔
606

607
  if ( index_len != std::string::npos )
6✔
608
    len = index_len - index;
6✔
609
  else
610
    len = index_len;
×
611

612
  if ( target->isa( OTString ) )
6✔
613
  {
614
    String* target_str = (String*)target;
6✔
615
    value_.replace( index, len, target_str->value_ );
6✔
616
  }
617
  else
618
  {
619
    return BObjectRef( UninitObject::create() );
×
620
  }
621

622
  return BObjectRef( this );
6✔
623
}
6✔
624

625

626
BObjectRef String::OperMultiSubscript( std::stack<BObjectRef>& indices )
7,217✔
627
{
628
  BObjectRef start_ref = indices.top();
7,217✔
629
  indices.pop();
7,217✔
630
  BObjectRef length_ref = indices.top();
7,217✔
631
  indices.pop();
7,217✔
632

633
  BObject& length_obj = *length_ref;
7,217✔
634
  BObject& start_obj = *start_ref;
7,217✔
635

636
  BObjectImp& length = length_obj.impref();
7,217✔
637
  BObjectImp& start = start_obj.impref();
7,217✔
638

639
  // first deal with the start position.
640
  size_t index;
641
  if ( start.isa( OTLong ) )
7,217✔
642
  {
643
    BLong& lng = (BLong&)start;
7,211✔
644
    index = (size_t)lng.value();
7,211✔
645
    if ( index == 0 || index > value_.size() )
7,211✔
646
      return BObjectRef( new BError( "Subscript out of range" ) );
1,264✔
647
    --index;
6,579✔
648
    auto itr = value_.cbegin();
6,579✔
649
    index = getBytePosition( &itr, index );
6,579✔
650
    if ( index == std::string::npos )
6,579✔
651
      return BObjectRef( new BError( "Subscript out of range" ) );
×
652
  }
653
  else if ( start.isa( OTString ) )
6✔
654
  {
655
    String& rtstr = (String&)start;
6✔
656
    std::string::size_type pos = value_.find( rtstr.value_ );
6✔
657
    if ( pos != std::string::npos )
6✔
658
      index = static_cast<unsigned int>( pos );
6✔
659
    else
660
      return BObjectRef( new UninitObject );
×
661
  }
662
  else
663
  {
664
    return BObjectRef( copy() );
×
665
  }
666

667
  // now deal with the length.
668
  size_t len;
669
  if ( length.isa( OTLong ) )
6,585✔
670
  {
671
    BLong& lng = (BLong&)length;
6,585✔
672

673
    len = (size_t)lng.value();
6,585✔
674
  }
675
  else if ( length.isa( OTDouble ) )
×
676
  {
677
    Double& dbl = (Double&)length;
×
678

679
    len = (size_t)dbl.value();
×
680
  }
681
  else
682
  {
683
    return BObjectRef( copy() );
×
684
  }
685
  auto itr = value_.cbegin();
6,585✔
686
  std::advance( itr, index );
6,585✔
687
  size_t index_len = getBytePosition( &itr, len );
6,585✔
688

689
  if ( index_len != std::string::npos )
6,585✔
690
    len = index_len - index;
5,467✔
691
  else
692
    len = index_len;
1,118✔
693
  return BObjectRef( new String( value_, index, len ) );
6,585✔
694
}
7,217✔
695

696
BObjectRef String::OperSubscript( const BObject& rightobj )
22,408✔
697
{
698
  const BObjectImp& right = rightobj.impref();
22,408✔
699
  if ( right.isa( OTLong ) )
22,408✔
700
  {
701
    BLong& lng = (BLong&)right;
19,835✔
702

703
    if ( lng.value() < 0 )
19,835✔
704
      return BObjectRef( new BError( "Subscript out of range" ) );
3✔
705

706
    size_t index = (size_t)lng.value();
19,832✔
707

708
    if ( index == 0 || index > value_.size() )
19,832✔
709
      return BObjectRef( new BError( "Subscript out of range" ) );
9✔
710

711
    --index;
19,823✔
712
    auto itr = value_.cbegin();
19,823✔
713
    index = getBytePosition( &itr, index );
19,823✔
714
    if ( index != std::string::npos )
19,823✔
715
    {
716
      utf8::unchecked::next( itr );
19,823✔
717
      size_t len = std::distance( value_.cbegin(), itr ) - index;
19,823✔
718
      return BObjectRef( new BObject( new String( value_.c_str() + index, len ) ) );
19,823✔
719
    }
720
    return BObjectRef( new BError( "Subscript out of range" ) );
×
721
  }
722
  if ( right.isa( OTDouble ) )
2,573✔
723
  {
724
    Double& dbl = (Double&)right;
9✔
725

726
    if ( dbl.value() < 0 )
9✔
727
      return BObjectRef( new BError( "Subscript out of range" ) );
3✔
728
    size_t index = (size_t)dbl.value();
6✔
729

730
    if ( index == 0 || index > value_.size() )
6✔
731
      return BObjectRef( new BError( "Subscript out of range" ) );
6✔
732

733
    --index;
×
734
    auto itr = value_.cbegin();
×
735
    index = getBytePosition( &itr, index );
×
736
    if ( index != std::string::npos )
×
737
    {
738
      utf8::unchecked::next( itr );
×
739
      size_t len = std::distance( value_.cbegin(), itr ) - index;
×
740
      return BObjectRef( new BObject( new String( value_.c_str() + index, len ) ) );
×
741
    }
742
    return BObjectRef( new BError( "Subscript out of range" ) );
×
743
  }
744
  if ( right.isa( OTString ) )
2,564✔
745
  {
746
    String& rtstr = (String&)right;
2,543✔
747
    auto pos = value_.find( rtstr.value_ );
2,543✔
748
    if ( pos != std::string::npos )
2,543✔
749
    {
750
      auto itr = value_.cbegin();
236✔
751
      std::advance( itr, pos );
236✔
752
      utf8::unchecked::next( itr );
236✔
753
      size_t len = std::distance( value_.cbegin(), itr ) - pos;
236✔
754
      return BObjectRef( new BObject( new String( value_, pos, len ) ) );
236✔
755
    }
756
    return BObjectRef( new UninitObject );
2,307✔
757
  }
758

759
  return BObjectRef( new UninitObject );
21✔
760
}
761

762
// -- format related stuff --
763
bool s_parse_int( int& i, std::string const& s )
39✔
764
{
765
  if ( s.empty() )
39✔
766
    return false;
×
767

768
  char* end;
769
  i = strtol( s.c_str(), &end, 10 );
39✔
770

771
  if ( !*end )
39✔
772
  {
773
    return true;
27✔
774
  }
775

776
  return false;
12✔
777
}
778

779
void int_to_binstr( int& value, std::stringstream& s )
×
780
{
781
  int i;
782
  for ( i = 31; i > 0; i-- )
×
783
  {
784
    if ( value & ( 1 << i ) )
×
785
      break;
×
786
  }
787
  for ( ; i >= 0; i-- )
×
788
  {
789
    if ( value & ( 1 << i ) )
×
790
      s << "1";
×
791
    else
792
      s << "0";
×
793
  }
794
}
×
795

796
// suplementory function to format
797
bool try_to_format( std::stringstream& to_stream, BObjectImp* what, std::string& frmt )
1,237✔
798
{
799
  if ( frmt.empty() )
1,237✔
800
  {
801
    to_stream << what->getStringRep();
1,029✔
802
    return false;
1,029✔
803
  }
804

805
  if ( frmt.find( 'b' ) != std::string::npos )
208✔
806
  {
807
    if ( auto* plong = impptrIf<BLong>( what ) )
×
808
    {
809
      int n = plong->value();
×
810
      if ( frmt.find( '#' ) != std::string::npos )
×
811
        to_stream << ( ( n < 0 ) ? "-" : "" ) << "0b";
×
812
      int_to_binstr( n, to_stream );
×
813
    }
814
    else
815
    {
816
      to_stream << "<needs Int>";
×
817
      return false;
×
818
    }
819
  }
820
  else if ( frmt.find( 'x' ) != std::string::npos )
208✔
821
  {
822
    if ( auto* plong = impptrIf<BLong>( what ) )
208✔
823
    {
824
      int n = plong->value();
179✔
825
      if ( frmt.find( '#' ) != std::string::npos )
179✔
826
        to_stream << "0x";
9✔
827
      to_stream << std::hex << n << std::dec;
179✔
828
    }
829
    else
830
    {
831
      to_stream << "<needs Int>";
29✔
832
      return false;
29✔
833
    }
834
  }
835
  else if ( frmt.find( 'o' ) != std::string::npos )
×
836
  {
837
    if ( auto* plong = impptrIf<BLong>( what ) )
×
838
    {
839
      int n = plong->value();
×
840
      if ( frmt.find( '#' ) != std::string::npos )
×
841
        to_stream << "0o";
×
842
      to_stream << std::oct << n << std::dec;
×
843
    }
844
    else
845
    {
846
      to_stream << "<needs Int>";
×
847
      return false;
×
848
    }
849
  }
850
  else if ( frmt.find( 'd' ) != std::string::npos )
×
851
  {
852
    int n;
853
    if ( auto* plong = impptrIf<BLong>( what ) )
×
854
      n = plong->value();
×
855
    else if ( auto* pdbl = impptrIf<Double>( what ) )
×
856
      n = (int)pdbl->value();
×
857
    else
858
    {
859
      to_stream << "<needs Int, Double>";
×
860
      return false;
×
861
    }
862
    to_stream << std::dec << n;
×
863
  }
864
  else
865
  {
866
    to_stream << "<bad format: " << frmt << ">";
×
867
    return false;
×
868
  }
869
  return true;
179✔
870
}
871
// --
872

873
std::string get_formatted( BObjectImp* what, std::string& frmt )
196✔
874
{
875
  std::stringstream result;
196✔
876
  try_to_format( result, what, frmt );
196✔
877
  return result.str();
392✔
878
}
196✔
879

880
BObjectImp* String::call_method( const char* methodname, Executor& ex )
×
881
{
882
  ObjMethod* objmethod = getKnownObjMethod( methodname );
×
883
  if ( objmethod != nullptr )
×
884
    return this->call_method_id( objmethod->id, ex );
×
NEW
885
  return nullptr;
×
886
}
887
BObjectImp* String::call_method_id( const int id, Executor& ex, bool /*forcebuiltin*/ )
22,688✔
888
{
889
  switch ( id )
22,688✔
890
  {
891
  case MTH_LENGTH:
5,641✔
892
    if ( ex.numParams() == 0 )
5,641✔
893
      return new BLong( static_cast<int>( length() ) );
5,641✔
894
    else
895
      return new BError( "string.length() doesn't take parameters." );
×
896
    break;
897
  case MTH_FIND:
15,873✔
898
  {
899
    if ( ex.numParams() > 2 )
15,873✔
900
      return new BError( "string.find(Search, [Start]) takes only two parameters" );
×
901
    if ( ex.numParams() < 1 )
15,873✔
902
      return new BError( "string.find(Search, [Start]) takes at least one parameter" );
×
903

904
    int d = 0;
15,873✔
905
    if ( ex.numParams() == 2 )
15,873✔
906
      d = ex.paramAsLong( 1 ) - 1;
107✔
907

908
    if ( auto s = impptrIf<String>( ex.getParamImp( 0 ) ) )
15,873✔
909
    {
910
      int posn = find( d, s->data() ) + 1;
15,789✔
911
      return new BLong( posn );
15,789✔
912
    }
913
    if ( auto regex = impptrIf<BRegExp>( ex.getParamImp( 0 ) ) )
84✔
914
    {
915
      return regex->find( this, d );
84✔
916
    }
NEW
917
    return new BError( "string.find(Search, [Start]): Search must be a string or regex" );
×
918
  }
919
  case MTH_MATCH:
63✔
920
  {
921
    if ( ex.numParams() != 1 )
63✔
922
      return new BError( "string.match(Pattern) takes exactly one parameter" );
6✔
923

924
    auto regex = impptrIf<BRegExp>( ex.getParamImp( 0 ) );
57✔
925

926
    if ( !regex )
57✔
927
      return new BError( "string.match(Pattern): Pattern must be a RegExp" );
6✔
928

929
    return regex->match( this );
51✔
930
  }
931
  case MTH_REPLACE:
75✔
932
  {
933
    if ( ex.numParams() != 2 )
75✔
934
      return new BError( "string.replace(Search, Replace) takes exactly two parameters" );
6✔
935

936
    auto regex = impptrIf<BRegExp>( ex.getParamImp( 0 ) );
69✔
937
    if ( !regex )
69✔
938
      return new BError( "string.replace(Search, Replace): Search must be a RegExp" );
6✔
939

940
    if ( auto s = impptrIf<String>( ex.getParamImp( 1 ) ) )
63✔
941
    {
942
      return regex->replace( this, s );
36✔
943
    }
944
    if ( auto funcref = impptrIf<BFunctionRef>( ex.getParamImp( 1 ) ) )
27✔
945
    {
946
      return regex->replace( ex, this, funcref );
21✔
947
    }
948

949
    return new BError( "string.replace(Search, Replace): Replace must be a string or function" );
6✔
950
  }
951
  case MTH_SPLIT:
57✔
952
  {
953
    size_t limit = std::numeric_limits<size_t>::max();
57✔
954

955
    if ( ex.numParams() == 0 )
57✔
956
      return new BError( "string.split(Separator[, Max_Split]) takes at least one parameter" );
×
957
    if ( ex.numParams() > 2 )
57✔
958
      return new BError( "string.split(Separator[, Max_Split]) takes at most two parameters" );
×
959

960
    if ( ex.numParams() == 2 )
57✔
961
    {
962
      int max_splits = ex.paramAsLong( 1 );
21✔
963
      if ( max_splits > -1 )
21✔
964
        limit = Clib::clamp_convert<size_t>( max_splits + 1 );
21✔
965
    }
966

967
    if ( auto regex = impptrIf<BRegExp>( ex.getParamImp( 0 ) ) )
57✔
968
    {
969
      return regex->split( this, limit );
30✔
970
    }
971
    if ( auto string_sep = impptrIf<String>( ex.getParamImp( 0 ) ) )
27✔
972
    {
973
      std::unique_ptr<ObjArray> result( new ObjArray );
27✔
974
      const auto& sep = string_sep->value_;
27✔
975

976
      // Empty separator splits into characters
977
      if ( sep.empty() )
27✔
978
      {
979
        return getCharacters( value_, limit );
12✔
980
      }
981

982
      // Has a non-empty separator, splits by `sep`
983
      std::size_t start = 0;
15✔
984
      while ( start < value_.size() )
60✔
985
      {
986
        // Limit reached, push rest and break
987
        if ( result->ref_arr.size() == limit - 1 )
57✔
988
        {
989
          result->addElement( new String( value_.substr( start ) ) );
9✔
990
          break;
9✔
991
        }
992

993
        std::size_t pos = value_.find( sep, start );
48✔
994
        if ( pos == std::string::npos )
48✔
995
        {
996
          // No more separators, push rest
997
          result->addElement( new String( value_.substr( start ) ) );
3✔
998
          break;
3✔
999
        }
1000

1001
        // Extract part
1002
        result->addElement( new String( value_.substr( start, pos - start ) ) );
45✔
1003
        start = pos + sep.size();
45✔
1004
      }
1005

1006
      return result.release();
15✔
1007
    }
27✔
1008

1009
    return new BError(
NEW
1010
        "string.split(Separator[, Max_Split]): Separator must be a RegExp or string" );
×
1011
  }
1012

1013
  case MTH_UPPER:
9✔
1014
  {
1015
    if ( ex.numParams() == 0 )
9✔
1016
    {
1017
      toUpper();
9✔
1018
      return this;
9✔
1019
    }
NEW
1020
    return new BError( "string.upper() doesn't take parameters." );
×
1021
  }
1022

1023
  case MTH_LOWER:
9✔
1024
  {
1025
    if ( ex.numParams() == 0 )
9✔
1026
    {
1027
      toLower();
9✔
1028
      return this;
9✔
1029
    }
NEW
1030
    return new BError( "string.lower() doesn't take parameters." );
×
1031
  }
1032
  case MTH_FORMAT:
929✔
1033
  {
1034
    if ( ex.numParams() > 0 )
929✔
1035
    {
1036
      // string s = this->getStringRep(); // string itself
1037
      std::stringstream result;
929✔
1038

1039
      size_t tag_start_pos;  // the position of tag's start "{"
1040
      size_t tag_stop_pos;   // the position of tag's end "}"
1041
      size_t tag_dot_pos;
1042

1043
      int tag_param_idx;
1044

1045
      size_t str_pos = 0;               // current string position
929✔
1046
      unsigned int next_param_idx = 0;  // next index of .format() parameter
929✔
1047

1048
      char w_spaces[] = "\t ";
929✔
1049

1050
      // Tells whether last found tag was an integer
1051
      bool last_tag_was_int = true;
929✔
1052

1053
      while ( ( tag_start_pos = value_.find( '{', str_pos ) ) != std::string::npos )
1,973✔
1054
      {
1055
        if ( ( tag_stop_pos = value_.find( '}', tag_start_pos ) ) != std::string::npos )
1,044✔
1056
        {
1057
          result << value_.substr( str_pos, tag_start_pos - str_pos );
1,044✔
1058
          str_pos = tag_stop_pos + 1;
1,044✔
1059

1060
          std::string tag_body =
1061
              value_.substr( tag_start_pos + 1, ( tag_stop_pos - tag_start_pos ) - 1 );
1,044✔
1062

1063
          tag_start_pos = tag_body.find_first_not_of( w_spaces );
1,044✔
1064
          tag_stop_pos = tag_body.find_last_not_of( w_spaces );
1,044✔
1065

1066
          // cout << "' tag_body1: '" << tag_body << "'";
1067

1068
          // trim the tag of whitespaces (slightly faster code ~25%)
1069
          if ( tag_start_pos != std::string::npos && tag_stop_pos != std::string::npos )
1,044✔
1070
            tag_body = tag_body.substr( tag_start_pos, ( tag_stop_pos - tag_start_pos ) + 1 );
51✔
1071
          else if ( tag_start_pos != std::string::npos )
993✔
1072
            tag_body = tag_body.substr( tag_start_pos );
×
1073
          else if ( tag_stop_pos != std::string::npos )
993✔
1074
            tag_body = tag_body.substr( 0, tag_stop_pos + 1 );
×
1075

1076
          // cout << "' tag_body2: '" << tag_body << "'";
1077

1078
          std::string frmt;
1,044✔
1079
          size_t formatter_pos = tag_body.find( ':' );
1,044✔
1080

1081
          if ( formatter_pos != std::string::npos )
1,044✔
1082
          {
1083
            frmt = tag_body.substr( formatter_pos + 1, std::string::npos );  //
12✔
1084
            tag_body = tag_body.substr( 0, formatter_pos );  // remove property from the tag
12✔
1085
          }
1086

1087
          std::string prop_name;
1,044✔
1088
          // parsing {1.this_part}
1089
          tag_dot_pos = tag_body.find( '.', 0 );
1,044✔
1090

1091
          // '.' is found within the tag, there is a property name
1092
          if ( tag_dot_pos != std::string::npos )
1,044✔
1093
          {
1094
            last_tag_was_int = true;
12✔
1095
            prop_name = tag_body.substr( tag_dot_pos + 1, std::string::npos );  //
12✔
1096
            tag_body = tag_body.substr( 0, tag_dot_pos );  // remove property from the tag
12✔
1097

1098
            // if s_tag_body is numeric then use it as an index
1099
            if ( s_parse_int( tag_param_idx, tag_body ) )
12✔
1100
            {
1101
              tag_param_idx -= 1;  // sinse POL indexes are 1-based
12✔
1102
            }
1103
            else
1104
            {
1105
              result << "<idx required before: '" << prop_name << "'>";
×
1106
              continue;
×
1107
            }
1108
          }
1109
          else
1110
          {
1111
            if ( tag_body.empty() )
1,032✔
1112
            {
1113
              // empty body just takes next integer idx
1114
              last_tag_was_int = true;
1,005✔
1115
              tag_param_idx = next_param_idx++;
1,005✔
1116
            }
1117
            else if ( s_parse_int( tag_param_idx, tag_body ) )
27✔
1118
            {
1119
              last_tag_was_int = true;
15✔
1120
              tag_param_idx -= 1;  // sinse POL indexes are 1-based
15✔
1121
            }
1122
            else
1123
            {
1124
              // string body takes next idx in line if this is
1125
              // the first string body occurrence,
1126
              // will reuse last idx if this is 2nd or more in a row
1127
              last_tag_was_int = false;
12✔
1128
              prop_name = tag_body;
12✔
1129
              tag_param_idx = last_tag_was_int ? next_param_idx++ : next_param_idx;
12✔
1130
            }
1131
          }
1132

1133
          // -- end of property parsing
1134

1135
          // cout << "prop_name: '" << prop_name << "' tag_body: '" << tag_body << "'";
1136
          if ( tag_param_idx < 0 || (int)ex.numParams() <= tag_param_idx )
1,044✔
1137
          {
1138
            result << "<invalid index: #" << ( tag_param_idx + 1 ) << ">";
3✔
1139
            continue;
3✔
1140
          }
1141

1142
          BObjectImp* imp = ex.getParamImp( tag_param_idx );
1,041✔
1143

1144
          if ( !prop_name.empty() )
1,041✔
1145
          {  // accesing object
1146
            BObjectRef obj_member = imp->get_member( prop_name.c_str() );
24✔
1147
            BObjectImp* member_imp = obj_member->impptr();
24✔
1148
            try_to_format( result, member_imp, frmt );
24✔
1149
          }
24✔
1150
          else
1151
          {
1152
            try_to_format( result, imp, frmt );
1,017✔
1153
          }
1154
        }
1,050✔
1155
        else
1156
        {
1157
          break;
×
1158
        }
1159
      }
1160

1161
      if ( str_pos < value_.length() )
929✔
1162
      {
1163
        result << value_.substr( str_pos, std::string::npos );
790✔
1164
      }
1165

1166
      return new String( result.str() );
929✔
1167
    }
929✔
1168

NEW
1169
    return new BError( "string.format() requires a parameter." );
×
1170
  }
1171
  case MTH_JOIN:
26✔
1172
  {
1173
    BObject* cont;
1174
    if ( ex.numParams() == 1 && ( cont = ex.getParamObj( 0 ) ) != nullptr )
26✔
1175
    {
1176
      if ( !( cont->isa( OTArray ) ) )
26✔
1177
        return new BError( "string.join expects an array" );
×
1178
      ObjArray* container = cont->impptr<ObjArray>();
26✔
1179
      // no empty check here on purpose
1180
      OSTRINGSTREAM joined;
26✔
1181
      bool first = true;
26✔
1182
      for ( const BObjectRef& ref : container->ref_arr )
191✔
1183
      {
1184
        if ( ref.get() )
165✔
1185
        {
1186
          BObject* bo = ref.get();
153✔
1187

1188
          if ( bo == nullptr )
153✔
1189
            continue;
×
1190
          if ( !first )
153✔
1191
            joined << value_;
130✔
1192
          else
1193
            first = false;
23✔
1194
          joined << bo->impptr()->getStringRep();
153✔
1195
        }
1196
      }
1197
      return new String( OSTRINGSTREAM_STR( joined ) );
26✔
1198
    }
26✔
NEW
1199
    return new BError( "string.join(array) requires a parameter." );
×
1200
  }
1201
  default:
6✔
1202
    return nullptr;
6✔
1203
  }
1204
}
1205

1206
ObjArray* String::getCharacters( const std::string& source, size_t limit )
27✔
1207
{
1208
  std::unique_ptr<ObjArray> result( new ObjArray );
27✔
1209

1210
  for ( auto rit = source.cbegin(); rit != source.cend(); )
150✔
1211
  {
1212
    // Limit reached, push rest and break
1213
    if ( result->ref_arr.size() == limit - 1 )
138✔
1214
    {
1215
      result->addElement( new String( std::string( rit, source.cend() ) ) );
15✔
1216
      break;
15✔
1217
    }
1218

1219
    auto previous = rit;
123✔
1220
    utf8::unchecked::next( rit );
123✔
1221

1222
    result->addElement( new String( std::string( previous, rit ) ) );
123✔
1223
  }
1224

1225
  return result.release();
54✔
1226
}
27✔
1227

1228
bool String::hasUTF8Characters() const
70✔
1229
{
1230
  return hasUTF8Characters( value_ );
70✔
1231
}
1232

1233
bool String::hasUTF8Characters( const std::string& str )
71✔
1234
{
1235
  for ( const auto& c : str )
723✔
1236
  {
1237
    if ( c & 0x80 )
670✔
1238
      return true;
18✔
1239
  }
1240
  return false;
53✔
1241
}
1242

1243
std::vector<unsigned short> String::toUTF16() const
48✔
1244
{
1245
  std::vector<unsigned short> u16;
48✔
1246
  utf8::unchecked::utf8to16( value_.begin(), value_.end(), std::back_inserter( u16 ) );
48✔
1247
  return u16;
48✔
1248
}
×
1249

1250
std::string String::fromUTF16( unsigned short code )
436✔
1251
{
1252
  std::string s;
436✔
1253
  std::vector<unsigned short> utf16( 1, code );
436✔
1254
  utf8::unchecked::utf16to8( utf16.begin(), utf16.end(), std::back_inserter( s ) );
436✔
1255
  Clib::sanitizeUnicode( &s );
436✔
1256
  return s;
872✔
1257
}
436✔
1258

1259
std::string String::fromUTF16( const unsigned short* code, size_t len, bool big_endian )
1✔
1260
{
1261
  std::string s;
1✔
1262
  size_t short_len = 0;
1✔
1263
  // convert until the first null terminator
1264
  while ( code[short_len] != 0 && short_len < len )
11✔
1265
    ++short_len;
10✔
1266

1267
  // minimum incomplete iterator implementation, just for the internal usage with utf8lib to
1268
  // directly decode flipped bytes
1269
  struct BigEndianIterator
1270
  {
1271
    const u16* ptr;
1272
    BigEndianIterator( const u16* begin ) : ptr( begin ){};
12✔
1273
    BigEndianIterator& operator++()
1274
    {
1275
      ++ptr;
1276
      return *this;
1277
    };
1278
    BigEndianIterator operator++( int )
10✔
1279
    {
1280
      BigEndianIterator itr( ptr );
10✔
1281
      ++ptr;
10✔
1282
      return itr;
10✔
1283
    };
1284
    u16 operator*() { return cfBEu16( *ptr ); };
10✔
1285
    bool operator!=( const BigEndianIterator& o ) { return ptr != o.ptr; };
11✔
1286
    bool operator==( const BigEndianIterator& o ) { return ptr == o.ptr; };
×
1287
  };
1288
  if ( big_endian )
1✔
1289
    utf8::unchecked::utf16to8( BigEndianIterator( code ), BigEndianIterator( code + short_len ),
1✔
1290
                               std::back_inserter( s ) );
1291
  else
1292
    utf8::unchecked::utf16to8( code, code + short_len, std::back_inserter( s ) );
×
1293
  Clib::sanitizeUnicode( &s );
1✔
1294
  return s;
1✔
1295
}
×
1296

1297
std::string String::fromUTF8( const char* code, size_t len )
2✔
1298
{
1299
  size_t short_len = 0;
2✔
1300
  // convert until the first null terminator
1301
  while ( code[short_len] != 0 && short_len < len )
22✔
1302
    ++short_len;
20✔
1303
  std::string s( code, short_len );
2✔
1304
  Clib::sanitizeUnicode( &s );
2✔
1305
  return s;
2✔
1306
}
×
1307

1308
std::vector<unsigned short> String::toUTF16( const std::string& text )
11✔
1309
{
1310
  std::vector<unsigned short> utf16;
11✔
1311
  if ( text.empty() )
11✔
1312
    return utf16;
1✔
1313
  utf8::unchecked::utf8to16( text.begin(), text.end(), std::back_inserter( utf16 ) );
10✔
1314
  return utf16;
10✔
1315
}
×
1316

1317
bool String::compare( const String& str ) const
3✔
1318
{
1319
  return value_.compare( str.value_ ) == 0;
3✔
1320
}
1321

1322
bool String::compare( size_t pos1, size_t len1, const String& str ) const
×
1323
{
1324
  auto itr1 = value_.cbegin();
×
1325
  pos1 = getBytePosition( &itr1, pos1 );
×
1326
  len1 = getBytePosition( &itr1, len1 ) - pos1;
×
1327
  return value_.compare( pos1, len1, str.value_ ) == 0;
×
1328
}
1329

1330
bool String::compare( size_t pos1, size_t len1, const String& str, size_t pos2, size_t len2 ) const
6✔
1331
{
1332
  auto itr1 = value_.cbegin();
6✔
1333
  pos1 = getBytePosition( &itr1, pos1 );
6✔
1334
  len1 = getBytePosition( &itr1, len1 ) - pos1;
6✔
1335
  auto itr2 = str.value_.cbegin();
6✔
1336
  pos2 = str.getBytePosition( &itr2, pos2 );
6✔
1337
  len2 = str.getBytePosition( &itr2, len2 ) - pos2;
6✔
1338
  return value_.compare( pos1, len1, str.value_, pos2, len2 ) == 0;
6✔
1339
}
1340

1341
String* String::fromUCArray( ObjArray* array, bool break_first_null )
6✔
1342
{
1343
  std::string res;
6✔
1344
  std::vector<u16> utf16;
6✔
1345
  for ( const auto& c : array->ref_arr )
30✔
1346
  {
1347
    if ( !c )
24✔
1348
      continue;
3✔
1349
    if ( auto* blong = c.get()->impptr_if<BLong>() )
21✔
1350
    {
1351
      if ( blong->value() == 0 && break_first_null )
21✔
1352
        break;
×
1353
      utf16.push_back( blong->value() & 0xFFFF );
21✔
1354
    }
1355
  }
1356
  if ( !utf16.empty() )
6✔
1357
    utf8::unchecked::utf16to8( utf16.begin(), utf16.end(), std::back_inserter( res ) );
6✔
1358

1359
  Clib::sanitizeUnicode( &res );
6✔
1360
  return new String( res );
12✔
1361
}
6✔
1362

1363
class StringIterator final : public ContIterator
1364
{
1365
public:
1366
  StringIterator( String* str, BObject* pIter );
1367
  BObject* step() override;
1368

1369
private:
1370
  // Keep String alive, to ensure iterators stay valid
1371
  BObject m_StringObj;
1372
  BObjectRef m_IterVal;
1373
  BLong* m_pIterVal;
1374
  std::string::const_iterator itr;
1375
  std::string::const_iterator end;
1376
};
1377

1378
StringIterator::StringIterator( String* pString, BObject* pIterVal )
54✔
1379
    : ContIterator(),
1380
      m_StringObj( pString ),
54✔
1381
      m_IterVal( pIterVal ),
54✔
1382
      m_pIterVal( new BLong( 0 ) ),
54✔
1383
      itr( pString->value().cbegin() ),
54✔
1384
      end( pString->value().cend() )
108✔
1385
{
1386
}
54✔
1387

1388
BObject* StringIterator::step()
324✔
1389
{
1390
  if ( itr == end )
324✔
1391
    return nullptr;
57✔
1392

1393
  // Iterate one utf8 character
1394
  auto previous = itr;
267✔
1395
  utf8::unchecked::next( itr );
267✔
1396
  m_pIterVal->increment();
267✔
1397
  m_IterVal->setimp( m_pIterVal );
267✔
1398

1399
  // Set current value to new substring
1400
  return new BObject( new String( std::string( previous, itr ) ) );
267✔
1401
}
1402

1403
ContIterator* String::createIterator( BObject* pIterVal )
54✔
1404
{
1405
  return new StringIterator( this, pIterVal );
54✔
1406
}
1407
}  // namespace Pol::Bscript
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