Coverage Report

Created: 2026-05-30 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/logging-log4cxx/src/main/cpp/transcoder.cpp
Line
Count
Source
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
#include <log4cxx/logstring.h>
19
#include <log4cxx/helpers/transcoder.h>
20
#include <log4cxx/helpers/pool.h>
21
#include <stdlib.h>
22
#include <log4cxx/helpers/exception.h>
23
#include <log4cxx/helpers/bytebuffer.h>
24
#include <log4cxx/helpers/charsetdecoder.h>
25
#include <log4cxx/helpers/charsetencoder.h>
26
#include <log4cxx/helpers/stringhelper.h>
27
#include <log4cxx/helpers/loglog.h>
28
#include <vector>
29
#include <cstring>
30
#if !defined(LOG4CXX)
31
  #define LOG4CXX 1
32
#endif
33
#include <log4cxx/private/log4cxx_private.h>
34
35
#if LOG4CXX_CFSTRING_API
36
  #include <CoreFoundation/CFString.h>
37
#endif
38
39
using namespace LOG4CXX_NS;
40
using namespace LOG4CXX_NS::helpers;
41
42
43
void Transcoder::decodeUTF8(const std::string& src, LogString& dst)
44
0
{
45
0
  std::string::const_iterator iter = src.begin();
46
47
0
  while (iter != src.end())
48
0
  {
49
0
    unsigned int sv = decode(src, iter);
50
51
0
    if (sv != 0xFFFF)
52
0
    {
53
0
      encode(sv, dst);
54
0
    }
55
0
    else
56
0
    {
57
0
      dst.append(1, LOSSCHAR);
58
0
      iter++;
59
0
    }
60
0
  }
61
0
}
62
63
void Transcoder::encodeUTF8(const LogString& src, std::string& dst)
64
0
{
65
#if LOG4CXX_LOGCHAR_IS_UTF8
66
  dst.append(src);
67
#else
68
0
  LogString::const_iterator iter = src.begin();
69
70
0
  while (iter != src.end())
71
0
  {
72
0
    unsigned int sv = decode(src, iter);
73
74
0
    if (sv != 0xFFFF)
75
0
    {
76
0
      encode(sv, dst);
77
0
    }
78
0
    else
79
0
    {
80
0
      dst.append(1, LOSSCHAR);
81
0
      iter++;
82
0
    }
83
0
  }
84
85
0
#endif
86
0
}
87
88
#if LOG4CXX_ABI_VERSION <= 15
89
char* Transcoder::encodeUTF8(const LogString& src, Pool& p)
90
0
{
91
#if LOG4CXX_LOGCHAR_IS_UTF8
92
  return p.pstrdup(src);
93
#else
94
0
  std::string tmp;
95
0
  encodeUTF8(src, tmp);
96
0
  return p.pstrdup(tmp);
97
0
#endif
98
0
}
99
#endif
100
101
void Transcoder::encodeUTF8(unsigned int sv, ByteBuffer& dst)
102
0
{
103
0
  size_t bytes = encodeUTF8(sv, dst.current());
104
0
  dst.increment_position(bytes);
105
0
}
106
107
108
size_t Transcoder::encodeUTF8(unsigned int ch, char* dst)
109
0
{
110
0
  if (ch < 0x80)
111
0
  {
112
0
    dst[0] = (char) ch;
113
0
    return 1;
114
0
  }
115
0
  else if (ch < 0x800)
116
0
  {
117
0
    dst[0] = (char) (0xC0 + (ch >> 6));
118
0
    dst[1] = (char) (0x80 + (ch & 0x3F));
119
0
    return 2;
120
0
  }
121
0
  else if (ch < 0x10000)
122
0
  {
123
0
    dst[0] = (char) (0xE0 + (ch >> 12));
124
0
    dst[1] = (char) (0x80 + ((ch >> 6) & 0x3F));
125
0
    dst[2] = (char) (0x80 + (ch & 0x3F));
126
0
    return 3;
127
0
  }
128
0
  else if (ch <= 0x10FFFF)
129
0
  {
130
0
    dst[0] = (char) (0xF0 + (ch >> 18));
131
0
    dst[1] = (char) (0x80 + ((ch >> 12) & 0x3F));
132
0
    dst[2] = (char) (0x80 + ((ch >> 6) & 0x3F));
133
0
    dst[3] = (char) (0x80 + (ch & 0x3F));
134
0
    return 4;
135
0
  }
136
0
  else
137
0
  {
138
    //
139
    //  output UTF-8 encoding of 0xFFFF
140
    //
141
0
    dst[0] = (char) 0xEF;
142
0
    dst[1] = (char) 0xBF;
143
0
    dst[2] = (char) 0xBF;
144
0
    return 3;
145
0
  }
146
0
}
147
148
void Transcoder::encodeUTF16BE(unsigned int sv, ByteBuffer& dst)
149
0
{
150
0
  size_t bytes = encodeUTF16BE(sv, dst.current());
151
0
  dst.increment_position(bytes);
152
0
}
153
154
155
size_t Transcoder::encodeUTF16BE(unsigned int ch, char* dst)
156
0
{
157
0
  if (ch <= 0xFFFF)
158
0
  {
159
0
    dst[0] = (char) (ch >> 8);
160
0
    dst[1] = (char) (ch & 0xFF);
161
0
    return 2;
162
0
  }
163
164
0
  if (ch <= 0x10FFFF)
165
0
  {
166
0
    unsigned char w = (unsigned char) ((ch >> 16) - 1);
167
0
    dst[0] = (char) (0xD8 + (w >> 2));
168
0
    dst[1] = (char) (((w & 0x03) << 6) + ((ch >> 10) & 0x3F));
169
0
    dst[2] = (char) (0xDC + ((ch >> 8) & 0x03));
170
0
    dst[3] = (char) (ch & 0xFF);
171
0
    return 4;
172
0
  }
173
174
0
  dst[0] = dst[1] = (char) 0xFF;
175
0
  return 2;
176
0
}
177
178
void Transcoder::encodeUTF16LE(unsigned int sv, ByteBuffer& dst)
179
0
{
180
0
  size_t bytes = encodeUTF16LE(sv, dst.current());
181
0
  dst.increment_position(bytes);
182
0
}
183
184
size_t Transcoder::encodeUTF16LE(unsigned int ch, char* dst)
185
0
{
186
0
  if (ch <= 0xFFFF)
187
0
  {
188
0
    dst[1] = (char) (ch >> 8);
189
0
    dst[0] = (char) (ch & 0xFF);
190
0
    return 2;
191
0
  }
192
193
0
  if (ch <= 0x10FFFF)
194
0
  {
195
0
    unsigned char w = (unsigned char) ((ch >> 16) - 1);
196
0
    dst[1] = (char) (0xD8 + (w >> 2));
197
0
    dst[0] = (char) (((w & 0x03) << 6) + ((ch >> 10) & 0x3F));
198
0
    dst[3] = (char) (0xDC + ((ch >> 8) & 0x03));
199
0
    dst[2] = (char) (ch & 0xFF);
200
0
    return 4;
201
0
  }
202
203
0
  dst[0] = dst[1] = (char) 0xFF;
204
0
  return 2;
205
0
}
206
207
208
unsigned int Transcoder::decode(const std::string& src,
209
  std::string::const_iterator& iter)
210
672k
{
211
672k
  std::string::const_iterator start(iter);
212
672k
  unsigned char ch1 = *(iter++);
213
214
672k
  if (ch1 <= 0x7F)
215
502k
  {
216
502k
    return ch1;
217
502k
  }
218
219
  //
220
  //   should not have continuation character here
221
  //
222
170k
  if ((ch1 & 0xC0) != 0x80 && iter != src.end())
223
129k
  {
224
129k
    unsigned char ch2 = *(iter++);
225
226
    //
227
    //   should be continuation
228
129k
    if ((ch2 & 0xC0) != 0x80)
229
118k
    {
230
118k
      iter = start;
231
118k
      return 0xFFFF;
232
118k
    }
233
234
10.7k
    if ((ch1 & 0xE0) == 0xC0)
235
1.85k
    {
236
1.85k
      if ((ch2 & 0xC0) == 0x80)
237
1.85k
      {
238
1.85k
        unsigned int rv = ((ch1 & 0x1F) << 6) + (ch2 & 0x3F);
239
240
1.85k
        if (rv >= 0x80)
241
615
        {
242
615
          return rv;
243
615
        }
244
1.85k
      }
245
246
1.23k
      iter = start;
247
1.23k
      return 0xFFFF;
248
1.85k
    }
249
250
8.88k
    if (iter != src.end())
251
8.75k
    {
252
8.75k
      unsigned char ch3 = *(iter++);
253
254
      //
255
      //   should be continuation
256
      //
257
8.75k
      if ((ch3 & 0xC0) != 0x80)
258
2.32k
      {
259
2.32k
        iter = start;
260
2.32k
        return 0xFFFF;
261
2.32k
      }
262
263
6.43k
      if ((ch1 & 0xF0) == 0xE0)
264
2.03k
      {
265
2.03k
        unsigned rv = ((ch1 & 0x0F) << 12)
266
2.03k
          + ((ch2 & 0x3F) << 6)
267
2.03k
          + (ch3 & 0x3F);
268
269
        // RFC 3629 §3 prohibits UTF-8 encodings of the UTF-16 surrogate
270
        // halves (U+D800..U+DFFF); accepting them lets malformed Unicode
271
        // cross the decode boundary into LogString and downstream output.
272
2.03k
        if (rv < 0x800 || (0xD800 <= rv && rv <= 0xDFFF))
273
727
        {
274
727
          iter = start;
275
727
          return 0xFFFF;
276
727
        }
277
278
1.31k
        return rv;
279
2.03k
      }
280
281
4.39k
      if (iter != src.end())
282
4.31k
      {
283
4.31k
        unsigned char ch4 = *(iter++);
284
285
4.31k
        if ((ch4 & 0xC0) != 0x80)
286
1.98k
        {
287
1.98k
          iter = start;
288
1.98k
          return 0xFFFF;
289
1.98k
        }
290
291
2.32k
        unsigned int rv = ((ch1 & 0x07) << 18)
292
2.32k
          + ((ch2 & 0x3F) << 12)
293
2.32k
          + ((ch3 & 0x3F) << 6)
294
2.32k
          + (ch4 & 0x3F);
295
296
        // RFC 3629 §3 caps UTF-8 at U+10FFFF; lead bytes F5..F7 (and
297
        // F4 with an over-high trailer) produce rv > 0x10FFFF, which
298
        // is not a Unicode code point. Without this bound, encodeUTF16
299
        // later silently aliases the bogus value to a valid in-range
300
        // code point — a substitution-collision filter-bypass primitive.
301
2.32k
        if (rv > 0xFFFF && rv <= 0x10FFFF)
302
765
        {
303
765
          return rv;
304
765
        }
305
306
2.32k
      }
307
4.39k
    }
308
8.88k
  }
309
310
42.9k
  iter = start;
311
42.9k
  return 0xFFFF;
312
170k
}
313
314
315
void Transcoder::encode(unsigned int sv, std::string& dst)
316
0
{
317
0
  char tmp[8];
318
0
  size_t bytes = encodeUTF8(sv, tmp);
319
0
  dst.append(tmp, bytes);
320
0
}
321
322
323
void Transcoder::decode(const std::string& src, LogString& dst)
324
34.4k
{
325
#if LOG4CXX_CHARSET_UTF8 && LOG4CXX_LOGCHAR_IS_UTF8
326
  dst.append(src);
327
#else
328
34.4k
  static CharsetDecoderPtr decoder(CharsetDecoder::getDefaultDecoder());
329
34.4k
  dst.reserve(dst.size() + src.size());
330
34.4k
  std::string::const_iterator iter = src.begin();
331
34.4k
#if !LOG4CXX_CHARSET_EBCDIC
332
333
34.4k
  for (;
334
517k
    iter != src.end() && ((unsigned char) *iter) < 0x80;
335
482k
    iter++)
336
482k
  {
337
482k
    dst.append(1, *iter);
338
482k
  }
339
340
34.4k
#endif
341
342
34.4k
  if (iter != src.end())
343
3.21k
  {
344
3.21k
    size_t offset = iter - src.begin();
345
3.21k
    ByteBuffer buf(const_cast<char*>(src.data() + offset), src.size() - offset);
346
347
173k
    while (buf.remaining() > 0)
348
170k
    {
349
170k
      log4cxx_status_t stat = decoder->decode(buf, dst);
350
351
170k
      if (CharsetDecoder::isError(stat))
352
168k
      {
353
168k
        dst.append(1, LOSSCHAR);
354
168k
        buf.increment_position(1);
355
168k
      }
356
170k
    }
357
358
3.21k
    decoder->decode(buf, dst);
359
3.21k
  }
360
361
34.4k
#endif
362
34.4k
}
363
364
#if LOG4CXX_ABI_VERSION <= 15
365
char* Transcoder::encode(const LogString& src, Pool& p)
366
0
{
367
#if LOG4CXX_CHARSET_UTF8 && LOG4CXX_LOGCHAR_IS_UTF8
368
  return p.pstrdup(src);
369
#else
370
0
  std::string tmp;
371
0
  encode(src, tmp);
372
0
  return p.pstrdup(tmp);
373
0
#endif
374
0
}
375
#endif
376
377
378
void Transcoder::encode(const LogString& src, std::string& dst)
379
0
{
380
#if LOG4CXX_CHARSET_UTF8 && LOG4CXX_LOGCHAR_IS_UTF8
381
  dst.append(src);
382
#else
383
0
  static CharsetEncoderPtr encoder(CharsetEncoder::getDefaultEncoder());
384
0
  dst.reserve(dst.size() + src.size());
385
0
  LogString::const_iterator iter = src.begin();
386
0
#if !LOG4CXX_CHARSET_EBCDIC
387
388
0
  for (;
389
0
    iter != src.end() && ((unsigned int) *iter) < 0x80;
390
0
    iter++)
391
0
  {
392
0
    dst.append(1, static_cast<char>(*iter));
393
0
  }
394
395
0
#endif
396
397
0
  if (iter != src.end())
398
0
  {
399
0
    char buf[BUFSIZE];
400
0
    ByteBuffer out(buf, BUFSIZE);
401
402
0
    while (iter != src.end())
403
0
    {
404
0
      log4cxx_status_t stat = encoder->encode(src, iter, out);
405
0
      out.flip();
406
0
      dst.append(out.data(), out.limit());
407
0
      out.clear();
408
409
0
      if (CharsetEncoder::isError(stat))
410
0
      {
411
0
        dst.append(1, LOSSCHAR);
412
0
        iter++;
413
0
      }
414
0
    }
415
416
0
    encoder->encode(src, iter, out);
417
0
  }
418
419
0
#endif
420
0
}
421
422
423
template<class String, class Iterator>
424
static unsigned int decodeUTF16(const String& in, Iterator& iter)
425
{
426
  unsigned int ch1 = *iter;
427
428
  //
429
  //   if not surrogate pair
430
  //
431
  if (ch1 < 0xD800 || ch1 > 0xDFFF)
432
  {
433
    //
434
    //  then advance iterator and return wchar_t value
435
    //
436
    if (ch1 != 0xFFFF)
437
    {
438
      iter++;
439
    }
440
441
    return ch1;
442
  }
443
  else if (ch1 < 0xDC00)
444
  {
445
    //
446
    //  started with high-surrogate value
447
    //     if there is an additional wchar_t
448
    Iterator iter2 = iter + 1;
449
450
    if (iter2 != in.end())
451
    {
452
      unsigned int ch2 = *iter2;
453
454
      //
455
      //    if it is a matching low surrogate then
456
      //       advance the iterator and return the scalar value
457
      if (ch2 >= 0xDC00 && ch2 <= 0xDFFF)
458
      {
459
        iter += 2;
460
        return (ch1 - 0xD800) * 0x400 + (ch2 - 0xDC00) + 0x10000;
461
      }
462
    }
463
  }
464
465
  //
466
  //    unrecognized value, do not advance iterator
467
  //
468
  return 0xFFFF;
469
}
470
471
template<class String>
472
static void encodeUTF16(unsigned int sv, String& dst)
473
{
474
  if (sv < 0x10000)
475
  {
476
    dst.append(1, sv);
477
  }
478
  else
479
  {
480
    unsigned char u = (unsigned char) (sv >> 16);
481
    unsigned char w = (unsigned char) (u - 1);
482
    unsigned short hs = (0xD800 + ((w & 0xF) << 6) + ((sv & 0xFFFF) >> 10));
483
    unsigned short ls = (0xDC00 + (sv & 0x3FF));
484
    dst.append(1, hs);
485
    dst.append(1, ls);
486
  }
487
}
488
489
490
491
#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR || defined(WIN32) || defined(_WIN32)
492
void Transcoder::decode(const std::wstring& src, LogString& dst)
493
0
{
494
0
#if LOG4CXX_LOGCHAR_IS_WCHAR
495
0
  dst.append(src);
496
#else
497
  std::wstring::const_iterator i = src.begin();
498
499
  while (i != src.end())
500
  {
501
    unsigned int cp = decode(src, i);
502
503
    if (cp != 0xFFFF)
504
    {
505
      encode(cp, dst);
506
    }
507
    else
508
    {
509
      dst.append(1, LOSSCHAR);
510
      i++;
511
    }
512
  }
513
514
#endif
515
0
}
516
517
void Transcoder::encode(const LogString& src, std::wstring& dst)
518
2.71k
{
519
2.71k
#if LOG4CXX_LOGCHAR_IS_WCHAR
520
2.71k
  dst.append(src);
521
#else
522
523
  for (LogString::const_iterator i = src.begin(); i != src.end();)
524
  {
525
    unsigned int cp = Transcoder::decode(src, i);
526
527
    if (cp != 0xFFFF)
528
    {
529
      encode(cp, dst);
530
    }
531
    else
532
    {
533
      dst.append(1, LOSSCHAR);
534
      i++;
535
    }
536
  }
537
538
#endif
539
2.71k
}
540
541
wchar_t* Transcoder::wencode(const LogString& src, Pool& p)
542
0
{
543
0
#if LOG4CXX_LOGCHAR_IS_WCHAR
544
0
  const std::wstring& tmp = src;
545
#else
546
  std::wstring tmp;
547
  encode(src, tmp);
548
#endif
549
0
  wchar_t* dst = (wchar_t*) p.palloc((tmp.length() + 1) * sizeof(wchar_t));
550
0
  dst[tmp.length()] = 0;
551
0
  std::memcpy(dst, tmp.data(), tmp.length() * sizeof(wchar_t));
552
0
  return dst;
553
0
}
554
555
556
unsigned int Transcoder::decode(const std::wstring& in,
557
  std::wstring::const_iterator& iter)
558
1.24M
{
559
1.24M
#if defined(__STDC_ISO_10646__)
560
1.24M
  return *(iter++);
561
#else
562
  return decodeUTF16(in, iter);
563
#endif
564
1.24M
}
565
566
567
void Transcoder::encode(unsigned int sv, std::wstring& dst)
568
504k
{
569
504k
#if defined(__STDC_ISO_10646__)
570
504k
  dst.append(1, sv);
571
#else
572
573
  if (sizeof(wchar_t) == 4)
574
  {
575
    dst.append(1, sv);
576
  }
577
  else
578
  {
579
    encodeUTF16(sv, dst);
580
  }
581
582
#endif
583
504k
}
584
585
#endif
586
587
588
589
#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR
590
void Transcoder::decode(const std::basic_string<UniChar>& src, LogString& dst)
591
{
592
#if LOG4CXX_LOGCHAR_IS_UNICHAR
593
  dst.append(src);
594
#else
595
596
  for (std::basic_string<UniChar>::const_iterator i = src.begin();
597
    i != src.end();)
598
  {
599
    unsigned int cp = decode(src, i);
600
    encode(cp, dst);
601
  }
602
603
#endif
604
}
605
606
void Transcoder::encode(const LogString& src, std::basic_string<UniChar>& dst)
607
{
608
#if LOG4CXX_LOGCHAR_IS_UNICHAR
609
  dst.append(src);
610
#else
611
612
  for (LogString::const_iterator i = src.begin();
613
    i != src.end();)
614
  {
615
    unsigned int cp = decode(src, i);
616
    encode(cp, dst);
617
  }
618
619
#endif
620
}
621
622
unsigned int Transcoder::decode(const std::basic_string<UniChar>& in,
623
  std::basic_string<UniChar>::const_iterator& iter)
624
{
625
  return decodeUTF16(in, iter);
626
}
627
628
void Transcoder::encode(unsigned int sv, std::basic_string<UniChar>& dst)
629
{
630
  encodeUTF16(sv, dst);
631
}
632
633
#endif
634
635
#if LOG4CXX_CFSTRING_API
636
void Transcoder::decode(const CFStringRef& src, LogString& dst)
637
{
638
  auto length = CFStringGetLength(src);
639
#if defined(_DEBUG)
640
  if (LogLog::isDebugEnabled())
641
  {
642
    LogString msg(LOG4CXX_STR("Transcoder::decodeCFString"));
643
    msg += LOG4CXX_STR(" length ");
644
    StringHelper::toString((size_t)length, msg);
645
    LogLog::debug(msg);
646
  }
647
#endif
648
649
  if (length > 0)
650
  {
651
    std::vector<unsigned short> tmp(length);
652
    CFStringGetCharacters(src, CFRangeMake(0, length), &tmp[0]);
653
    for (auto i = tmp.begin(); i != tmp.end(); )
654
    {
655
      unsigned int cp = decodeUTF16(tmp, i);
656
      encode(cp, dst);
657
    }
658
  }
659
}
660
661
CFStringRef Transcoder::encode(const LogString& src)
662
{
663
  std::basic_string<unsigned short> tmp;
664
  for (auto ch : src)
665
    encodeUTF16(ch, tmp);
666
  return CFStringCreateWithCharacters(kCFAllocatorDefault, tmp.data(), tmp.size());
667
}
668
#endif // #if LOG4CXX_CFSTRING_API
669
670
671
logchar Transcoder::decode(char val)
672
0
{
673
#if LOG4CXX_CHARSET_EBCDIC
674
  LogString dst;
675
  Transcoder::decode(std::string(1, val), dst);
676
  return dst[0];
677
#else
678
0
  return val;
679
0
#endif
680
0
}
681
682
LogString Transcoder::decode(const char* val)
683
2
{
684
#if LOG4CXX_LOGCHAR_IS_UTF8 && !LOG4CXX_CHARSET_EBCDIC
685
  return val;
686
#else
687
2
  LogString dst;
688
2
  Transcoder::decode(val, dst);
689
2
  return dst;
690
2
#endif
691
2
}
692
693
694
std::string Transcoder::encodeCharsetName(const LogString& val)
695
0
{
696
0
  char asciiTable[] = { ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
697
0
      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
698
0
      '@', 'A', 'B', 'C', 'D', 'E', 'F',  'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
699
0
      'P', 'Q', 'R', 'S', 'T', 'U', 'V',  'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
700
0
      '`', 'a', 'b', 'c', 'd', 'e', 'f',  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
701
0
      'p', 'q', 'r', 's', 't', 'u', 'v',  'w', 'x', 'y', 'z', '{', '|', '}', '~'
702
0
    };
703
0
  std::string out;
704
705
0
  for (auto& item : val)
706
0
  {
707
0
    if (item >= 0x20 && item < 0x7F)
708
0
    {
709
0
      out.append(1, asciiTable[item - 0x20]);
710
0
    }
711
0
    else
712
0
    {
713
0
      out.append(1, LOSSCHAR);
714
0
    }
715
0
  }
716
717
0
  return out;
718
0
}