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

polserver / polserver / 28964407401

08 Jul 2026 05:58PM UTC coverage: 61.276% (+0.3%) from 60.927%
28964407401

push

github

web-flow
Split bobject header  (#892)

* splitted bobject header

* fixed runecl

* crashfix

* missing header

* more missing (debug build)

* runecl debug include

* renamed contiter, moved formating

* removed more unused headers

1145 of 1517 new or added lines in 23 files covered. (75.48%)

217 existing lines in 4 files now uncovered.

44972 of 73392 relevant lines covered (61.28%)

557151.3 hits per line

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

84.02
/pol-core/bscript/bstring.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 "bstring.h"
12

13
#include <cstdlib>
14
#include <ctype.h>
15
#include <cwctype>
16
#include <fmt/compile.h>
17
#include <iterator>
18
#include <string>
19
#include <utf8cpp/utf8.h>
20

21
#include "../clib/clib_endian.h"
22
#include "../clib/stlutil.h"
23
#include "../clib/strutil.h"
24
#include "barray.h"
25
#include "bboolean.h"
26
#include "bcontiter.h"
27
#include "bdouble.h"
28
#include "berror.h"
29
#include "bfuncref.h"
30
#include "blong.h"
31
#include "bobject.h"
32
#include "bregexp.h"
33
#include "buninit.h"
34
#include "executor.h"
35
#include "objmethods.h"
36
#include "str.h"
37

38
#ifdef __GNUG__
39
#include <streambuf>
40
#endif
41

42
#ifdef _MSC_VER
43
#include "../clib/Header_Windows.h"
44
#include <codecvt>
45
#endif
46

47

48
namespace Pol::Bscript
49
{
50
using namespace fmt::literals;
51

NEW
52
String::String( BObjectImp& objimp ) : BObjectImp( OTString ), value_( objimp.getStringRep() ) {}
×
53

54
String::String( const char* s, size_t len, Tainted san ) : BObjectImp( OTString ), value_( s, len )
19,823✔
55
{
56
  if ( san == Tainted::YES )
19,823✔
NEW
57
    Clib::sanitizeUnicodeWithIso( &value_ );
×
58
}
19,823✔
59

60
String::String( const std::string& str, std::string::size_type pos, std::string::size_type n )
6,830✔
61
    : BObjectImp( OTString ), value_( str, pos, n )
6,830✔
62
{
63
}
6,830✔
64

65
String::String( const char* str, Tainted san ) : BObjectImp( OTString ), value_( str )
111,210✔
66
{
67
  if ( san == Tainted::YES )
111,210✔
68
    Clib::sanitizeUnicodeWithIso( &value_ );
73✔
69
}
111,210✔
70

71
String::String( const std::string& str, Tainted san ) : BObjectImp( OTString ), value_( str )
63,863✔
72
{
73
  if ( san == Tainted::YES )
63,863✔
74
    Clib::sanitizeUnicodeWithIso( &value_ );
27,572✔
75
}
63,863✔
76

77
String::String( const std::string_view& str, Tainted san ) : BObjectImp( OTString ), value_( str )
10✔
78
{
79
  if ( san == Tainted::YES )
10✔
80
    Clib::sanitizeUnicodeWithIso( &value_ );
10✔
81
}
10✔
82

83
String::String( const std::wstring& str, Tainted san ) : BObjectImp( OTString )
147✔
84
{
85
  for ( const auto& c : str )
348✔
86
  {
87
    utf8::unchecked::append( c, std::back_inserter( value_ ) );
201✔
88
  }
89

90
  if ( san == Tainted::YES )
147✔
NEW
91
    Clib::sanitizeUnicodeWithIso( &value_ );
×
92
}
147✔
93

94
String* String::StrStr( int begin, int len ) const
39✔
95
{
96
  auto itr = value_.cbegin();
39✔
97
  --begin;
39✔
98
  size_t startpos = getBytePosition( &itr, begin );
39✔
99
  size_t endpos = getBytePosition( &itr, len );
39✔
100
  if ( startpos != std::string::npos )
39✔
101
    return new String( value_.substr( startpos, endpos - startpos ) );
36✔
102
  return new String( value_ );
3✔
103
}
104

105
size_t String::length() const
7,755✔
106
{
107
  return utf8::unchecked::distance( value_.begin(), value_.end() );
7,755✔
108
}
109

110
String* String::ETrim( const char* CRSet, int type ) const
6✔
111
{
112
  std::string tmp = value_;
6✔
113

114
  if ( type == 1 )  // This is for Leading Only.
6✔
115
  {
116
    // Find the first character position after excluding leading blank spaces
NEW
117
    size_t startpos = tmp.find_first_not_of( CRSet );
×
NEW
118
    if ( std::string::npos != startpos )
×
NEW
119
      tmp = tmp.substr( startpos );
×
120
    else
NEW
121
      tmp = "";
×
NEW
122
    return new String( tmp );
×
123
  }
124
  if ( type == 2 )  // This is for Trailing Only.
6✔
125
  {
126
    // Find the first character position from reverse
NEW
127
    size_t endpos = tmp.find_last_not_of( CRSet );
×
NEW
128
    if ( std::string::npos != endpos )
×
NEW
129
      tmp = tmp.substr( 0, endpos + 1 );
×
130
    else
NEW
131
      tmp = "";
×
NEW
132
    return new String( tmp );
×
133
  }
134
  if ( type == 3 )
6✔
135
  {
136
    // Find the first character position after excluding leading blank spaces
137
    size_t startpos = tmp.find_first_not_of( CRSet );
6✔
138
    // Find the first character position from reverse af
139
    size_t endpos = tmp.find_last_not_of( CRSet );
6✔
140

141
    // if all spaces or empty return on empty string
142
    if ( ( std::string::npos == startpos ) || ( std::string::npos == endpos ) )
6✔
NEW
143
      tmp = "";
×
144
    else
145
      tmp = tmp.substr( startpos, endpos - startpos + 1 );
6✔
146
    return new String( tmp );
6✔
147
  }
NEW
148
  return new String( tmp );
×
149
}
6✔
150

151
void String::EStrReplace( String* str1, String* str2 )
9✔
152
{
153
  std::string::size_type valpos = 0;
9✔
154
  while ( std::string::npos != ( valpos = value_.find( str1->value_, valpos ) ) )
15✔
155
  {
156
    value_.replace( valpos, str1->value_.size(), str2->value_ );
6✔
157
    valpos += str2->value_.size();
6✔
158
  }
159
}
9✔
160

161
void String::ESubStrReplace( String* replace_with, unsigned int index, unsigned int len )
6✔
162
{
163
  auto itr = value_.cbegin();
6✔
164
  size_t begin = getBytePosition( &itr, index - 1 );
6✔
165
  size_t end = getBytePosition( &itr, len );
6✔
166
  if ( begin != std::string::npos )
6✔
167
    value_.replace( begin, end - begin, replace_with->value_ );
6✔
168
}
6✔
169

170
std::string String::pack() const
23✔
171
{
172
  return fmt::format( "s{}"_cf, value_ );
46✔
173
}
174
void String::packonto( std::string& str ) const
48✔
175
{
176
  fmt::format_to( std::back_inserter( str ), "S{}:{}"_cf, value_.size(), value_ );
48✔
177
}
48✔
178
void String::packonto( std::string& str, const std::string& value )
15✔
179
{
180
  fmt::format_to( std::back_inserter( str ), "S{}:{}"_cf, value.size(), value );
15✔
181
}
15✔
182

183
BObjectImp* String::unpack( std::istream& is )
25✔
184
{
185
  std::string tmp;
25✔
186
  getline( is, tmp );
25✔
187

188
  return new String( tmp );
50✔
189
}
25✔
190

191
BObjectImp* String::unpackWithLen( std::istream& is )
34✔
192
{
193
  unsigned len;
194
  char colon;
195
  if ( !( is >> len >> colon ) )
34✔
196
  {
NEW
197
    return new BError( "Unable to unpack string length." );
×
198
  }
199
  if ( (int)len < 0 )
34✔
200
  {
NEW
201
    return new BError( "Unable to unpack string length. Invalid length!" );
×
202
  }
203
  if ( colon != ':' )
34✔
204
  {
NEW
205
    return new BError( "Unable to unpack string length. Bad format. Colon not found!" );
×
206
  }
207

208
  is.unsetf( std::ios::skipws );
34✔
209
  std::string tmp;
34✔
210
  tmp.reserve( len );
34✔
211
  while ( len-- )
191✔
212
  {
213
    char ch = '\0';
157✔
214
    if ( !( is >> ch ) || ch == '\0' )
157✔
215
    {
NEW
216
      return new BError( "Unable to unpack string length. String length excessive." );
×
217
    }
218
    tmp += ch;
157✔
219
  }
220

221
  is.setf( std::ios::skipws );
34✔
222
  return new String( tmp );
34✔
223
}
34✔
224

225
size_t String::sizeEstimate() const
1,808✔
226
{
227
  return sizeof( String ) + value_.capacity();
1,808✔
228
}
229

230
/*
231
    0-based string find
232
    find( "str srch", 2, "srch"):
233
    01^-- start
234
    */
235
int String::find( int begin, const char* target ) const
19,922✔
236
{
237
  // returns -1 when begin is out of range for string
238
  auto itr = value_.cbegin();
19,922✔
239
  size_t pos = getBytePosition( &itr, begin );
19,922✔
240
  pos = value_.find( target, pos );
19,922✔
241
  if ( pos == std::string::npos )
19,922✔
242
    return -1;
16,302✔
243

244
  pos = utf8::unchecked::distance( value_.cbegin(), std::next( value_.cbegin(), pos ) );
3,620✔
245
  return static_cast<int>( pos );
3,620✔
246
}
247

248
unsigned int String::SafeCharAmt() const
24✔
249
{
250
  unsigned int strlen = static_cast<unsigned int>( length() );
24✔
251
  for ( unsigned int i = 0; i < strlen; ++i )
279✔
252
  {
253
    unsigned char tmp = value_[i];
255✔
254
    if ( tmp >= 0x80 )  // Ascii range
255✔
NEW
255
      return i;
×
256
    if ( isalnum( tmp ) )  // a-z A-Z 0-9
255✔
257
      continue;
237✔
258
    if ( ispunct( tmp ) )  // !"#$%&'()*+,-./:;<=>?@{|}~
18✔
259
    {
260
      if ( tmp == '{' || tmp == '}' )
18✔
NEW
261
        return i;
×
262
      continue;
18✔
263
    }
264

NEW
265
    return i;
×
266
  }
267
  return strlen;
24✔
268
}
269

270
BObjectImp* String::selfPlusObjImp( const BObjectImp& objimp ) const
7,540✔
271
{
272
  return objimp.selfPlusObj( *this );
7,540✔
273
}
274
BObjectImp* String::selfPlusObj( const BObjectImp& objimp ) const
1,161✔
275
{
276
  return new String( value_ + objimp.getStringRep() );
1,161✔
277
}
278
BObjectImp* String::selfPlusObj( const BLong& objimp ) const
2,030✔
279
{
280
  return new String( value_ + objimp.getStringRep() );
2,030✔
281
}
282
BObjectImp* String::selfPlusObj( const Double& objimp ) const
366✔
283
{
284
  return new String( value_ + objimp.getStringRep() );
366✔
285
}
286
BObjectImp* String::selfPlusObj( const String& objimp ) const
7,462✔
287
{
288
  return new String( value_ + objimp.getStringRep() );
7,462✔
289
}
290
BObjectImp* String::selfPlusObj( const ObjArray& objimp ) const
183✔
291
{
292
  return new String( value_ + objimp.getStringRep() );
183✔
293
}
294
void String::selfPlusObjImp( BObjectImp& objimp, BObject& obj )
225✔
295
{
296
  objimp.selfPlusObj( *this, obj );
225✔
297
}
225✔
298
void String::selfPlusObj( BObjectImp& objimp, BObject& /*obj*/ )
24✔
299
{
300
  value_ += objimp.getStringRep();
24✔
301
}
24✔
302
void String::selfPlusObj( BLong& objimp, BObject& /*obj*/ )
15✔
303
{
304
  value_ += objimp.getStringRep();
15✔
305
}
15✔
306
void String::selfPlusObj( Double& objimp, BObject& /*obj*/ )
12✔
307
{
308
  value_ += objimp.getStringRep();
12✔
309
}
12✔
310
void String::selfPlusObj( String& objimp, BObject& /*obj*/ )
207✔
311
{
312
  value_ += objimp.getStringRep();
207✔
313
}
207✔
314
void String::selfPlusObj( ObjArray& objimp, BObject& /*obj*/ )
9✔
315
{
316
  value_ += objimp.getStringRep();
9✔
317
}
9✔
318

319

320
void String::remove( const std::string& rm )
291✔
321
{
322
  auto pos = value_.find( rm );
291✔
323
  if ( pos != std::string::npos )
291✔
324
    value_.erase( pos, rm.size() );
60✔
325
}
291✔
326

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

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

391
  if ( objimp.isa( OTBoolean ) )
170✔
392
    return isTrue() == static_cast<const BBoolean&>( objimp ).isTrue();
3✔
393
  return base::operator==( objimp );
167✔
394
}
395

396
bool String::operator<( const BObjectImp& objimp ) const
3,405✔
397
{
398
  if ( objimp.isa( OTString ) )
3,405✔
399
    return ( value_ < static_cast<const String&>( objimp ).value_ );
3,188✔
400

401
  return base::operator<( objimp );
217✔
402
}
403

404
void String::toUpper()
42✔
405
{
406
  if ( !hasUTF8Characters() )
42✔
407
  {
408
    Clib::mkupperASCII( value_ );
33✔
409
    return;
33✔
410
  }
411
#ifndef WINDOWS
412
  auto wstr = Clib::to_wstring( value_ );
9✔
413
  value_.clear();
9✔
414
  for ( const auto& c : wstr )
237✔
415
  {
416
    utf8::unchecked::append( std::towupper( c ), std::back_inserter( value_ ) );
228✔
417
  }
418
#else
419
  std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
420
  std::wstring str = converter.from_bytes( value_ );
421

422
  int len = LCMapStringW( LOCALE_USER_DEFAULT, LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING, &str[0],
423
                          static_cast<int>( str.size() ), 0, 0 );
424
  if ( !len )
425
    return;
426
  else if ( len == static_cast<int>( str.size() ) )
427
  {
428
    LCMapStringW( LOCALE_USER_DEFAULT, LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING, &str[0],
429
                  static_cast<int>( str.size() ), &str[0], static_cast<int>( str.size() ) );
430
    value_ = converter.to_bytes( str );
431
  }
432
  else
433
  {
434
    std::wstring buf;
435
    buf.reserve( len );
436
    LCMapStringW( LOCALE_USER_DEFAULT, LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING, &str[0],
437
                  static_cast<int>( str.size() ), &buf[0], static_cast<int>( buf.size() ) );
438
    value_ = converter.to_bytes( buf );
439
  }
440
#endif
441
}
9✔
442

443
void String::toLower()
27✔
444
{
445
  if ( !hasUTF8Characters() )
27✔
446
  {
447
    Clib::mklowerASCII( value_ );
18✔
448
    return;
18✔
449
  }
450
#ifndef WINDOWS
451
  auto wstr = Clib::to_wstring( value_ );
9✔
452
  value_.clear();
9✔
453
  for ( const auto& c : wstr )
237✔
454
  {
455
    utf8::unchecked::append( std::towlower( c ), std::back_inserter( value_ ) );
228✔
456
  }
457
#else
458
  std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
459
  std::wstring str = converter.from_bytes( value_ );
460

461
  int len = LCMapStringW( LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_LINGUISTIC_CASING, &str[0],
462
                          static_cast<int>( str.size() ), 0, 0 );
463
  if ( !len )
464
    return;
465
  else if ( len == static_cast<int>( str.size() ) )
466
  {
467
    LCMapStringW( LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_LINGUISTIC_CASING, &str[0],
468
                  static_cast<int>( str.size() ), &str[0], static_cast<int>( str.size() ) );
469
    value_ = converter.to_bytes( str );
470
  }
471
  else
472
  {
473
    std::wstring buf;
474
    buf.reserve( len );
475
    LCMapStringW( LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_LINGUISTIC_CASING, &str[0],
476
                  static_cast<int>( str.size() ), &buf[0], static_cast<int>( buf.size() ) );
477
    value_ = converter.to_bytes( buf );
478
  }
479
#endif
480
}
9✔
481

482
size_t String::getBytePosition( std::string::const_iterator* itr, size_t codeindex ) const
53,056✔
483
{
484
  auto itr_end = value_.cend();
53,056✔
485
  for ( size_t i = 0; i < codeindex && *itr != itr_end; ++i )
112,007✔
486
    utf8::unchecked::next( *itr );
58,951✔
487

488
  if ( *itr != itr_end )
53,056✔
489
  {
490
    return std::distance( value_.cbegin(), *itr );
51,394✔
491
  }
492
  return std::string::npos;
1,662✔
493
}
494

495
BObjectImp* String::array_assign( BObjectImp* idx, BObjectImp* target, bool /*copy*/ )
15✔
496
{
497
  std::string::size_type pos, len;
498

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

541
  if ( pos != std::string::npos )
15✔
542
  {
543
    if ( target->isa( OTString ) )
15✔
544
    {
545
      String* target_str = (String*)target;
15✔
546
      value_.replace( pos, len, target_str->value_ );
15✔
547
    }
548
    return this;
15✔
549
  }
550

NEW
551
  return UninitObject::create();
×
552
}
553

554
BObjectRef String::OperMultiSubscriptAssign( std::stack<BObjectRef>& indices, BObjectImp* target )
6✔
555
{
556
  BObjectRef start_ref = indices.top();
6✔
557
  indices.pop();
6✔
558
  BObjectRef length_ref = indices.top();
6✔
559
  indices.pop();
6✔
560

561
  BObject& length_obj = *length_ref;
6✔
562
  BObject& start_obj = *start_ref;
6✔
563

564
  BObjectImp& length = length_obj.impref();
6✔
565
  BObjectImp& start = start_obj.impref();
6✔
566

567
  // first deal with the start position.
568
  size_t index;
569
  if ( start.isa( OTLong ) )
6✔
570
  {
571
    BLong& lng = (BLong&)start;
6✔
572
    index = (size_t)lng.value();
6✔
573
    if ( index == 0 || index > value_.size() )
6✔
NEW
574
      return BObjectRef( new BError( "Subscript out of range" ) );
×
575
    --index;
6✔
576
    auto itr = value_.cbegin();
6✔
577
    index = getBytePosition( &itr, index );
6✔
578
    if ( index == std::string::npos )
6✔
NEW
579
      return BObjectRef( new BError( "Subscript out of range" ) );
×
580
  }
NEW
581
  else if ( start.isa( OTString ) )
×
582
  {
NEW
583
    String& rtstr = (String&)start;
×
NEW
584
    std::string::size_type pos = value_.find( rtstr.value_ );
×
NEW
585
    if ( pos != std::string::npos )
×
NEW
586
      index = static_cast<size_t>( pos );
×
587
    else
NEW
588
      return BObjectRef( new UninitObject );
×
589
  }
590
  else
591
  {
NEW
592
    return BObjectRef( copy() );
×
593
  }
594

595
  // now deal with the length.
596
  size_t len;
597
  if ( length.isa( OTLong ) )
6✔
598
  {
599
    BLong& lng = (BLong&)length;
6✔
600

601
    len = (size_t)lng.value();
6✔
602
  }
NEW
603
  else if ( length.isa( OTDouble ) )
×
604
  {
NEW
605
    Double& dbl = (Double&)length;
×
606

NEW
607
    len = (size_t)dbl.value();
×
608
  }
609
  else
610
  {
NEW
611
    return BObjectRef( copy() );
×
612
  }
613
  auto itr = value_.cbegin();
6✔
614
  std::advance( itr, index );
6✔
615
  size_t index_len = getBytePosition( &itr, len );
6✔
616

617
  if ( index_len != std::string::npos )
6✔
618
    len = index_len - index;
6✔
619
  else
NEW
620
    len = index_len;
×
621

622
  if ( target->isa( OTString ) )
6✔
623
  {
624
    String* target_str = (String*)target;
6✔
625
    value_.replace( index, len, target_str->value_ );
6✔
626
  }
627
  else
628
  {
NEW
629
    return BObjectRef( UninitObject::create() );
×
630
  }
631

632
  return BObjectRef( this );
6✔
633
}
6✔
634

635

636
BObjectRef String::OperMultiSubscript( std::stack<BObjectRef>& indices )
7,226✔
637
{
638
  BObjectRef start_ref = indices.top();
7,226✔
639
  indices.pop();
7,226✔
640
  BObjectRef length_ref = indices.top();
7,226✔
641
  indices.pop();
7,226✔
642

643
  BObject& length_obj = *length_ref;
7,226✔
644
  BObject& start_obj = *start_ref;
7,226✔
645

646
  BObjectImp& length = length_obj.impref();
7,226✔
647
  BObjectImp& start = start_obj.impref();
7,226✔
648

649
  // first deal with the start position.
650
  size_t index;
651
  if ( start.isa( OTLong ) )
7,226✔
652
  {
653
    BLong& lng = (BLong&)start;
7,220✔
654
    index = (size_t)lng.value();
7,220✔
655
    if ( index == 0 || index > value_.size() )
7,220✔
656
      return BObjectRef( new BError( "Subscript out of range" ) );
1,264✔
657
    --index;
6,588✔
658
    auto itr = value_.cbegin();
6,588✔
659
    index = getBytePosition( &itr, index );
6,588✔
660
    if ( index == std::string::npos )
6,588✔
NEW
661
      return BObjectRef( new BError( "Subscript out of range" ) );
×
662
  }
663
  else if ( start.isa( OTString ) )
6✔
664
  {
665
    String& rtstr = (String&)start;
6✔
666
    std::string::size_type pos = value_.find( rtstr.value_ );
6✔
667
    if ( pos != std::string::npos )
6✔
668
      index = static_cast<unsigned int>( pos );
6✔
669
    else
NEW
670
      return BObjectRef( new UninitObject );
×
671
  }
672
  else
673
  {
NEW
674
    return BObjectRef( copy() );
×
675
  }
676

677
  // now deal with the length.
678
  size_t len;
679
  if ( length.isa( OTLong ) )
6,594✔
680
  {
681
    BLong& lng = (BLong&)length;
6,594✔
682

683
    len = (size_t)lng.value();
6,594✔
684
  }
NEW
685
  else if ( length.isa( OTDouble ) )
×
686
  {
NEW
687
    Double& dbl = (Double&)length;
×
688

NEW
689
    len = (size_t)dbl.value();
×
690
  }
691
  else
692
  {
NEW
693
    return BObjectRef( copy() );
×
694
  }
695
  auto itr = value_.cbegin();
6,594✔
696
  std::advance( itr, index );
6,594✔
697
  size_t index_len = getBytePosition( &itr, len );
6,594✔
698

699
  if ( index_len != std::string::npos )
6,594✔
700
    len = index_len - index;
5,473✔
701
  else
702
    len = index_len;
1,121✔
703
  return BObjectRef( new String( value_, index, len ) );
6,594✔
704
}
7,226✔
705

706
BObjectRef String::OperSubscript( const BObject& rightobj )
22,408✔
707
{
708
  const BObjectImp& right = rightobj.impref();
22,408✔
709
  if ( right.isa( OTLong ) )
22,408✔
710
  {
711
    BLong& lng = (BLong&)right;
19,835✔
712

713
    if ( lng.value() < 0 )
19,835✔
714
      return BObjectRef( new BError( "Subscript out of range" ) );
3✔
715

716
    size_t index = (size_t)lng.value();
19,832✔
717

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

721
    --index;
19,823✔
722
    auto itr = value_.cbegin();
19,823✔
723
    index = getBytePosition( &itr, index );
19,823✔
724
    if ( index != std::string::npos )
19,823✔
725
    {
726
      utf8::unchecked::next( itr );
19,823✔
727
      size_t len = std::distance( value_.cbegin(), itr ) - index;
19,823✔
728
      return BObjectRef( new BObject( new String( value_.c_str() + index, len ) ) );
19,823✔
729
    }
NEW
730
    return BObjectRef( new BError( "Subscript out of range" ) );
×
731
  }
732
  if ( right.isa( OTDouble ) )
2,573✔
733
  {
734
    Double& dbl = (Double&)right;
9✔
735

736
    if ( dbl.value() < 0 )
9✔
737
      return BObjectRef( new BError( "Subscript out of range" ) );
3✔
738
    size_t index = (size_t)dbl.value();
6✔
739

740
    if ( index == 0 || index > value_.size() )
6✔
741
      return BObjectRef( new BError( "Subscript out of range" ) );
6✔
742

NEW
743
    --index;
×
NEW
744
    auto itr = value_.cbegin();
×
NEW
745
    index = getBytePosition( &itr, index );
×
NEW
746
    if ( index != std::string::npos )
×
747
    {
NEW
748
      utf8::unchecked::next( itr );
×
NEW
749
      size_t len = std::distance( value_.cbegin(), itr ) - index;
×
NEW
750
      return BObjectRef( new BObject( new String( value_.c_str() + index, len ) ) );
×
751
    }
NEW
752
    return BObjectRef( new BError( "Subscript out of range" ) );
×
753
  }
754
  if ( right.isa( OTString ) )
2,564✔
755
  {
756
    String& rtstr = (String&)right;
2,543✔
757
    auto pos = value_.find( rtstr.value_ );
2,543✔
758
    if ( pos != std::string::npos )
2,543✔
759
    {
760
      auto itr = value_.cbegin();
236✔
761
      std::advance( itr, pos );
236✔
762
      utf8::unchecked::next( itr );
236✔
763
      size_t len = std::distance( value_.cbegin(), itr ) - pos;
236✔
764
      return BObjectRef( new BObject( new String( value_, pos, len ) ) );
236✔
765
    }
766
    return BObjectRef( new UninitObject );
2,307✔
767
  }
768

769
  return BObjectRef( new UninitObject );
21✔
770
}
771

772
// -- format related stuff --
773
bool s_parse_int( int& i, std::string const& s )
39✔
774
{
775
  if ( s.empty() )
39✔
NEW
776
    return false;
×
777

778
  char* end;
779
  i = strtol( s.c_str(), &end, 10 );
39✔
780

781
  if ( !*end )
39✔
782
  {
783
    return true;
27✔
784
  }
785

786
  return false;
12✔
787
}
788

NEW
789
BObjectImp* String::call_method( const char* methodname, Executor& ex )
×
790
{
NEW
791
  ObjMethod* objmethod = getKnownObjMethod( methodname );
×
NEW
792
  if ( objmethod != nullptr )
×
NEW
793
    return this->call_method_id( objmethod->id, ex );
×
NEW
794
  return nullptr;
×
795
}
796
BObjectImp* String::call_method_id( const int id, Executor& ex, bool /*forcebuiltin*/ )
22,866✔
797
{
798
  switch ( id )
22,866✔
799
  {
800
  case MTH_LENGTH:
5,645✔
801
    if ( ex.numParams() == 0 )
5,645✔
802
      return new BLong( static_cast<int>( length() ) );
5,645✔
803
    else
NEW
804
      return new BError( "string.length() doesn't take parameters." );
×
805
    break;
806
  case MTH_FIND:
15,952✔
807
  {
808
    if ( ex.numParams() > 2 )
15,952✔
NEW
809
      return new BError( "string.find(Search, [Start]) takes only two parameters" );
×
810
    if ( ex.numParams() < 1 )
15,952✔
NEW
811
      return new BError( "string.find(Search, [Start]) takes at least one parameter" );
×
812

813
    int d = 0;
15,952✔
814
    if ( ex.numParams() == 2 )
15,952✔
815
      d = ex.paramAsLong( 1 ) - 1;
108✔
816

817
    if ( auto s = impptrIf<String>( ex.getParamImp( 0 ) ) )
15,952✔
818
    {
819
      int posn = find( d, s->data() ) + 1;
15,868✔
820
      return new BLong( posn );
15,868✔
821
    }
822
    if ( auto regex = impptrIf<BRegExp>( ex.getParamImp( 0 ) ) )
84✔
823
    {
824
      return regex->find( this, d );
84✔
825
    }
NEW
826
    return new BError( "string.find(Search, [Start]): Search must be a string or regex" );
×
827
  }
828
  case MTH_MATCH:
63✔
829
  {
830
    if ( ex.numParams() != 1 )
63✔
831
      return new BError( "string.match(Pattern) takes exactly one parameter" );
6✔
832

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

835
    if ( !regex )
57✔
836
      return new BError( "string.match(Pattern): Pattern must be a RegExp" );
6✔
837

838
    return regex->match( this );
51✔
839
  }
840
  case MTH_REPLACE:
75✔
841
  {
842
    if ( ex.numParams() != 2 )
75✔
843
      return new BError( "string.replace(Search, Replace) takes exactly two parameters" );
6✔
844

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

849
    if ( auto s = impptrIf<String>( ex.getParamImp( 1 ) ) )
63✔
850
    {
851
      return regex->replace( this, s );
36✔
852
    }
853
    if ( auto funcref = impptrIf<BFunctionRef>( ex.getParamImp( 1 ) ) )
27✔
854
    {
855
      return regex->replace( ex, this, funcref );
21✔
856
    }
857

858
    return new BError( "string.replace(Search, Replace): Replace must be a string or function" );
6✔
859
  }
860
  case MTH_SPLIT:
57✔
861
  {
862
    size_t limit = std::numeric_limits<size_t>::max();
57✔
863

864
    if ( ex.numParams() == 0 )
57✔
NEW
865
      return new BError( "string.split(Separator[, Max_Split]) takes at least one parameter" );
×
866
    if ( ex.numParams() > 2 )
57✔
NEW
867
      return new BError( "string.split(Separator[, Max_Split]) takes at most two parameters" );
×
868

869
    if ( ex.numParams() == 2 )
57✔
870
    {
871
      int max_splits = ex.paramAsLong( 1 );
21✔
872
      if ( max_splits > -1 )
21✔
873
        limit = Clib::clamp_convert<size_t>( max_splits + 1 );
21✔
874
    }
875

876
    if ( auto regex = impptrIf<BRegExp>( ex.getParamImp( 0 ) ) )
57✔
877
    {
878
      return regex->split( this, limit );
30✔
879
    }
880
    if ( auto string_sep = impptrIf<String>( ex.getParamImp( 0 ) ) )
27✔
881
    {
882
      std::unique_ptr<ObjArray> result( new ObjArray );
27✔
883
      const auto& sep = string_sep->value_;
27✔
884

885
      // Empty separator splits into characters
886
      if ( sep.empty() )
27✔
887
      {
888
        return getCharacters( value_, limit );
12✔
889
      }
890

891
      // Has a non-empty separator, splits by `sep`
892
      std::size_t start = 0;
15✔
893
      while ( start < value_.size() )
60✔
894
      {
895
        // Limit reached, push rest and break
896
        if ( result->ref_arr.size() == limit - 1 )
57✔
897
        {
898
          result->addElement( new String( value_.substr( start ) ) );
9✔
899
          break;
9✔
900
        }
901

902
        std::size_t pos = value_.find( sep, start );
48✔
903
        if ( pos == std::string::npos )
48✔
904
        {
905
          // No more separators, push rest
906
          result->addElement( new String( value_.substr( start ) ) );
3✔
907
          break;
3✔
908
        }
909

910
        // Extract part
911
        result->addElement( new String( value_.substr( start, pos - start ) ) );
45✔
912
        start = pos + sep.size();
45✔
913
      }
914

915
      return result.release();
15✔
916
    }
27✔
917

918
    return new BError(
NEW
919
        "string.split(Separator[, Max_Split]): Separator must be a RegExp or string" );
×
920
  }
921

922
  case MTH_UPPER:
9✔
923
  {
924
    if ( ex.numParams() == 0 )
9✔
925
    {
926
      toUpper();
9✔
927
      return this;
9✔
928
    }
NEW
929
    return new BError( "string.upper() doesn't take parameters." );
×
930
  }
931

932
  case MTH_LOWER:
9✔
933
  {
934
    if ( ex.numParams() == 0 )
9✔
935
    {
936
      toLower();
9✔
937
      return this;
9✔
938
    }
NEW
939
    return new BError( "string.lower() doesn't take parameters." );
×
940
  }
941
  case MTH_FORMAT:
1,024✔
942
  {
943
    if ( ex.numParams() > 0 )
1,024✔
944
    {
945
      // string s = this->getStringRep(); // string itself
946
      std::stringstream result;
1,024✔
947

948
      size_t tag_start_pos;  // the position of tag's start "{"
949
      size_t tag_stop_pos;   // the position of tag's end "}"
950
      size_t tag_dot_pos;
951

952
      int tag_param_idx;
953

954
      size_t str_pos = 0;               // current string position
1,024✔
955
      unsigned int next_param_idx = 0;  // next index of .format() parameter
1,024✔
956

957
      char w_spaces[] = "\t ";
1,024✔
958

959
      // Tells whether last found tag was an integer
960
      bool last_tag_was_int = true;
1,024✔
961

962
      while ( ( tag_start_pos = value_.find( '{', str_pos ) ) != std::string::npos )
2,168✔
963
      {
964
        if ( ( tag_stop_pos = value_.find( '}', tag_start_pos ) ) != std::string::npos )
1,144✔
965
        {
966
          result << value_.substr( str_pos, tag_start_pos - str_pos );
1,144✔
967
          str_pos = tag_stop_pos + 1;
1,144✔
968

969
          std::string tag_body =
970
              value_.substr( tag_start_pos + 1, ( tag_stop_pos - tag_start_pos ) - 1 );
1,144✔
971

972
          tag_start_pos = tag_body.find_first_not_of( w_spaces );
1,144✔
973
          tag_stop_pos = tag_body.find_last_not_of( w_spaces );
1,144✔
974

975
          // cout << "' tag_body1: '" << tag_body << "'";
976

977
          // trim the tag of whitespaces (slightly faster code ~25%)
978
          if ( tag_start_pos != std::string::npos && tag_stop_pos != std::string::npos )
1,144✔
979
            tag_body = tag_body.substr( tag_start_pos, ( tag_stop_pos - tag_start_pos ) + 1 );
51✔
980
          else if ( tag_start_pos != std::string::npos )
1,093✔
NEW
981
            tag_body = tag_body.substr( tag_start_pos );
×
982
          else if ( tag_stop_pos != std::string::npos )
1,093✔
NEW
983
            tag_body = tag_body.substr( 0, tag_stop_pos + 1 );
×
984

985
          // cout << "' tag_body2: '" << tag_body << "'";
986

987
          std::string frmt;
1,144✔
988
          size_t formatter_pos = tag_body.find( ':' );
1,144✔
989

990
          if ( formatter_pos != std::string::npos )
1,144✔
991
          {
992
            frmt = tag_body.substr( formatter_pos + 1, std::string::npos );  //
12✔
993
            tag_body = tag_body.substr( 0, formatter_pos );  // remove property from the tag
12✔
994
          }
995

996
          std::string prop_name;
1,144✔
997
          // parsing {1.this_part}
998
          tag_dot_pos = tag_body.find( '.', 0 );
1,144✔
999

1000
          // '.' is found within the tag, there is a property name
1001
          if ( tag_dot_pos != std::string::npos )
1,144✔
1002
          {
1003
            last_tag_was_int = true;
12✔
1004
            prop_name = tag_body.substr( tag_dot_pos + 1, std::string::npos );  //
12✔
1005
            tag_body = tag_body.substr( 0, tag_dot_pos );  // remove property from the tag
12✔
1006

1007
            // if s_tag_body is numeric then use it as an index
1008
            if ( s_parse_int( tag_param_idx, tag_body ) )
12✔
1009
            {
1010
              tag_param_idx -= 1;  // sinse POL indexes are 1-based
12✔
1011
            }
1012
            else
1013
            {
NEW
1014
              result << "<idx required before: '" << prop_name << "'>";
×
NEW
1015
              continue;
×
1016
            }
1017
          }
1018
          else
1019
          {
1020
            if ( tag_body.empty() )
1,132✔
1021
            {
1022
              // empty body just takes next integer idx
1023
              last_tag_was_int = true;
1,105✔
1024
              tag_param_idx = next_param_idx++;
1,105✔
1025
            }
1026
            else if ( s_parse_int( tag_param_idx, tag_body ) )
27✔
1027
            {
1028
              last_tag_was_int = true;
15✔
1029
              tag_param_idx -= 1;  // sinse POL indexes are 1-based
15✔
1030
            }
1031
            else
1032
            {
1033
              // string body takes next idx in line if this is
1034
              // the first string body occurrence,
1035
              // will reuse last idx if this is 2nd or more in a row
1036
              last_tag_was_int = false;
12✔
1037
              prop_name = tag_body;
12✔
1038
              tag_param_idx = last_tag_was_int ? next_param_idx++ : next_param_idx;
12✔
1039
            }
1040
          }
1041

1042
          // -- end of property parsing
1043

1044
          // cout << "prop_name: '" << prop_name << "' tag_body: '" << tag_body << "'";
1045
          if ( tag_param_idx < 0 || (int)ex.numParams() <= tag_param_idx )
1,144✔
1046
          {
1047
            result << "<invalid index: #" << ( tag_param_idx + 1 ) << ">";
3✔
1048
            continue;
3✔
1049
          }
1050

1051
          BObjectImp* imp = ex.getParamImp( tag_param_idx );
1,141✔
1052

1053
          if ( !prop_name.empty() )
1,141✔
1054
          {  // accesing object
1055
            BObjectRef obj_member = imp->get_member( prop_name.c_str() );
24✔
1056
            BObjectImp* member_imp = obj_member->impptr();
24✔
1057
            try_to_format( result, member_imp, frmt );
24✔
1058
          }
24✔
1059
          else
1060
          {
1061
            try_to_format( result, imp, frmt );
1,117✔
1062
          }
1063
        }
1,150✔
1064
        else
1065
        {
NEW
1066
          break;
×
1067
        }
1068
      }
1069

1070
      if ( str_pos < value_.length() )
1,024✔
1071
      {
1072
        result << value_.substr( str_pos, std::string::npos );
880✔
1073
      }
1074

1075
      return new String( result.str() );
1,024✔
1076
    }
1,024✔
1077

NEW
1078
    return new BError( "string.format() requires a parameter." );
×
1079
  }
1080
  case MTH_JOIN:
26✔
1081
  {
1082
    BObject* cont;
1083
    if ( ex.numParams() == 1 && ( cont = ex.getParamObj( 0 ) ) != nullptr )
26✔
1084
    {
1085
      if ( !( cont->isa( OTArray ) ) )
26✔
NEW
1086
        return new BError( "string.join expects an array" );
×
1087
      ObjArray* container = cont->impptr<ObjArray>();
26✔
1088
      // no empty check here on purpose
1089
      std::string joined;
26✔
1090
      bool first = true;
26✔
1091
      for ( const BObjectRef& ref : container->ref_arr )
191✔
1092
      {
1093
        if ( ref.get() )
165✔
1094
        {
1095
          BObject* bo = ref.get();
153✔
1096

1097
          if ( bo == nullptr )
153✔
NEW
1098
            continue;
×
1099
          if ( !first )
153✔
1100
            joined += value_;
130✔
1101
          else
1102
            first = false;
23✔
1103
          joined += bo->impptr()->getStringRep();
153✔
1104
        }
1105
      }
1106
      return new String( joined );
26✔
1107
    }
26✔
NEW
1108
    return new BError( "string.join(array) requires a parameter." );
×
1109
  }
1110
  default:
6✔
1111
    return nullptr;
6✔
1112
  }
1113
}
1114

1115
ObjArray* String::getCharacters( const std::string& source, size_t limit )
27✔
1116
{
1117
  std::unique_ptr<ObjArray> result( new ObjArray );
27✔
1118

1119
  for ( auto rit = source.cbegin(); rit != source.cend(); )
150✔
1120
  {
1121
    // Limit reached, push rest and break
1122
    if ( result->ref_arr.size() == limit - 1 )
138✔
1123
    {
1124
      result->addElement( new String( std::string( rit, source.cend() ) ) );
15✔
1125
      break;
15✔
1126
    }
1127

1128
    auto previous = rit;
123✔
1129
    utf8::unchecked::next( rit );
123✔
1130

1131
    result->addElement( new String( std::string( previous, rit ) ) );
123✔
1132
  }
1133

1134
  return result.release();
54✔
1135
}
27✔
1136

1137
bool String::hasUTF8Characters() const
70✔
1138
{
1139
  return hasUTF8Characters( value_ );
70✔
1140
}
1141

1142
bool String::hasUTF8Characters( const std::string& str )
71✔
1143
{
1144
  for ( const auto& c : str )
723✔
1145
  {
1146
    if ( c & 0x80 )
670✔
1147
      return true;
18✔
1148
  }
1149
  return false;
53✔
1150
}
1151

1152
std::vector<unsigned short> String::toUTF16() const
48✔
1153
{
1154
  std::vector<unsigned short> u16;
48✔
1155
  utf8::unchecked::utf8to16( value_.begin(), value_.end(), std::back_inserter( u16 ) );
48✔
1156
  return u16;
48✔
NEW
1157
}
×
1158

1159
std::string String::fromUTF16( unsigned short code )
441✔
1160
{
1161
  std::string s;
441✔
1162
  std::vector<unsigned short> utf16( 1, code );
441✔
1163
  utf8::unchecked::utf16to8( utf16.begin(), utf16.end(), std::back_inserter( s ) );
441✔
1164
  Clib::sanitizeUnicode( &s );
441✔
1165
  return s;
882✔
1166
}
441✔
1167

1168
std::string String::fromUTF16( const unsigned short* code, size_t len, bool big_endian )
1✔
1169
{
1170
  std::string s;
1✔
1171
  size_t short_len = 0;
1✔
1172
  // convert until the first null terminator
1173
  while ( code[short_len] != 0 && short_len < len )
11✔
1174
    ++short_len;
10✔
1175

1176
  // minimum incomplete iterator implementation, just for the internal usage with utf8lib to
1177
  // directly decode flipped bytes
1178
  struct BigEndianIterator
1179
  {
1180
    const u16* ptr;
1181
    BigEndianIterator( const u16* begin ) : ptr( begin ) {};
12✔
1182
    BigEndianIterator& operator++()
1183
    {
1184
      ++ptr;
1185
      return *this;
1186
    };
1187
    BigEndianIterator operator++( int )
10✔
1188
    {
1189
      BigEndianIterator itr( ptr );
10✔
1190
      ++ptr;
10✔
1191
      return itr;
10✔
1192
    };
1193
    u16 operator*() { return cfBEu16( *ptr ); };
10✔
1194
    bool operator!=( const BigEndianIterator& o ) { return ptr != o.ptr; };
11✔
NEW
1195
    bool operator==( const BigEndianIterator& o ) { return ptr == o.ptr; };
×
1196
  };
1197
  if ( big_endian )
1✔
1198
    utf8::unchecked::utf16to8( BigEndianIterator( code ), BigEndianIterator( code + short_len ),
1✔
1199
                               std::back_inserter( s ) );
1200
  else
NEW
1201
    utf8::unchecked::utf16to8( code, code + short_len, std::back_inserter( s ) );
×
1202
  Clib::sanitizeUnicode( &s );
1✔
1203
  return s;
1✔
NEW
1204
}
×
1205

1206
std::string String::fromUTF8( const char* code, size_t len )
2✔
1207
{
1208
  size_t short_len = 0;
2✔
1209
  // convert until the first null terminator
1210
  while ( code[short_len] != 0 && short_len < len )
22✔
1211
    ++short_len;
20✔
1212
  std::string s( code, short_len );
2✔
1213
  Clib::sanitizeUnicode( &s );
2✔
1214
  return s;
2✔
NEW
1215
}
×
1216

1217
std::vector<unsigned short> String::toUTF16( const std::string& text )
21✔
1218
{
1219
  std::vector<unsigned short> utf16;
21✔
1220
  if ( text.empty() )
21✔
1221
    return utf16;
1✔
1222
  utf8::unchecked::utf8to16( text.begin(), text.end(), std::back_inserter( utf16 ) );
20✔
1223
  return utf16;
20✔
NEW
1224
}
×
1225

1226
bool String::compare( const String& str ) const
3✔
1227
{
1228
  return value_.compare( str.value_ ) == 0;
3✔
1229
}
1230

NEW
1231
bool String::compare( size_t pos1, size_t len1, const String& str ) const
×
1232
{
NEW
1233
  auto itr1 = value_.cbegin();
×
NEW
1234
  pos1 = getBytePosition( &itr1, pos1 );
×
NEW
1235
  len1 = getBytePosition( &itr1, len1 ) - pos1;
×
NEW
1236
  return value_.compare( pos1, len1, str.value_ ) == 0;
×
1237
}
1238

1239
bool String::compare( size_t pos1, size_t len1, const String& str, size_t pos2, size_t len2 ) const
6✔
1240
{
1241
  auto itr1 = value_.cbegin();
6✔
1242
  pos1 = getBytePosition( &itr1, pos1 );
6✔
1243
  len1 = getBytePosition( &itr1, len1 ) - pos1;
6✔
1244
  auto itr2 = str.value_.cbegin();
6✔
1245
  pos2 = str.getBytePosition( &itr2, pos2 );
6✔
1246
  len2 = str.getBytePosition( &itr2, len2 ) - pos2;
6✔
1247
  return value_.compare( pos1, len1, str.value_, pos2, len2 ) == 0;
6✔
1248
}
1249

1250
String* String::fromUCArray( ObjArray* array, bool break_first_null )
6✔
1251
{
1252
  std::string res;
6✔
1253
  std::vector<u16> utf16;
6✔
1254
  for ( const auto& c : array->ref_arr )
30✔
1255
  {
1256
    if ( !c )
24✔
1257
      continue;
3✔
1258
    if ( auto* blong = c.get()->impptr_if<BLong>() )
21✔
1259
    {
1260
      if ( blong->value() == 0 && break_first_null )
21✔
NEW
1261
        break;
×
1262
      utf16.push_back( blong->value() & 0xFFFF );
21✔
1263
    }
1264
  }
1265
  if ( !utf16.empty() )
6✔
1266
    utf8::unchecked::utf16to8( utf16.begin(), utf16.end(), std::back_inserter( res ) );
6✔
1267

1268
  Clib::sanitizeUnicode( &res );
6✔
1269
  return new String( res );
12✔
1270
}
6✔
1271

1272
class StringIterator final : public ContIterator
1273
{
1274
public:
1275
  StringIterator( String* str, BObject* pIter );
1276
  BObject* step() override;
1277

1278
private:
1279
  // Keep String alive, to ensure iterators stay valid
1280
  BObject m_StringObj;
1281
  BObjectRef m_IterVal;
1282
  BLong* m_pIterVal;
1283
  std::string::const_iterator itr;
1284
  std::string::const_iterator end;
1285
};
1286

1287
StringIterator::StringIterator( String* pString, BObject* pIterVal )
54✔
1288
    : ContIterator(),
1289
      m_StringObj( pString ),
54✔
1290
      m_IterVal( pIterVal ),
54✔
1291
      m_pIterVal( new BLong( 0 ) ),
54✔
1292
      itr( pString->value().cbegin() ),
54✔
1293
      end( pString->value().cend() )
108✔
1294
{
1295
}
54✔
1296

1297
BObject* StringIterator::step()
324✔
1298
{
1299
  if ( itr == end )
324✔
1300
    return nullptr;
57✔
1301

1302
  // Iterate one utf8 character
1303
  auto previous = itr;
267✔
1304
  utf8::unchecked::next( itr );
267✔
1305
  m_pIterVal->increment();
267✔
1306
  m_IterVal->setimp( m_pIterVal );
267✔
1307

1308
  // Set current value to new substring
1309
  return new BObject( new String( std::string( previous, itr ) ) );
267✔
1310
}
1311

1312
ContIterator* String::createIterator( BObject* pIterVal )
54✔
1313
{
1314
  return new StringIterator( this, pIterVal );
54✔
1315
}
1316
}  // namespace Pol::Bscript
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc