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

polserver / polserver / 13577146445

27 Feb 2025 10:30PM UTC coverage: 58.821% (-0.02%) from 58.838%
13577146445

push

github

web-flow
Fix display of corpse body items (hair, face, beard) on human death (#770)

* Support better test filters in coretest

* add test

* Implementation

* docs

* better comments for test

* maybe fix logfacility no-pch build

6 of 6 new or added lines in 2 files covered. (100.0%)

16 existing lines in 5 files now uncovered.

42332 of 71968 relevant lines covered (58.82%)

395120.38 hits per line

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

79.4
/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 <utf8/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 "str.h"
27

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

32
#ifdef _MSC_VER
33
#pragma warning( disable : 4244 )
34
#include "../clib/Header_Windows.h"
35
#include <codecvt>
36
#endif
37

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

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

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

55
String::String( const char* str, Tainted san ) : BObjectImp( OTString ), value_( str )
50,343✔
56
{
57
  if ( san == Tainted::YES )
50,343✔
58
    Clib::sanitizeUnicodeWithIso( &value_ );
65✔
59
}
50,343✔
60

61
String::String( const std::string& str, Tainted san ) : BObjectImp( OTString ), value_( str )
30,809✔
62
{
63
  if ( san == Tainted::YES )
30,809✔
64
    Clib::sanitizeUnicodeWithIso( &value_ );
4,515✔
65
}
30,809✔
66

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

73
String* String::StrStr( int begin, int len ) const
20✔
74
{
75
  auto itr = value_.cbegin();
20✔
76
  --begin;
20✔
77
  size_t startpos = getBytePosition( &itr, begin );
20✔
78
  size_t endpos = getBytePosition( &itr, len );
20✔
79
  if ( startpos != std::string::npos )
20✔
80
    return new String( value_.substr( startpos, endpos - startpos ) );
18✔
81
  return new String( value_ );
2✔
82
}
83

84
size_t String::length() const
2,060✔
85
{
86
  return utf8::unchecked::distance( value_.begin(), value_.end() );
2,060✔
87
}
88

89
String* String::ETrim( const char* CRSet, int type ) const
4✔
90
{
91
  std::string tmp = value_;
4✔
92

93
  if ( type == 1 )  // This is for Leading Only.
4✔
94
  {
95
    // Find the first character position after excluding leading blank spaces
96
    size_t startpos = tmp.find_first_not_of( CRSet );
×
97
    if ( std::string::npos != startpos )
×
98
      tmp = tmp.substr( startpos );
×
99
    else
100
      tmp = "";
×
101
    return new String( tmp );
×
102
  }
103
  else if ( type == 2 )  // This is for Trailing Only.
4✔
104
  {
105
    // Find the first character position from reverse
106
    size_t endpos = tmp.find_last_not_of( CRSet );
×
107
    if ( std::string::npos != endpos )
×
108
      tmp = tmp.substr( 0, endpos + 1 );
×
109
    else
110
      tmp = "";
×
111
    return new String( tmp );
×
112
  }
113
  else if ( type == 3 )
4✔
114
  {
115
    // Find the first character position after excluding leading blank spaces
116
    size_t startpos = tmp.find_first_not_of( CRSet );
4✔
117
    // Find the first character position from reverse af
118
    size_t endpos = tmp.find_last_not_of( CRSet );
4✔
119

120
    // if all spaces or empty return on empty string
121
    if ( ( std::string::npos == startpos ) || ( std::string::npos == endpos ) )
4✔
122
      tmp = "";
×
123
    else
124
      tmp = tmp.substr( startpos, endpos - startpos + 1 );
4✔
125
    return new String( tmp );
4✔
126
  }
127
  else
128
    return new String( tmp );
×
129
}
4✔
130

131
void String::EStrReplace( String* str1, String* str2 )
6✔
132
{
133
  std::string::size_type valpos = 0;
6✔
134
  while ( std::string::npos != ( valpos = value_.find( str1->value_, valpos ) ) )
10✔
135
  {
136
    value_.replace( valpos, str1->value_.size(), str2->value_ );
4✔
137
    valpos += str2->value_.size();
4✔
138
  }
139
}
6✔
140

141
void String::ESubStrReplace( String* replace_with, unsigned int index, unsigned int len )
4✔
142
{
143
  auto itr = value_.cbegin();
4✔
144
  size_t begin = getBytePosition( &itr, index - 1 );
4✔
145
  size_t end = getBytePosition( &itr, len );
4✔
146
  if ( begin != std::string::npos )
4✔
147
    value_.replace( begin, end - begin, replace_with->value_ );
4✔
148
}
4✔
149

150
std::string String::pack() const
17✔
151
{
152
  return "s" + value_;
17✔
153
}
154

155
void String::packonto( std::ostream& os ) const
32✔
156
{
157
  os << "S" << value_.size() << ":" << value_;
32✔
158
}
32✔
159
void String::packonto( std::ostream& os, const std::string& value )
10✔
160
{
161
  os << "S" << value.size() << ":" << value;
10✔
162
}
10✔
163

164
BObjectImp* String::unpack( std::istream& is )
20✔
165
{
166
  std::string tmp;
20✔
167
  getline( is, tmp );
20✔
168

169
  return new String( tmp );
40✔
170
}
20✔
171

172
BObjectImp* String::unpackWithLen( std::istream& is )
23✔
173
{
174
  unsigned len;
175
  char colon;
176
  if ( !( is >> len >> colon ) )
23✔
177
  {
178
    return new BError( "Unable to unpack string length." );
×
179
  }
180
  if ( (int)len < 0 )
23✔
181
  {
182
    return new BError( "Unable to unpack string length. Invalid length!" );
×
183
  }
184
  if ( colon != ':' )
23✔
185
  {
186
    return new BError( "Unable to unpack string length. Bad format. Colon not found!" );
×
187
  }
188

189
  is.unsetf( std::ios::skipws );
23✔
190
  std::string tmp;
23✔
191
  tmp.reserve( len );
23✔
192
  while ( len-- )
129✔
193
  {
194
    char ch = '\0';
106✔
195
    if ( !( is >> ch ) || ch == '\0' )
106✔
196
    {
197
      return new BError( "Unable to unpack string length. String length excessive." );
×
198
    }
199
    tmp += ch;
106✔
200
  }
201

202
  is.setf( std::ios::skipws );
23✔
203
  return new String( tmp );
23✔
204
}
23✔
205

206
size_t String::sizeEstimate() const
1,198✔
207
{
208
  return sizeof( String ) + value_.capacity();
1,198✔
209
}
210

211
/*
212
    0-based string find
213
    find( "str srch", 2, "srch"):
214
    01^-- start
215
    */
216
int String::find( int begin, const char* target ) const
4,683✔
217
{
218
  // returns -1 when begin is out of range for string
219
  auto itr = value_.cbegin();
4,683✔
220
  size_t pos = getBytePosition( &itr, begin );
4,683✔
221
  pos = value_.find( target, pos );
4,683✔
222
  if ( pos == std::string::npos )
4,683✔
223
    return -1;
4,111✔
224
  else
225
  {
226
    pos = utf8::unchecked::distance( value_.cbegin(), std::next( value_.cbegin(), pos ) );
572✔
227
    return static_cast<int>( pos );
572✔
228
  }
229
}
230

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

256
BObjectImp* String::selfPlusObjImp( const BObjectImp& objimp ) const
2,927✔
257
{
258
  return objimp.selfPlusObj( *this );
2,927✔
259
}
260
BObjectImp* String::selfPlusObj( const BObjectImp& objimp ) const
678✔
261
{
262
  return new String( value_ + objimp.getStringRep() );
678✔
263
}
264
BObjectImp* String::selfPlusObj( const BLong& objimp ) const
1,371✔
265
{
266
  return new String( value_ + objimp.getStringRep() );
1,371✔
267
}
268
BObjectImp* String::selfPlusObj( const Double& objimp ) const
244✔
269
{
270
  return new String( value_ + objimp.getStringRep() );
244✔
271
}
272
BObjectImp* String::selfPlusObj( const String& objimp ) const
2,871✔
273
{
274
  return new String( value_ + objimp.getStringRep() );
2,871✔
275
}
276
BObjectImp* String::selfPlusObj( const ObjArray& objimp ) const
122✔
277
{
278
  return new String( value_ + objimp.getStringRep() );
122✔
279
}
280
void String::selfPlusObjImp( BObjectImp& objimp, BObject& obj )
117✔
281
{
282
  objimp.selfPlusObj( *this, obj );
117✔
283
}
117✔
284
void String::selfPlusObj( BObjectImp& objimp, BObject& /*obj*/ )
16✔
285
{
286
  value_ += objimp.getStringRep();
16✔
287
}
16✔
288
void String::selfPlusObj( BLong& objimp, BObject& /*obj*/ )
10✔
289
{
290
  value_ += objimp.getStringRep();
10✔
291
}
10✔
292
void String::selfPlusObj( Double& objimp, BObject& /*obj*/ )
8✔
293
{
294
  value_ += objimp.getStringRep();
8✔
295
}
8✔
296
void String::selfPlusObj( String& objimp, BObject& /*obj*/ )
105✔
297
{
298
  value_ += objimp.getStringRep();
105✔
299
}
105✔
300
void String::selfPlusObj( ObjArray& objimp, BObject& /*obj*/ )
6✔
301
{
302
  value_ += objimp.getStringRep();
6✔
303
}
6✔
304

305

306
void String::remove( const std::string& rm )
194✔
307
{
308
  auto pos = value_.find( rm );
194✔
309
  if ( pos != std::string::npos )
194✔
310
    value_.erase( pos, rm.size() );
40✔
311
}
194✔
312

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

372
bool String::operator==( const BObjectImp& objimp ) const
1,886✔
373
{
374
  if ( objimp.isa( OTString ) )
1,886✔
375
    return ( value_ == static_cast<const String&>( objimp ).value_ );
1,774✔
376

377
  if ( objimp.isa( OTBoolean ) )
112✔
378
    return isTrue() == static_cast<const BBoolean&>( objimp ).isTrue();
4✔
379
  return base::operator==( objimp );
108✔
380
}
381

382
bool String::operator<( const BObjectImp& objimp ) const
1,787✔
383
{
384
  if ( objimp.isa( OTString ) )
1,787✔
385
    return ( value_ < static_cast<const String&>( objimp ).value_ );
1,639✔
386

387
  return base::operator<( objimp );
148✔
388
}
389

390
namespace
391
{
392
template <typename T, typename std::enable_if<sizeof( T ) == sizeof( unsigned int ), int>::type = 0>
393
std::vector<wchar_t> convertutf8( const std::string& value )
12✔
394
{
395
  std::vector<wchar_t> codes;
12✔
396
  utf8::unchecked::utf8to32( value.begin(), value.end(), std::back_inserter( codes ) );
12✔
397
  return codes;
12✔
398
}
×
399
template <typename T,
400
          typename std::enable_if<sizeof( T ) == sizeof( unsigned short ), int>::type = 0>
401
std::vector<wchar_t> convertutf8( const std::string& value )
402
{
403
  std::vector<wchar_t> codes;
404
  utf8::unchecked::utf8to16( value.begin(), value.end(), std::back_inserter( codes ) );
405
  return codes;
406
}
407
}  // namespace
408

409
void String::toUpper()
26✔
410
{
411
  if ( !hasUTF8Characters() )
26✔
412
  {
413
    Clib::mkupperASCII( value_ );
20✔
414
    return;
20✔
415
  }
416
#ifndef WINDOWS
417
  std::vector<wchar_t> codes = convertutf8<wchar_t>( value_ );
6✔
418
  value_.clear();
6✔
419
  for ( const auto& c : codes )
158✔
420
  {
421
    utf8::unchecked::append( std::towupper( c ), std::back_inserter( value_ ) );
152✔
422
  }
423
#else
424
  std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
425
  std::wstring str = converter.from_bytes( value_ );
426

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

448
void String::toLower()
16✔
449
{
450
  if ( !hasUTF8Characters() )
16✔
451
  {
452
    Clib::mklowerASCII( value_ );
10✔
453
    return;
10✔
454
  }
455
#ifndef WINDOWS
456
  std::vector<wchar_t> codes = convertutf8<wchar_t>( value_ );
6✔
457
  value_.clear();
6✔
458
  for ( const auto& c : codes )
158✔
459
  {
460
    utf8::unchecked::append( std::towlower( c ), std::back_inserter( value_ ) );
152✔
461
  }
462
#else
463
  std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
464
  std::wstring str = converter.from_bytes( value_ );
465

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

487
size_t String::getBytePosition( std::string::const_iterator* itr, size_t codeindex ) const
5,461✔
488
{
489
  auto itr_end = value_.cend();
5,461✔
490
  for ( size_t i = 0; i < codeindex && *itr != itr_end; ++i )
24,481✔
491
    utf8::unchecked::next( *itr );
19,020✔
492

493
  if ( *itr != itr_end )
5,461✔
494
  {
495
    return std::distance( value_.cbegin(), *itr );
4,863✔
496
  }
497
  return std::string::npos;
598✔
498
}
499

500
BObjectImp* String::array_assign( BObjectImp* idx, BObjectImp* target, bool /*copy*/ )
10✔
501
{
502
  std::string::size_type pos, len;
503

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

546
  if ( pos != std::string::npos )
10✔
547
  {
548
    if ( target->isa( OTString ) )
10✔
549
    {
550
      String* target_str = (String*)target;
10✔
551
      value_.replace( pos, len, target_str->value_ );
10✔
552
    }
553
    return this;
10✔
554
  }
555
  else
556
  {
557
    return UninitObject::create();
×
558
  }
559
}
560

561
BObjectRef String::OperMultiSubscriptAssign( std::stack<BObjectRef>& indices, BObjectImp* target )
4✔
562
{
563
  BObjectRef start_ref = indices.top();
4✔
564
  indices.pop();
4✔
565
  BObjectRef length_ref = indices.top();
4✔
566
  indices.pop();
4✔
567

568
  BObject& length_obj = *length_ref;
4✔
569
  BObject& start_obj = *start_ref;
4✔
570

571
  BObjectImp& length = length_obj.impref();
4✔
572
  BObjectImp& start = start_obj.impref();
4✔
573

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

602
  // now deal with the length.
603
  size_t len;
604
  if ( length.isa( OTLong ) )
4✔
605
  {
606
    BLong& lng = (BLong&)length;
4✔
607

608
    len = (size_t)lng.value();
4✔
609
  }
610
  else if ( length.isa( OTDouble ) )
×
611
  {
612
    Double& dbl = (Double&)length;
×
613

614
    len = (size_t)dbl.value();
×
615
  }
616
  else
617
  {
618
    return BObjectRef( copy() );
×
619
  }
620
  auto itr = value_.cbegin();
4✔
621
  std::advance( itr, index );
4✔
622
  size_t index_len = getBytePosition( &itr, len );
4✔
623

624
  if ( index_len != std::string::npos )
4✔
625
    len = index_len - index;
4✔
626
  else
627
    len = index_len;
×
628

629
  if ( target->isa( OTString ) )
4✔
630
  {
631
    String* target_str = (String*)target;
4✔
632
    value_.replace( index, len, target_str->value_ );
4✔
633
  }
634
  else
635
  {
636
    return BObjectRef( UninitObject::create() );
×
637
  }
638

639
  return BObjectRef( this );
4✔
640
}
4✔
641

642

643
BObjectRef String::OperMultiSubscript( std::stack<BObjectRef>& indices )
361✔
644
{
645
  BObjectRef start_ref = indices.top();
361✔
646
  indices.pop();
361✔
647
  BObjectRef length_ref = indices.top();
361✔
648
  indices.pop();
361✔
649

650
  BObject& length_obj = *length_ref;
361✔
651
  BObject& start_obj = *start_ref;
361✔
652

653
  BObjectImp& length = length_obj.impref();
361✔
654
  BObjectImp& start = start_obj.impref();
361✔
655

656
  // first deal with the start position.
657
  size_t index;
658
  if ( start.isa( OTLong ) )
361✔
659
  {
660
    BLong& lng = (BLong&)start;
357✔
661
    index = (size_t)lng.value();
357✔
662
    if ( index == 0 || index > value_.size() )
357✔
663
      return BObjectRef( new BError( "Subscript out of range" ) );
88✔
664
    --index;
313✔
665
    auto itr = value_.cbegin();
313✔
666
    index = getBytePosition( &itr, index );
313✔
667
    if ( index == std::string::npos )
313✔
668
      return BObjectRef( new BError( "Subscript out of range" ) );
×
669
  }
670
  else if ( start.isa( OTString ) )
4✔
671
  {
672
    String& rtstr = (String&)start;
4✔
673
    std::string::size_type pos = value_.find( rtstr.value_ );
4✔
674
    if ( pos != std::string::npos )
4✔
675
      index = static_cast<unsigned int>( pos );
4✔
676
    else
677
      return BObjectRef( new UninitObject );
×
678
  }
679
  else
680
  {
681
    return BObjectRef( copy() );
×
682
  }
683

684
  // now deal with the length.
685
  size_t len;
686
  if ( length.isa( OTLong ) )
317✔
687
  {
688
    BLong& lng = (BLong&)length;
317✔
689

690
    len = (size_t)lng.value();
317✔
691
  }
692
  else if ( length.isa( OTDouble ) )
×
693
  {
694
    Double& dbl = (Double&)length;
×
695

696
    len = (size_t)dbl.value();
×
697
  }
698
  else
699
  {
700
    return BObjectRef( copy() );
×
701
  }
702
  auto itr = value_.cbegin();
317✔
703
  std::advance( itr, index );
317✔
704
  size_t index_len = getBytePosition( &itr, len );
317✔
705

706
  if ( index_len != std::string::npos )
317✔
707
    len = index_len - index;
229✔
708
  else
709
    len = index_len;
88✔
710
  return BObjectRef( new String( value_, index, len ) );
317✔
711
}
361✔
712

713
BObjectRef String::OperSubscript( const BObject& rightobj )
3,886✔
714
{
715
  const BObjectImp& right = rightobj.impref();
3,886✔
716
  if ( right.isa( OTLong ) )
3,886✔
717
  {
718
    BLong& lng = (BLong&)right;
82✔
719

720
    if ( lng.value() < 0 )
82✔
721
      return BObjectRef( new BError( "Subscript out of range" ) );
2✔
722

723
    size_t index = (size_t)lng.value();
80✔
724

725
    if ( index == 0 || index > value_.size() )
80✔
726
      return BObjectRef( new BError( "Subscript out of range" ) );
6✔
727

728
    --index;
74✔
729
    auto itr = value_.cbegin();
74✔
730
    index = getBytePosition( &itr, index );
74✔
731
    if ( index != std::string::npos )
74✔
732
    {
733
      utf8::unchecked::next( itr );
74✔
734
      size_t len = std::distance( value_.cbegin(), itr ) - index;
74✔
735
      return BObjectRef( new BObject( new String( value_.c_str() + index, len ) ) );
74✔
736
    }
737
    return BObjectRef( new BError( "Subscript out of range" ) );
×
738
  }
739
  else if ( right.isa( OTDouble ) )
3,804✔
740
  {
741
    Double& dbl = (Double&)right;
6✔
742

743
    if ( dbl.value() < 0 )
6✔
744
      return BObjectRef( new BError( "Subscript out of range" ) );
2✔
745
    size_t index = (size_t)dbl.value();
4✔
746

747
    if ( index == 0 || index > value_.size() )
4✔
748
      return BObjectRef( new BError( "Subscript out of range" ) );
4✔
749

750
    --index;
×
751
    auto itr = value_.cbegin();
×
752
    index = getBytePosition( &itr, index );
×
753
    if ( index != std::string::npos )
×
754
    {
755
      utf8::unchecked::next( itr );
×
756
      size_t len = std::distance( value_.cbegin(), itr ) - index;
×
757
      return BObjectRef( new BObject( new String( value_.c_str() + index, len ) ) );
×
758
    }
759
    return BObjectRef( new BError( "Subscript out of range" ) );
×
760
  }
761
  else if ( right.isa( OTString ) )
3,798✔
762
  {
763
    String& rtstr = (String&)right;
3,784✔
764
    auto pos = value_.find( rtstr.value_ );
3,784✔
765
    if ( pos != std::string::npos )
3,784✔
766
    {
767
      auto itr = value_.cbegin();
194✔
768
      std::advance( itr, pos );
194✔
769
      utf8::unchecked::next( itr );
194✔
770
      size_t len = std::distance( value_.cbegin(), itr ) - pos;
194✔
771
      return BObjectRef( new BObject( new String( value_, pos, len ) ) );
194✔
772
    }
773
    else
774
      return BObjectRef( new UninitObject );
3,590✔
775
  }
776
  else
777
  {
778
    return BObjectRef( new UninitObject );
14✔
779
  }
780
}
781

782
// -- format related stuff --
783
bool s_parse_int( int& i, std::string const& s )
26✔
784
{
785
  if ( s.empty() )
26✔
786
    return false;
×
787

788
  char* end;
789
  i = strtol( s.c_str(), &end, 10 );
26✔
790

791
  if ( !*end )
26✔
792
  {
793
    return true;
18✔
794
  }
795
  else
796
  {
797
    return false;
8✔
798
  }
799
}
800

801
void int_to_binstr( int& value, std::stringstream& s )
×
802
{
803
  int i;
804
  for ( i = 31; i > 0; i-- )
×
805
  {
806
    if ( value & ( 1 << i ) )
×
807
      break;
×
808
  }
809
  for ( ; i >= 0; i-- )
×
810
  {
811
    if ( value & ( 1 << i ) )
×
812
      s << "1";
×
813
    else
814
      s << "0";
×
815
  }
816
}
×
817

818
// suplementory function to format
819
bool try_to_format( std::stringstream& to_stream, BObjectImp* what, std::string& frmt )
958✔
820
{
821
  if ( frmt.empty() )
958✔
822
  {
823
    to_stream << what->getStringRep();
899✔
824
    return false;
899✔
825
  }
826

827
  if ( frmt.find( 'b' ) != std::string::npos )
59✔
828
  {
829
    if ( auto* plong = impptrIf<BLong>( what ) )
×
830
    {
831
      int n = plong->value();
×
832
      if ( frmt.find( '#' ) != std::string::npos )
×
833
        to_stream << ( ( n < 0 ) ? "-" : "" ) << "0b";
×
834
      int_to_binstr( n, to_stream );
×
835
    }
836
    else
837
    {
838
      to_stream << "<needs Int>";
×
839
      return false;
×
840
    }
841
  }
842
  else if ( frmt.find( 'x' ) != std::string::npos )
59✔
843
  {
844
    if ( auto* plong = impptrIf<BLong>( what ) )
59✔
845
    {
846
      int n = plong->value();
52✔
847
      if ( frmt.find( '#' ) != std::string::npos )
52✔
848
        to_stream << "0x";
6✔
849
      to_stream << std::hex << n << std::dec;
52✔
850
    }
851
    else
852
    {
853
      to_stream << "<needs Int>";
7✔
854
      return false;
7✔
855
    }
856
  }
857
  else if ( frmt.find( 'o' ) != std::string::npos )
×
858
  {
859
    if ( auto* plong = impptrIf<BLong>( what ) )
×
860
    {
861
      int n = plong->value();
×
862
      if ( frmt.find( '#' ) != std::string::npos )
×
863
        to_stream << "0o";
×
864
      to_stream << std::oct << n << std::dec;
×
865
    }
866
    else
867
    {
868
      to_stream << "<needs Int>";
×
869
      return false;
×
870
    }
871
  }
872
  else if ( frmt.find( 'd' ) != std::string::npos )
×
873
  {
874
    int n;
875
    if ( auto* plong = impptrIf<BLong>( what ) )
×
876
      n = plong->value();
×
877
    else if ( auto* pdbl = impptrIf<Double>( what ) )
×
878
      n = (int)pdbl->value();
×
879
    else
880
    {
881
      to_stream << "<needs Int, Double>";
×
882
      return false;
×
883
    }
884
    to_stream << std::dec << n;
×
885
  }
886
  else
887
  {
888
    to_stream << "<bad format: " << frmt << ">";
×
889
    return false;
×
890
  }
891
  return true;
52✔
892
}
893
// --
894

895
std::string get_formatted( BObjectImp* what, std::string& frmt )
51✔
896
{
897
  std::stringstream result;
51✔
898
  try_to_format( result, what, frmt );
51✔
899
  return result.str();
102✔
900
}
51✔
901

902
BObjectImp* String::call_method( const char* methodname, Executor& ex )
×
903
{
904
  ObjMethod* objmethod = getKnownObjMethod( methodname );
×
905
  if ( objmethod != nullptr )
×
906
    return this->call_method_id( objmethod->id, ex );
×
907
  else
908
    return nullptr;
×
909
}
910
BObjectImp* String::call_method_id( const int id, Executor& ex, bool /*forcebuiltin*/ )
2,127✔
911
{
912
  switch ( id )
2,127✔
913
  {
914
  case MTH_LENGTH:
556✔
915
    if ( ex.numParams() == 0 )
556✔
916
      return new BLong( static_cast<int>( length() ) );
556✔
917
    else
918
      return new BError( "string.length() doesn't take parameters." );
×
919
    break;
920
  case MTH_FIND:
735✔
921
  {
922
    if ( ex.numParams() > 2 )
735✔
923
      return new BError( "string.find(Search, [Start]) takes only two parameters" );
×
924
    if ( ex.numParams() < 1 )
735✔
925
      return new BError( "string.find(Search, [Start]) takes at least one parameter" );
×
926
    const char* s = ex.paramAsString( 0 );
735✔
927
    int d = 0;
735✔
928
    if ( ex.numParams() == 2 )
735✔
929
      d = ex.paramAsLong( 1 );
72✔
930
    int posn = find( d ? ( d - 1 ) : 0, s ) + 1;
735✔
931
    return new BLong( posn );
735✔
932
  }
933
  case MTH_UPPER:
6✔
934
  {
935
    if ( ex.numParams() == 0 )
6✔
936
    {
937
      toUpper();
6✔
938
      return this;
6✔
939
    }
940
    else
941
      return new BError( "string.upper() doesn't take parameters." );
×
942
  }
943

944
  case MTH_LOWER:
6✔
945
  {
946
    if ( ex.numParams() == 0 )
6✔
947
    {
948
      toLower();
6✔
949
      return this;
6✔
950
    }
951
    else
952
      return new BError( "string.lower() doesn't take parameters." );
×
953
  }
954
  case MTH_FORMAT:
808✔
955
  {
956
    if ( ex.numParams() > 0 )
808✔
957
    {
958
      // string s = this->getStringRep(); // string itself
959
      std::stringstream result;
808✔
960

961
      size_t tag_start_pos;  // the position of tag's start "{"
962
      size_t tag_stop_pos;   // the position of tag's end "}"
963
      size_t tag_dot_pos;
964

965
      int tag_param_idx;
966

967
      size_t str_pos = 0;               // current string position
808✔
968
      unsigned int next_param_idx = 0;  // next index of .format() parameter
808✔
969

970
      char w_spaces[] = "\t ";
808✔
971

972
      // Tells whether last found tag was an integer
973
      bool last_tag_was_int = true;
808✔
974

975
      while ( ( tag_start_pos = value_.find( '{', str_pos ) ) != std::string::npos )
1,717✔
976
      {
977
        if ( ( tag_stop_pos = value_.find( '}', tag_start_pos ) ) != std::string::npos )
909✔
978
        {
979
          result << value_.substr( str_pos, tag_start_pos - str_pos );
909✔
980
          str_pos = tag_stop_pos + 1;
909✔
981

982
          std::string tag_body =
983
              value_.substr( tag_start_pos + 1, ( tag_stop_pos - tag_start_pos ) - 1 );
909✔
984

985
          tag_start_pos = tag_body.find_first_not_of( w_spaces );
909✔
986
          tag_stop_pos = tag_body.find_last_not_of( w_spaces );
909✔
987

988
          // cout << "' tag_body1: '" << tag_body << "'";
989

990
          // trim the tag of whitespaces (slightly faster code ~25%)
991
          if ( tag_start_pos != std::string::npos && tag_stop_pos != std::string::npos )
909✔
992
            tag_body = tag_body.substr( tag_start_pos, ( tag_stop_pos - tag_start_pos ) + 1 );
34✔
993
          else if ( tag_start_pos != std::string::npos )
875✔
994
            tag_body = tag_body.substr( tag_start_pos );
×
995
          else if ( tag_stop_pos != std::string::npos )
875✔
996
            tag_body = tag_body.substr( 0, tag_stop_pos + 1 );
×
997

998
          // cout << "' tag_body2: '" << tag_body << "'";
999

1000
          std::string frmt;
909✔
1001
          size_t formatter_pos = tag_body.find( ':' );
909✔
1002

1003
          if ( formatter_pos != std::string::npos )
909✔
1004
          {
1005
            frmt = tag_body.substr( formatter_pos + 1, std::string::npos );  //
8✔
1006
            tag_body = tag_body.substr( 0, formatter_pos );  // remove property from the tag
8✔
1007
          }
1008

1009
          std::string prop_name;
909✔
1010
          // parsing {1.this_part}
1011
          tag_dot_pos = tag_body.find( '.', 0 );
909✔
1012

1013
          // '.' is found within the tag, there is a property name
1014
          if ( tag_dot_pos != std::string::npos )
909✔
1015
          {
1016
            last_tag_was_int = true;
8✔
1017
            prop_name = tag_body.substr( tag_dot_pos + 1, std::string::npos );  //
8✔
1018
            tag_body = tag_body.substr( 0, tag_dot_pos );  // remove property from the tag
8✔
1019

1020
            // if s_tag_body is numeric then use it as an index
1021
            if ( s_parse_int( tag_param_idx, tag_body ) )
8✔
1022
            {
1023
              tag_param_idx -= 1;  // sinse POL indexes are 1-based
8✔
1024
            }
1025
            else
1026
            {
1027
              result << "<idx required before: '" << prop_name << "'>";
×
1028
              continue;
×
1029
            }
1030
          }
1031
          else
1032
          {
1033
            if ( tag_body == "" )
901✔
1034
            {
1035
              // empty body just takes next integer idx
1036
              last_tag_was_int = true;
883✔
1037
              tag_param_idx = next_param_idx++;
883✔
1038
            }
1039
            else if ( s_parse_int( tag_param_idx, tag_body ) )
18✔
1040
            {
1041
              last_tag_was_int = true;
10✔
1042
              tag_param_idx -= 1;  // sinse POL indexes are 1-based
10✔
1043
            }
1044
            else
1045
            {
1046
              // string body takes next idx in line if this is
1047
              // the first string body occurrence,
1048
              // will reuse last idx if this is 2nd or more in a row
1049
              last_tag_was_int = false;
8✔
1050
              prop_name = tag_body;
8✔
1051
              tag_param_idx = last_tag_was_int ? next_param_idx++ : next_param_idx;
8✔
1052
            }
1053
          }
1054

1055
          // -- end of property parsing
1056

1057
          // cout << "prop_name: '" << prop_name << "' tag_body: '" << tag_body << "'";
1058
          if ( tag_param_idx < 0 || (int)ex.numParams() <= tag_param_idx )
909✔
1059
          {
1060
            result << "<invalid index: #" << ( tag_param_idx + 1 ) << ">";
2✔
1061
            continue;
2✔
1062
          }
1063

1064
          BObjectImp* imp = ex.getParamImp( tag_param_idx );
907✔
1065

1066
          if ( !prop_name.empty() )
907✔
1067
          {  // accesing object
1068
            BObjectRef obj_member = imp->get_member( prop_name.c_str() );
16✔
1069
            BObjectImp* member_imp = obj_member->impptr();
16✔
1070
            try_to_format( result, member_imp, frmt );
16✔
1071
          }
16✔
1072
          else
1073
          {
1074
            try_to_format( result, imp, frmt );
891✔
1075
          }
1076
        }
913✔
1077
        else
1078
        {
1079
          break;
×
1080
        }
1081
      }
1082

1083
      if ( str_pos < value_.length() )
808✔
1084
      {
1085
        result << value_.substr( str_pos, std::string::npos );
690✔
1086
      }
1087

1088
      return new String( result.str() );
808✔
1089
    }
808✔
1090
    else
1091
    {
1092
      return new BError( "string.format() requires a parameter." );
×
1093
    }
1094
  }
1095
  case MTH_JOIN:
12✔
1096
  {
1097
    BObject* cont;
1098
    if ( ex.numParams() == 1 && ( cont = ex.getParamObj( 0 ) ) != nullptr )
12✔
1099
    {
1100
      if ( !( cont->isa( OTArray ) ) )
12✔
1101
        return new BError( "string.join expects an array" );
×
1102
      ObjArray* container = cont->impptr<ObjArray>();
12✔
1103
      // no empty check here on purpose
1104
      OSTRINGSTREAM joined;
12✔
1105
      bool first = true;
12✔
1106
      for ( const BObjectRef& ref : container->ref_arr )
58✔
1107
      {
1108
        if ( ref.get() )
46✔
1109
        {
1110
          BObject* bo = ref.get();
38✔
1111

1112
          if ( bo == nullptr )
38✔
1113
            continue;
×
1114
          if ( !first )
38✔
1115
            joined << value_;
28✔
1116
          else
1117
            first = false;
10✔
1118
          joined << bo->impptr()->getStringRep();
38✔
1119
        }
1120
      }
1121
      return new String( OSTRINGSTREAM_STR( joined ) );
12✔
1122
    }
12✔
1123
    else
1124
      return new BError( "string.join(array) requires a parameter." );
×
1125
  }
1126
  default:
4✔
1127
    return nullptr;
4✔
1128
  }
1129
}
1130

1131
bool String::hasUTF8Characters() const
43✔
1132
{
1133
  return hasUTF8Characters( value_ );
43✔
1134
}
1135

1136
bool String::hasUTF8Characters( const std::string& str )
44✔
1137
{
1138
  for ( const auto& c : str )
465✔
1139
  {
1140
    if ( c & 0x80 )
433✔
1141
      return true;
12✔
1142
  }
1143
  return false;
32✔
1144
}
1145

1146
std::vector<unsigned short> String::toUTF16() const
22✔
1147
{
1148
  std::vector<unsigned short> u16;
22✔
1149
  utf8::unchecked::utf8to16( value_.begin(), value_.end(), std::back_inserter( u16 ) );
22✔
1150
  return u16;
22✔
1151
}
×
1152

1153
std::string String::fromUTF16( unsigned short code )
206✔
1154
{
1155
  std::string s;
206✔
1156
  std::vector<unsigned short> utf16( 1, code );
206✔
1157
  utf8::unchecked::utf16to8( utf16.begin(), utf16.end(), std::back_inserter( s ) );
206✔
1158
  Clib::sanitizeUnicode( &s );
206✔
1159
  return s;
412✔
1160
}
206✔
1161

1162
std::string String::fromUTF16( const unsigned short* code, size_t len, bool big_endian )
1✔
1163
{
1164
  std::string s;
1✔
1165
  size_t short_len = 0;
1✔
1166
  // convert until the first null terminator
1167
  while ( code[short_len] != 0 && short_len < len )
11✔
1168
    ++short_len;
10✔
1169

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

1199
std::string String::fromUTF8( const char* code, size_t len )
2✔
1200
{
1201
  size_t short_len = 0;
2✔
1202
  // convert until the first null terminator
1203
  while ( code[short_len] != 0 && short_len < len )
22✔
1204
    ++short_len;
20✔
1205
  std::string s( code, short_len );
2✔
1206
  Clib::sanitizeUnicode( &s );
2✔
1207
  return s;
2✔
1208
}
×
1209

1210
std::vector<unsigned short> String::toUTF16( const std::string& text )
11✔
1211
{
1212
  std::vector<unsigned short> utf16;
11✔
1213
  if ( text.empty() )
11✔
1214
    return utf16;
1✔
1215
  utf8::unchecked::utf8to16( text.begin(), text.end(), std::back_inserter( utf16 ) );
10✔
1216
  return utf16;
10✔
1217
}
×
1218

1219
bool String::compare( const String& str ) const
2✔
1220
{
1221
  return value_.compare( str.value_ ) == 0;
2✔
1222
}
1223

1224
bool String::compare( size_t pos1, size_t len1, const String& str ) const
×
1225
{
1226
  auto itr1 = value_.cbegin();
×
1227
  pos1 = getBytePosition( &itr1, pos1 );
×
1228
  len1 = getBytePosition( &itr1, len1 ) - pos1;
×
1229
  return value_.compare( pos1, len1, str.value_ ) == 0;
×
1230
}
1231

1232
bool String::compare( size_t pos1, size_t len1, const String& str, size_t pos2, size_t len2 ) const
4✔
1233
{
1234
  auto itr1 = value_.cbegin();
4✔
1235
  pos1 = getBytePosition( &itr1, pos1 );
4✔
1236
  len1 = getBytePosition( &itr1, len1 ) - pos1;
4✔
1237
  auto itr2 = str.value_.cbegin();
4✔
1238
  pos2 = str.getBytePosition( &itr2, pos2 );
4✔
1239
  len2 = str.getBytePosition( &itr2, len2 ) - pos2;
4✔
1240
  return value_.compare( pos1, len1, str.value_, pos2, len2 ) == 0;
4✔
1241
}
1242

1243
String* String::fromUCArray( ObjArray* array, bool break_first_null )
4✔
1244
{
1245
  std::string res;
4✔
1246
  std::vector<u16> utf16;
4✔
1247
  for ( const auto& c : array->ref_arr )
20✔
1248
  {
1249
    if ( !c )
16✔
1250
      continue;
2✔
1251
    if ( auto* blong = c.get()->impptr_if<BLong>() )
14✔
1252
    {
1253
      if ( blong->value() == 0 && break_first_null )
14✔
1254
        break;
×
1255
      utf16.push_back( blong->value() & 0xFFFF );
14✔
1256
    }
1257
  }
1258
  if ( !utf16.empty() )
4✔
1259
    utf8::unchecked::utf16to8( utf16.begin(), utf16.end(), std::back_inserter( res ) );
4✔
1260

1261
  Clib::sanitizeUnicode( &res );
4✔
1262
  return new String( res );
8✔
1263
}
4✔
1264

1265
class StringIterator final : public ContIterator
1266
{
1267
public:
1268
  StringIterator( String* str, BObject* pIter );
1269
  virtual BObject* step() override;
1270

1271
private:
1272
  // Keep String alive, to ensure iterators stay valid
1273
  BObject m_StringObj;
1274
  BObjectRef m_IterVal;
1275
  BLong* m_pIterVal;
1276
  std::string::const_iterator itr;
1277
  std::string::const_iterator end;
1278
};
1279

1280
StringIterator::StringIterator( String* pString, BObject* pIterVal )
34✔
1281
    : ContIterator(),
1282
      m_StringObj( pString ),
34✔
1283
      m_IterVal( pIterVal ),
34✔
1284
      m_pIterVal( new BLong( 0 ) ),
34✔
1285
      itr( pString->value().cbegin() ),
34✔
1286
      end( pString->value().cend() )
68✔
1287
{
1288
}
34✔
1289

1290
BObject* StringIterator::step()
204✔
1291
{
1292
  if ( itr == end )
204✔
1293
    return nullptr;
36✔
1294

1295
  // Iterate one utf8 character
1296
  auto previous = itr;
168✔
1297
  utf8::unchecked::next( itr );
168✔
1298
  m_pIterVal->increment();
168✔
1299
  m_IterVal->setimp( m_pIterVal );
168✔
1300

1301
  // Set current value to new substring
1302
  return new BObject( new String( std::string( previous, itr ) ) );
168✔
1303
}
1304

1305
ContIterator* String::createIterator( BObject* pIterVal )
34✔
1306
{
1307
  return new StringIterator( this, pIterVal );
34✔
1308
}
1309
}  // namespace Bscript
1310
}  // namespace Pol
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