1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 30 авг. 2017 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <core/types.h>
23 #include <core/stdlib/stdio.h>
24 #include <core/stdlib/string.h>
25 
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <wctype.h>
29 #include <stdarg.h>
30 
31 #include <core/io/charset.h>
32 #include <core/LSPString.h>
33 
34 #define GRANULARITY     0x20
35 #define BUF_SIZE        0x200
36 //#define BUF_SIZE        16
37 
38 #define XSAFE_TRANS(index, length, result) \
39     if (index < 0) \
40     { \
41         if ((index += (length)) < 0) \
42             return result; \
43     } \
44     else if (size_t(index) > (length)) \
45         return result;
46 
47 #define XSAFE_ITRANS(index, length, result) \
48     if (index < 0) \
49     { \
50         if ((index += (length)) < 0) \
51             return result; \
52     } \
53     else if (size_t(index) >= (length)) \
54         return result;
55 
56 namespace lsp
57 {
58     static lsp_utf16_t UTF16_NULL = 0;
59 
is_space(lsp_wchar_t c)60     static bool is_space(lsp_wchar_t c)
61     {
62         switch (c)
63         {
64             case ' ':
65             case '\t':
66             case '\n':
67             case '\r':
68             case '\v':
69                 return true;
70             default:
71                 return false;
72         }
73     }
74 
LSPString()75     LSPString::LSPString()
76     {
77         nLength     = 0;
78         nCapacity   = 0;
79         pData       = NULL;
80         pTemp       = NULL;
81     }
82 
~LSPString()83     LSPString::~LSPString()
84     {
85         truncate();
86     }
87 
88 #ifndef ARCH_LE
xcmp(const lsp_wchar_t * a,const lsp_wchar_t * b,size_t n)89     int LSPString::xcmp(const lsp_wchar_t *a, const lsp_wchar_t *b, size_t n)
90     {
91         while (n--)
92         {
93             int32_t retval = int32_t(*(a++)) - int32_t(*(b++));
94             if (retval != 0)
95                 return (retval > 0) ? 1 : -1;
96         }
97         return 0;
98     }
99 #endif /* ARCH_LE */
100 
xlen(const lsp_wchar_t * s)101     inline size_t LSPString::xlen(const lsp_wchar_t *s)
102     {
103         const lsp_wchar_t *p = s;
104         while (*p != '\0')
105             ++p;
106         return p - s;
107     }
108 
xcasecmp(const lsp_wchar_t * a,const lsp_wchar_t * b,size_t n)109     int LSPString::xcasecmp(const lsp_wchar_t *a, const lsp_wchar_t *b, size_t n)
110     {
111         while (n--)
112         {
113             int32_t retval = int32_t(towlower(*(a++))) - int32_t(towlower(*(b++)));
114             if (retval != 0)
115                 return (retval > 0) ? 1 : -1;
116         }
117         return 0;
118     }
119 
acopy(lsp_wchar_t * dst,const char * src,size_t n)120     void LSPString::acopy(lsp_wchar_t *dst, const char *src, size_t n)
121     {
122         while (n--)
123             *(dst++) = uint8_t(*(src++));
124     }
125 
drop_temp()126     void LSPString::drop_temp()
127     {
128         if (pTemp == NULL)
129             return;
130 
131         if (pTemp->pData != NULL)
132             ::free(pTemp->pData);
133 
134         ::free(pTemp);
135         pTemp = NULL;
136     }
137 
clear()138     void LSPString::clear()
139     {
140         drop_temp();
141         nLength     = 0;
142     }
143 
truncate()144     void LSPString::truncate()
145     {
146         drop_temp();
147 
148         nLength     = 0;
149         nCapacity   = 0;
150         if (pData == NULL)
151             return;
152 
153         xfree(pData);
154         pData = NULL;
155     }
156 
truncate(size_t size)157     bool LSPString::truncate(size_t size)
158     {
159         drop_temp();
160         if (size > nCapacity)
161             return true;
162         if (nLength > size)
163             nLength = size;
164 
165         lsp_wchar_t *v = xrealloc(pData, size);
166         if ((v == NULL) && (size > 0))
167             return false;
168 
169         pData       = (size > 0) ? v : NULL;
170         nCapacity   = size;
171         return true;
172     }
173 
set_length(size_t length)174     size_t LSPString::set_length(size_t length)
175     {
176         if (nLength <= length)
177             return length;
178         drop_temp();
179         return nLength = length;
180     }
181 
size_reserve(size_t size)182     bool LSPString::size_reserve(size_t size)
183     {
184         if (size > 0)
185         {
186             lsp_wchar_t *v = xrealloc(pData, size);
187             if (v == NULL)
188                 return false;
189             pData   = v;
190         }
191         else if (pData != NULL)
192         {
193             ::free(pData);
194             pData   = NULL;
195         }
196 
197         nCapacity   = size;
198         return true;
199     }
200 
cap_reserve(size_t size)201     inline bool LSPString::cap_reserve(size_t size)
202     {
203         size_t ncap = (size + (GRANULARITY-1)) & (~(GRANULARITY-1));
204         return (ncap > nCapacity) ? size_reserve(ncap) : true;
205     }
206 
cap_grow(size_t delta)207     inline bool LSPString::cap_grow(size_t delta)
208     {
209         size_t avail = nCapacity - nLength;
210         if (delta <= avail)
211             return true;
212         avail = nCapacity >> 1;
213         if (avail < delta)
214             avail = delta;
215         return size_reserve(nCapacity + ((avail + (GRANULARITY-1)) & (~(GRANULARITY-1))));
216     }
217 
reduce()218     void LSPString::reduce()
219     {
220         drop_temp();
221         if (nCapacity <= nLength)
222             return;
223         lsp_wchar_t *v = xrealloc(pData, nLength);
224         if ((v == NULL) && (nLength > 0))
225             return;
226         pData       = (nLength > 0) ? v : NULL;
227         nCapacity   = nLength;
228     }
229 
trim()230     void LSPString::trim()
231     {
232         if ((pData == NULL) || (nLength <= 0))
233             return;
234 
235         // Cut tail first
236         lsp_wchar_t *p = &pData[nLength];
237         while (nLength > 0)
238         {
239             if (!is_space(*(--p)))
240                 break;
241             nLength--;
242         }
243         if (nLength <= 0)
244             return;
245 
246         // Cut head
247         p = pData;
248         while (true)
249         {
250             if (!is_space(*p))
251                 break;
252             p++;
253         }
254         if (p > pData)
255             nLength -= (p - pData);
256         if (nLength <= 0)
257             return;
258 
259         xmove(pData, p, nLength);
260     }
261 
swap(LSPString * src)262     void LSPString::swap(LSPString *src)
263     {
264         size_t len      = src->nLength;
265         size_t cap      = src->nCapacity;
266         lsp_wchar_t *c  = src->pData;
267 
268         src->nLength    = nLength;
269         src->nCapacity  = nCapacity;
270         src->pData      = pData;
271 
272         nLength         = len;
273         nCapacity       = cap;
274         pData           = c;
275     }
276 
swap(ssize_t idx1,ssize_t idx2)277     bool LSPString::swap(ssize_t idx1, ssize_t idx2)
278     {
279         XSAFE_ITRANS(idx1, nLength, false);
280         XSAFE_ITRANS(idx2, nLength, false);
281         if (idx1 == idx2)
282             return true;
283 
284         // Swap characters
285         lsp_wchar_t c   = pData[idx1];
286         pData[idx1]     = pData[idx2];
287         pData[idx2]     = c;
288 
289         return true;
290     }
291 
take(LSPString * src)292     void LSPString::take(LSPString *src)
293     {
294         drop_temp();
295         if (pData != NULL)
296             xfree(pData);
297 
298         nLength         = src->nLength;
299         nCapacity       = src->nCapacity;
300         pData           = src->pData;
301 
302         src->nLength    = 0;
303         src->nCapacity  = 0;
304         src->pData      = NULL;
305     }
306 
copy() const307     LSPString *LSPString::copy() const
308     {
309         LSPString *s = new LSPString();
310         if (s == NULL)
311             return s;
312 
313         s->nLength      = nLength;
314         s->nCapacity    = nLength;
315         if (s->nLength > 0)
316         {
317             s->pData        = xmalloc(nLength);
318             if (s->pData == NULL)
319             {
320                 delete s;
321                 return NULL;
322             }
323 
324             xmove(s->pData, pData, nLength);
325         }
326         else
327             s->pData        = NULL;
328 
329         return s;
330     }
331 
release()332     LSPString *LSPString::release()
333     {
334         LSPString *str = new LSPString();
335         if (str != NULL)
336             str->swap(this);
337         return str;
338     }
339 
copy(ssize_t first) const340     LSPString *LSPString::copy(ssize_t first) const
341     {
342         LSPString *s = new LSPString();
343         if (s != NULL)
344         {
345             if (!s->set(this, first))
346             {
347                 delete s;
348                 s = NULL;
349             }
350         }
351 
352         return s;
353     }
354 
copy(ssize_t first,ssize_t last) const355     LSPString *LSPString::copy(ssize_t first, ssize_t last) const
356     {
357         LSPString *s = new LSPString();
358         if (s != NULL)
359         {
360             if (!s->set(this, first, last))
361             {
362                 delete s;
363                 s = NULL;
364             }
365         }
366 
367         return s;
368     }
369 
at(ssize_t index) const370     lsp_wchar_t LSPString::at(ssize_t index) const
371     {
372         if (index < 0)
373         {
374             if ((index += nLength) < 0)
375                 return 0;
376         }
377         else if (size_t(index) >= nLength)
378             return 0;
379 
380         return pData[index];
381     }
382 
first() const383     lsp_wchar_t LSPString::first() const
384     {
385         return (nLength > 0) ? pData[0] : 0;
386     }
387 
last() const388     lsp_wchar_t LSPString::last() const
389     {
390         return (nLength > 0) ? pData[nLength-1] : 0;
391     }
392 
set(lsp_wchar_t ch)393     bool LSPString::set(lsp_wchar_t ch)
394     {
395         drop_temp();
396 
397         if (nCapacity == 0)
398         {
399             lsp_wchar_t *v = xmalloc(GRANULARITY);
400             if (v == NULL)
401                 return false;
402             v[0]        = ch;
403             pData       = v;
404             nCapacity   = GRANULARITY;
405         }
406         else
407             pData[0]    = ch;
408 
409         nLength     = 1;
410         return true;
411     }
412 
set(const lsp_wchar_t * arr)413     bool LSPString::set(const lsp_wchar_t *arr)
414     {
415         return set(arr, xlen(arr));
416     }
417 
set(const lsp_wchar_t * arr,size_t n)418     bool LSPString::set(const lsp_wchar_t *arr, size_t n)
419     {
420         drop_temp();
421 
422         if (!cap_reserve(n))
423             return false;
424 
425         xmove(pData, arr, n);
426         nLength     = n;
427         return true;
428     }
429 
set(ssize_t first,lsp_wchar_t ch)430     bool LSPString::set(ssize_t first, lsp_wchar_t ch)
431     {
432         XSAFE_ITRANS(first, nLength, false);
433         pData[first]    = ch;
434         return true;
435     }
436 
set(const LSPString * src)437     bool LSPString::set(const LSPString *src)
438     {
439         if (src == this)
440             return true;
441         drop_temp();
442 
443         if (!cap_reserve(src->nLength))
444             return false;
445         if (src->nLength > 0)
446             xmove(pData, src->pData, src->nLength);
447         nLength     = src->nLength;
448         return true;
449     }
450 
set(const LSPString * src,ssize_t first)451     bool LSPString::set(const LSPString *src, ssize_t first)
452     {
453         drop_temp();
454 
455         XSAFE_TRANS(first, src->nLength, false);
456         ssize_t length = src->nLength - first;
457 
458         if (length > 0)
459         {
460             if (!cap_reserve(length))
461                 return false;
462             xmove(pData, &src->pData[first], length);
463             nLength     = length;
464         }
465         else
466             nLength     = 0;
467         return true;
468     }
469 
set(const LSPString * src,ssize_t first,ssize_t last)470     bool LSPString::set(const LSPString *src, ssize_t first, ssize_t last)
471     {
472         drop_temp();
473 
474         XSAFE_TRANS(first, src->nLength, false);
475         XSAFE_TRANS(last, src->nLength, false);
476 
477         ssize_t length = last - first;
478         if (length > 0)
479         {
480             if (!cap_reserve(length))
481                 return false;
482             xmove(pData, &src->pData[first], length);
483             nLength     = length;
484         }
485         else
486             nLength     = 0;
487 
488         return true;
489     }
490 
insert(ssize_t pos,lsp_wchar_t ch)491     bool LSPString::insert(ssize_t pos, lsp_wchar_t ch)
492     {
493         XSAFE_TRANS(pos, nLength, false);
494 
495         if (!cap_grow(1))
496             return false;
497 
498         ssize_t length = nLength - pos;
499         if (length > 0)
500             xmove(&pData[pos+1], &pData[pos], length);
501 
502         pData[pos] = ch;
503         nLength++;
504         return true;
505     }
506 
insert(ssize_t pos,const lsp_wchar_t * arr,ssize_t n)507     bool LSPString::insert(ssize_t pos, const lsp_wchar_t *arr, ssize_t n)
508     {
509         XSAFE_TRANS(pos, nLength, false);
510         if (!cap_grow(n))
511             return false;
512 
513         ssize_t count = nLength - pos;
514         if (count > 0)
515             xmove(&pData[pos+n], &pData[pos], count);
516         xmove(&pData[pos], arr, n);
517         nLength    += n;
518 
519         return true;
520     }
521 
insert(ssize_t pos,const LSPString * src)522     bool LSPString::insert(ssize_t pos, const LSPString *src)
523     {
524         if (src->nLength <= 0)
525             return true;
526 
527         XSAFE_TRANS(pos, nLength, false);
528         if (!cap_grow(src->nLength))
529             return false;
530 
531         ssize_t count = nLength - pos;
532         if (count > 0)
533             xmove(&pData[pos+src->nLength], &pData[pos], count);
534         xmove(&pData[pos], src->pData, src->nLength);
535         nLength    += src->nLength;
536 
537         return true;
538     }
539 
insert(ssize_t pos,const LSPString * src,ssize_t first)540     bool LSPString::insert(ssize_t pos, const LSPString *src, ssize_t first)
541     {
542         XSAFE_TRANS(first, src->nLength, false);
543         ssize_t length = src->nLength - first;
544         if (length <= 0)
545             return true;
546 
547         XSAFE_TRANS(pos, nLength, false);
548         if (!cap_grow(length))
549             return false;
550 
551         ssize_t count = nLength - pos;
552         if (count > 0)
553             xmove(&pData[pos+length], &pData[pos], count);
554         xmove(&pData[pos], &src->pData[first], length);
555         nLength    += length;
556 
557         return true;
558     }
559 
insert(ssize_t pos,const LSPString * src,ssize_t first,ssize_t last)560     bool LSPString::insert(ssize_t pos, const LSPString *src, ssize_t first, ssize_t last)
561     {
562         XSAFE_TRANS(first, src->nLength, false);
563         XSAFE_TRANS(last, src->nLength, false);
564         ssize_t length = last - first;
565         if (length <= 0)
566             return true;
567 
568         XSAFE_TRANS(pos, nLength, false);
569         if (!cap_grow(length))
570             return false;
571 
572         ssize_t count = nLength - pos;
573         if (count > 0)
574             xmove(&pData[pos+length], &pData[pos], count);
575         xmove(&pData[pos], &src->pData[first], length);
576         nLength    += length;
577 
578         return true;
579     }
580 
append(char ch)581     bool LSPString::append(char ch)
582     {
583         if (!cap_grow(1))
584             return false;
585         pData[nLength++] = uint8_t(ch);
586         return true;
587     }
588 
append(lsp_wchar_t ch)589     bool LSPString::append(lsp_wchar_t ch)
590     {
591         if (!cap_grow(1))
592             return false;
593         pData[nLength++] = ch;
594         return true;
595     }
596 
append(lsp_swchar_t ch)597     bool LSPString::append(lsp_swchar_t ch)
598     {
599         if (!cap_grow(1))
600             return false;
601         pData[nLength++] = ch;
602         return true;
603     }
604 
append(const lsp_wchar_t * arr,size_t n)605     bool LSPString::append(const lsp_wchar_t *arr, size_t n)
606     {
607         if (!cap_grow(n))
608             return false;
609         xmove(&pData[nLength], arr, n);
610         nLength += n;
611         return true;
612     }
613 
append_ascii(const char * arr,size_t n)614     bool LSPString::append_ascii(const char *arr, size_t n)
615     {
616         if (!cap_grow(n))
617             return false;
618         acopy(&pData[nLength], arr, n);
619         nLength += n;
620         return true;
621     }
622 
append_utf8(const char * arr,size_t n)623     bool LSPString::append_utf8(const char *arr, size_t n)
624     {
625         if (nLength <= 0)
626             return set_utf8(arr, n);
627 
628         LSPString tmp;
629         if (!tmp.set_utf8(arr, n))
630             return false;
631         return append(&tmp);
632     }
633 
append(const LSPString * src)634     bool LSPString::append(const LSPString *src)
635     {
636         if (src->nLength <= 0)
637             return true;
638         if (!cap_grow(src->nLength))
639             return false;
640         xmove(&pData[nLength], src->pData, src->nLength);
641         nLength += src->nLength;
642         return true;
643     }
644 
append(const LSPString * src,ssize_t first)645     bool LSPString::append(const LSPString *src, ssize_t first)
646     {
647         XSAFE_TRANS(first, src->nLength, false);
648         ssize_t length = src->nLength - first;
649         if (length <= 0)
650             return true;
651 
652         if (!cap_grow(length))
653             return false;
654         xmove(&pData[nLength], &src->pData[first], length);
655         nLength += length;
656         return true;
657     }
658 
append(const LSPString * src,ssize_t first,ssize_t last)659     bool LSPString::append(const LSPString *src, ssize_t first, ssize_t last)
660     {
661         XSAFE_TRANS(first, src->nLength, false);
662         XSAFE_TRANS(last, src->nLength, false);
663         ssize_t length = last - first;
664         if (length <= 0)
665             return true;
666 
667         if (!cap_grow(length))
668             return false;
669         xmove(&pData[nLength], &src->pData[first], length);
670         nLength += length;
671         return true;
672     }
673 
prepend(lsp_wchar_t ch)674     bool LSPString::prepend(lsp_wchar_t ch)
675     {
676         if (!cap_grow(1))
677             return false;
678         if (nLength > 0)
679             xmove(&pData[1], pData, nLength);
680         pData[0]    = ch;
681         nLength     ++;
682         return true;
683     }
684 
prepend(const lsp_wchar_t * arr,size_t n)685     bool LSPString::prepend(const lsp_wchar_t *arr, size_t n)
686     {
687         if (n <= 0)
688             return true;
689         if (!cap_grow(n))
690             return false;
691         if (nLength > 0)
692             xmove(&pData[n], pData, nLength);
693         xmove(pData, arr, n);
694         nLength += n;
695         return true;
696     }
697 
prepend_ascii(const char * arr,size_t n)698     bool LSPString::prepend_ascii(const char *arr, size_t n)
699     {
700         if (n <= 0)
701             return true;
702         if (!cap_grow(n))
703             return false;
704         if (nLength > 0)
705             xmove(&pData[n], pData, nLength);
706         acopy(pData, arr, n);
707         nLength += n;
708         return true;
709     }
710 
prepend_utf8(const char * arr,size_t n)711     bool LSPString::prepend_utf8(const char *arr, size_t n)
712     {
713         if (nLength <= 0)
714             return set_utf8(arr, n);
715 
716         LSPString tmp;
717         if (!tmp.set_utf8(arr, n))
718             return false;
719         return prepend(&tmp);
720     }
721 
prepend(const LSPString * src)722     bool LSPString::prepend(const LSPString *src)
723     {
724         if (src->nLength <= 0)
725             return true;
726         if (!cap_grow(src->nLength))
727             return false;
728         if (nLength > 0)
729             xmove(&pData[src->nLength], pData, nLength);
730         xmove(pData, src->pData, src->nLength);
731         nLength += src->nLength;
732         return true;
733     }
734 
prepend(const LSPString * src,ssize_t first)735     bool LSPString::prepend(const LSPString *src, ssize_t first)
736     {
737         XSAFE_TRANS(first, src->nLength, false);
738         ssize_t length = src->nLength - first;
739         if (length <= 0)
740             return true;
741 
742         if (!cap_grow(length))
743             return false;
744         if (nLength > 0)
745             xmove(&pData[length], pData, nLength);
746         xmove(pData, &src->pData[first], length);
747         nLength += length;
748         return true;
749     }
750 
prepend(const LSPString * src,ssize_t first,ssize_t last)751     bool LSPString::prepend(const LSPString *src, ssize_t first, ssize_t last)
752     {
753         XSAFE_TRANS(first, src->nLength, false);
754         XSAFE_TRANS(last, src->nLength, false);
755         ssize_t length = last - first;
756         if (length <= 0)
757             return true;
758 
759         if (!cap_grow(length))
760             return false;
761         if (nLength > 0)
762             xmove(&pData[length], pData, nLength);
763         xmove(pData, &src->pData[first], length);
764         nLength += length;
765         return true;
766     }
767 
ends_with(lsp_wchar_t ch) const768     bool LSPString::ends_with(lsp_wchar_t ch) const
769     {
770         return (nLength > 0) ? pData[nLength-1] == ch : false;
771     }
772 
ends_with_nocase(lsp_wchar_t ch) const773     bool LSPString::ends_with_nocase(lsp_wchar_t ch) const
774     {
775         if (nLength <= 0)
776             return false;
777         return towlower(pData[nLength-1]) == towlower(ch);
778     }
779 
ends_with(const LSPString * src) const780     bool LSPString::ends_with(const LSPString *src) const
781     {
782         if (src->nLength <= 0)
783             return true;
784 
785         ssize_t offset = nLength - src->nLength;
786         if (offset < 0)
787             return false;
788 
789         return xcmp(&pData[offset], src->pData, src->nLength) == 0;
790     }
791 
ends_with_nocase(const LSPString * src) const792     bool LSPString::ends_with_nocase(const LSPString *src) const
793     {
794         if (src->nLength <= 0)
795             return true;
796 
797         ssize_t offset = nLength - src->nLength;
798         if (offset < 0)
799             return false;
800 
801         return xcasecmp(&pData[offset], src->pData, src->nLength) == 0;
802     }
803 
starts_with(lsp_wchar_t ch) const804     bool LSPString::starts_with(lsp_wchar_t ch) const
805     {
806         return (nLength > 0) ? pData[0] == ch : false;
807     }
808 
starts_with(lsp_wchar_t ch,size_t offset) const809     bool LSPString::starts_with(lsp_wchar_t ch, size_t offset) const
810     {
811         return (offset < nLength) ? pData[offset] == ch : false;
812     }
813 
starts_with_nocase(lsp_wchar_t ch) const814     bool LSPString::starts_with_nocase(lsp_wchar_t ch) const
815     {
816         if (nLength <= 0)
817             return false;
818         return towlower(pData[0]) == towlower(ch);
819     }
820 
starts_with_nocase(lsp_wchar_t ch,size_t offset) const821     bool LSPString::starts_with_nocase(lsp_wchar_t ch, size_t offset) const
822     {
823         if (offset < nLength)
824             return false;
825         return towlower(pData[offset]) == towlower(ch);
826     }
827 
starts_with(const LSPString * src) const828     bool LSPString::starts_with(const LSPString *src) const
829     {
830         if (src->nLength <= 0)
831             return true;
832 
833         if (nLength < src->nLength)
834             return false;
835 
836         return xcasecmp(pData, src->pData, src->nLength) == 0;
837     }
838 
starts_with_ascii(const char * str) const839     bool LSPString::starts_with_ascii(const char *str) const
840     {
841         for (size_t i=0, n=nLength; (i < n); ++i)
842         {
843             lsp_wchar_t c = uint8_t(*(str++));
844             if (c == 0)
845                 return true;
846             else if (c != pData[i])
847                 return false;
848         }
849         return (*str == '\0');
850     }
851 
starts_with(const LSPString * src,size_t offset) const852     bool LSPString::starts_with(const LSPString *src, size_t offset) const
853     {
854         if (src->nLength <= 0)
855             return true;
856 
857         if (nLength < (src->nLength + offset))
858             return false;
859 
860         return xcasecmp(&pData[offset], src->pData, src->nLength) == 0;
861     }
862 
starts_with_ascii(const char * str,size_t offset) const863     bool LSPString::starts_with_ascii(const char *str, size_t offset) const
864     {
865         for (size_t i=offset, n=nLength; (i < n); ++i)
866         {
867             lsp_wchar_t c = uint8_t(*(str++));
868             if (c == 0)
869                 return true;
870             else if (c != pData[i])
871                 return false;
872         }
873         return (*str == '\0');
874     }
875 
starts_with_nocase(const LSPString * src) const876     bool LSPString::starts_with_nocase(const LSPString *src) const
877     {
878         if (src->nLength <= 0)
879             return true;
880 
881         if (nLength < src->nLength)
882             return false;
883 
884         return xcasecmp(pData, src->pData, src->nLength) == 0;
885     }
886 
starts_with_nocase(const LSPString * src,size_t offset) const887     bool LSPString::starts_with_nocase(const LSPString *src, size_t offset) const
888     {
889         if (src->nLength <= 0)
890             return true;
891 
892         if (nLength < (offset + src->nLength))
893             return false;
894 
895         return xcasecmp(&pData[offset], src->pData, src->nLength) == 0;
896     }
897 
starts_with_ascii_nocase(const char * str) const898     bool LSPString::starts_with_ascii_nocase(const char *str) const
899     {
900         for (size_t i=0, n=nLength; (i < n); ++i)
901         {
902             lsp_wchar_t c = uint8_t(*(str++));
903             if (c == 0)
904                 return true;
905             else if (towlower(c) != towlower(pData[i]))
906                 return false;
907         }
908         return (*str == '\0');
909     }
910 
starts_with_ascii_nocase(const char * str,size_t offset) const911     bool LSPString::starts_with_ascii_nocase(const char *str, size_t offset) const
912     {
913         for (size_t i=offset, n=nLength; (i < n); ++i)
914         {
915             lsp_wchar_t c = uint8_t(*(str++));
916             if (c == 0)
917                 return true;
918             else if (towlower(c) != towlower(pData[i]))
919                 return false;
920         }
921         return (*str == '\0');
922     }
923 
remove()924     bool LSPString::remove()
925     {
926         drop_temp();
927         nLength = 0;
928         return true;
929     }
930 
remove(ssize_t first)931     bool LSPString::remove(ssize_t first)
932     {
933         XSAFE_TRANS(first, nLength, false);
934         nLength = first;
935         return true;
936     }
937 
remove(ssize_t first,ssize_t last)938     bool LSPString::remove(ssize_t first, ssize_t last)
939     {
940         XSAFE_TRANS(first, nLength, false);
941         XSAFE_TRANS(last, nLength, false);
942         ssize_t length = last - first;
943         if (length <= 0)
944             return true;
945 
946         ssize_t count = nLength - last;
947         if (count > 0)
948             xmove(&pData[first], &pData[last], count);
949 
950         nLength -= length;
951         return true;
952     }
953 
remove_last()954     bool LSPString::remove_last()
955     {
956         if (nLength <= 0)
957             return false;
958         --nLength;
959         return true;
960     }
961 
reverse()962     void LSPString::reverse()
963     {
964         drop_temp();
965 
966         size_t n = (nLength >> 1);
967         lsp_wchar_t *h = pData, *t = &pData[nLength];
968         while (n--)
969         {
970             lsp_wchar_t c = *h;
971             *(h++)  = *(--t);
972             *t      = c;
973         }
974     }
975 
shuffle()976     void LSPString::shuffle()
977     {
978         if (nLength < 2)
979             return;
980 
981         size_t n = nLength * 2;
982         size_t idx1 = rand() % nLength, idx2;
983 
984         while (n--)
985         {
986             // Generate random indexes
987             idx1 = (idx1 + rand()) % nLength;
988             idx2 = (idx1 + rand()) % nLength;
989             if (idx1 == idx2)
990                 continue;
991 
992             // Swap characters
993             lsp_wchar_t c   = pData[idx1];
994             pData[idx1]     = pData[idx2];
995             pData[idx2]     = c;
996         }
997     }
998 
replace(ssize_t pos,lsp_wchar_t ch)999     bool LSPString::replace(ssize_t pos, lsp_wchar_t ch)
1000     {
1001         XSAFE_TRANS(pos, nLength, false);
1002 
1003         if (size_t(pos) < nLength)
1004         {
1005             pData[pos]  = ch;
1006             nLength     = pos;
1007 
1008             return true;
1009         }
1010 
1011         // Append at the tail
1012         return append(ch);
1013     }
1014 
replace(ssize_t pos,const lsp_wchar_t * arr,size_t n)1015     bool LSPString::replace(ssize_t pos, const lsp_wchar_t *arr, size_t n)
1016     {
1017         XSAFE_TRANS(pos, nLength, false);
1018 
1019         if (!cap_reserve(pos + n))
1020             return false;
1021 
1022         xmove(&pData[pos], arr, n);
1023         nLength =  pos + n;
1024         return true;
1025     }
1026 
replace(ssize_t pos,const LSPString * src)1027     bool LSPString::replace(ssize_t pos, const LSPString *src)
1028     {
1029         XSAFE_TRANS(pos, nLength, false);
1030 
1031         if (!cap_reserve(pos + src->nLength))
1032             return false;
1033 
1034         xmove(&pData[pos], src->pData, src->nLength);
1035         nLength =  pos + src->nLength;
1036         return true;
1037     }
1038 
replace(ssize_t pos,const LSPString * src,ssize_t first)1039     bool LSPString::replace(ssize_t pos, const LSPString *src, ssize_t first)
1040     {
1041         XSAFE_TRANS(pos, nLength, false);
1042         XSAFE_TRANS(first, src->nLength, false);
1043         ssize_t length = src->nLength - first;
1044 
1045         if (length > 0)
1046         {
1047             if (!cap_reserve(pos + length))
1048                 return false;
1049 
1050             xmove(&pData[pos], &src->pData[first], length);
1051         }
1052         nLength =  pos + length;
1053         return true;
1054     }
1055 
replace(ssize_t pos,const LSPString * src,ssize_t first,ssize_t last)1056     bool LSPString::replace(ssize_t pos, const LSPString *src, ssize_t first, ssize_t last)
1057     {
1058         XSAFE_TRANS(pos, nLength, false);
1059         XSAFE_TRANS(first, src->nLength, false);
1060         ssize_t length = src->nLength - first;
1061 
1062         if (!cap_reserve(pos + length))
1063             return false;
1064 
1065         xmove(&pData[pos], &src->pData[first], length);
1066         nLength =  pos + length;
1067         return true;
1068     }
1069 
replace(ssize_t first,ssize_t last,lsp_wchar_t ch)1070     bool LSPString::replace(ssize_t first, ssize_t last, lsp_wchar_t ch)
1071     {
1072         XSAFE_TRANS(first, nLength, false);
1073         XSAFE_TRANS(last, nLength, false);
1074         ssize_t count = last - first;
1075         if (count < 0)
1076             count = 0;
1077 
1078         if (!cap_reserve(nLength - count + 1))
1079             return false;
1080 
1081         ssize_t tail = nLength - first - count;
1082         if (tail > 0)
1083             xmove(&pData[first + 1], &pData[tail], nLength - tail);
1084         pData[first]    = ch;
1085 
1086         nLength = nLength - count + 1;
1087         return true;
1088     }
1089 
replace(ssize_t first,ssize_t last,const lsp_wchar_t * arr,size_t n)1090     bool LSPString::replace(ssize_t first, ssize_t last, const lsp_wchar_t *arr, size_t n)
1091     {
1092         XSAFE_TRANS(first, nLength, false);
1093         XSAFE_TRANS(last, nLength, false);
1094         ssize_t count = last - first;
1095         if (count < 0)
1096             count = 0;
1097 
1098         if (!cap_reserve(nLength - count + n))
1099             return false;
1100 
1101         ssize_t tail = nLength - first - count;
1102         if (tail > 0)
1103             xmove(&pData[first + n], &pData[tail], nLength - tail);
1104         if (n > 0)
1105             xmove(&pData[first], arr, n);
1106         nLength = nLength - count + n;
1107         return true;
1108     }
1109 
replace(ssize_t first,ssize_t last,const LSPString * src)1110     bool LSPString::replace(ssize_t first, ssize_t last, const LSPString *src)
1111     {
1112         XSAFE_TRANS(first, nLength, false);
1113         XSAFE_TRANS(last, nLength, false);
1114         ssize_t count = last - first;
1115         if (count < 0)
1116             count = 0;
1117 
1118         if (!cap_reserve(nLength - count + src->nLength))
1119             return false;
1120 
1121         ssize_t tail = nLength - first - count;
1122         if (tail > 0)
1123             xmove(&pData[first + src->nLength], &pData[tail], nLength - tail);
1124         if (src->nLength > 0)
1125             xmove(&pData[first], src->pData, src->nLength);
1126         nLength = nLength - count + src->nLength;
1127         return true;
1128     }
1129 
replace(ssize_t first,ssize_t last,const LSPString * src,ssize_t sfirst)1130     bool LSPString::replace(ssize_t first, ssize_t last, const LSPString *src, ssize_t sfirst)
1131     {
1132         XSAFE_TRANS(first, nLength, false);
1133         XSAFE_TRANS(last, nLength, false);
1134         ssize_t count = last - first;
1135         if (count < 0)
1136             count = 0;
1137 
1138         XSAFE_TRANS(sfirst, src->nLength, false);
1139         ssize_t scount = src->nLength - sfirst;
1140 
1141         if (!cap_reserve(nLength - count + scount))
1142             return false;
1143 
1144         ssize_t tail = nLength - first - count;
1145         if (tail > 0)
1146             xmove(&pData[first + scount], &pData[tail], nLength - tail);
1147         if (scount > 0)
1148             xmove(&pData[first], &src->pData[sfirst], scount);
1149         nLength = nLength - count + scount;
1150         return true;
1151     }
1152 
replace(ssize_t first,ssize_t last,const LSPString * src,ssize_t sfirst,ssize_t slast)1153     bool LSPString::replace(ssize_t first, ssize_t last, const LSPString *src, ssize_t sfirst, ssize_t slast)
1154     {
1155         XSAFE_TRANS(first, nLength, false);
1156         XSAFE_TRANS(last, nLength, false);
1157         ssize_t count = last - first;
1158         if (count < 0)
1159             count = 0;
1160 
1161         XSAFE_TRANS(sfirst, src->nLength, false);
1162         XSAFE_TRANS(slast, src->nLength, false);
1163         ssize_t scount = slast - sfirst;
1164         if (scount < 0)
1165             scount = 0;
1166 
1167         if (!cap_reserve(nLength - count + scount))
1168             return false;
1169 
1170         ssize_t tail = nLength - first - count;
1171         if (tail > 0)
1172             xmove(&pData[first + scount], &pData[tail], nLength - tail);
1173         if (scount > 0)
1174             xmove(&pData[first], &src->pData[sfirst], scount);
1175         nLength = nLength - count + scount;
1176         return true;
1177     }
1178 
replace_all(lsp_wchar_t ch,lsp_wchar_t rep)1179     size_t LSPString::replace_all(lsp_wchar_t ch, lsp_wchar_t rep)
1180     {
1181         size_t n = 0;
1182         for (size_t i=0; i<nLength; ++i)
1183         {
1184             if (pData[i] == ch)
1185             {
1186                 pData[i] = rep;
1187                 ++n;
1188             }
1189         }
1190         return n;
1191     }
1192 
index_of(ssize_t start,const LSPString * str) const1193     ssize_t LSPString::index_of(ssize_t start, const LSPString *str) const
1194     {
1195         XSAFE_TRANS(start, nLength, -1);
1196         if (str->nLength <= 0)
1197             return start;
1198 
1199         ssize_t last = nLength - str->nLength;
1200         while (start < last)
1201         {
1202             if (xcmp(&pData[start], str->pData, str->nLength) == 0)
1203                 return start;
1204             start ++;
1205         }
1206         return -1;
1207     }
1208 
index_of(const LSPString * str) const1209     ssize_t LSPString::index_of(const LSPString *str) const
1210     {
1211         if (str->nLength <= 0)
1212             return 0;
1213 
1214         ssize_t start = 0, last = nLength - str->nLength;
1215         while (start < last)
1216         {
1217             if (xcmp(&pData[start], str->pData, str->nLength) == 0)
1218                 return start;
1219             start ++;
1220         }
1221         return -1;
1222     }
1223 
index_of(ssize_t start,lsp_wchar_t ch) const1224     ssize_t LSPString::index_of(ssize_t start, lsp_wchar_t ch) const
1225     {
1226         XSAFE_TRANS(start, nLength, -1);
1227 
1228         ssize_t length = nLength;
1229         while (start < length)
1230         {
1231             if (pData[start] == ch)
1232                 return start;
1233             start ++;
1234         }
1235 
1236         return -1;
1237     }
1238 
index_of(lsp_wchar_t ch) const1239     ssize_t LSPString::index_of(lsp_wchar_t ch) const
1240     {
1241         for (size_t start = 0; start < nLength; ++start)
1242         {
1243             if (pData[start] == ch)
1244                 return start;
1245         }
1246         return -1;
1247     }
1248 
rindex_of(ssize_t start,const LSPString * str) const1249     ssize_t LSPString::rindex_of(ssize_t start, const LSPString *str) const
1250     {
1251         XSAFE_ITRANS(start, nLength, -1);
1252         if (str->nLength <= 0)
1253             return start;
1254 
1255         start -= str->nLength;
1256         while (start >= 0)
1257         {
1258             if (xcmp(&pData[start], str->pData, str->nLength) == 0)
1259                 return start;
1260             start --;
1261         }
1262         return -1;
1263     }
1264 
rindex_of(const LSPString * str) const1265     ssize_t LSPString::rindex_of(const LSPString *str) const
1266     {
1267         if (str->nLength <= 0)
1268             return 0;
1269 
1270         ssize_t start = nLength - str->nLength - 1;
1271         while (start >= 0)
1272         {
1273             if (xcmp(&pData[start], str->pData, str->nLength) == 0)
1274                 return start;
1275             start --;
1276         }
1277         return -1;
1278     }
1279 
rindex_of(ssize_t start,lsp_wchar_t ch) const1280     ssize_t LSPString::rindex_of(ssize_t start, lsp_wchar_t ch) const
1281     {
1282         XSAFE_ITRANS(start, nLength, -1);
1283 
1284         while (start >= 0)
1285         {
1286             if (pData[start] == ch)
1287                 return start;
1288             start --;
1289         }
1290 
1291         return -1;
1292     }
1293 
rindex_of(lsp_wchar_t ch) const1294     ssize_t LSPString::rindex_of(lsp_wchar_t ch) const
1295     {
1296         for (ssize_t start=nLength-1; start >= 0; --start)
1297         {
1298             if (pData[start] == ch)
1299                 return start;
1300         }
1301         return -1;
1302     }
1303 
substring(ssize_t first) const1304     LSPString *LSPString::substring(ssize_t first) const
1305     {
1306         XSAFE_TRANS(first, nLength, NULL);
1307         ssize_t length = nLength - first;
1308 
1309         LSPString *s = new LSPString();
1310         if (s == NULL)
1311             return s;
1312 
1313         s->nLength      = length;
1314         s->nCapacity    = length;
1315 
1316         if (length > 0)
1317         {
1318             s->pData        = xmalloc(length);
1319             if (s->pData == NULL)
1320             {
1321                 delete s;
1322                 return NULL;
1323             }
1324 
1325             xmove(s->pData, &pData[first], length);
1326         }
1327         else
1328             s->pData        = NULL;
1329 
1330         return s;
1331     }
1332 
substring(ssize_t first,ssize_t last) const1333     LSPString *LSPString::substring(ssize_t first, ssize_t last) const
1334     {
1335         XSAFE_TRANS(first, nLength, NULL);
1336         XSAFE_TRANS(last, nLength, NULL);
1337         ssize_t length  = last - first;
1338         if (length < 0)
1339             length          = 0;
1340 
1341         LSPString *s = new LSPString();
1342         if (s == NULL)
1343             return s;
1344 
1345         s->nLength      = length;
1346         s->nCapacity    = length;
1347 
1348         if (length > 0)
1349         {
1350             s->pData        = xmalloc(length);
1351             if (s->pData == NULL)
1352             {
1353                 delete s;
1354                 return NULL;
1355             }
1356 
1357             xmove(s->pData, &pData[first], length);
1358         }
1359         else
1360             s->pData        = NULL;
1361 
1362         return s;
1363     }
1364 
compare_to(const lsp_wchar_t * src) const1365     int LSPString::compare_to(const lsp_wchar_t *src) const
1366     {
1367         return compare_to(src, xlen(src));
1368     }
1369 
compare_to(const lsp_wchar_t * src,size_t len) const1370     int LSPString::compare_to(const lsp_wchar_t *src, size_t len) const
1371     {
1372         ssize_t n = (nLength > len) ? len : nLength;
1373         const lsp_wchar_t *a = pData, *b = src;
1374 
1375         while (n--)
1376         {
1377             int retval = int(*(a++)) - int(*(b++));
1378             if (retval != 0)
1379                 return retval;
1380         }
1381 
1382         if (a < &pData[nLength])
1383             return int(*a);
1384         else if (b < &src[len])
1385             return -int(*b);
1386 
1387         return 0;
1388     }
1389 
compare_to_ascii(const char * src) const1390     int LSPString::compare_to_ascii(const char *src) const
1391     {
1392         size_t i=0;
1393         for ( ; i<nLength; ++i)
1394         {
1395             if (src[i] == '\0')
1396                 return pData[i];
1397             int retval = int(pData[i]) - uint8_t(src[i]);
1398             if (retval != 0)
1399                 return retval;
1400         }
1401         return -int(uint8_t(src[i]));
1402     }
1403 
compare_to_utf8(const char * src) const1404     int LSPString::compare_to_utf8(const char *src) const
1405     {
1406         LSPString tmp;
1407         return (tmp.set_utf8(src)) ? compare_to(&tmp) : 0;
1408     }
1409 
compare_to_ascii_nocase(const char * src) const1410     int LSPString::compare_to_ascii_nocase(const char *src) const
1411     {
1412         size_t i=0;
1413         for ( ; i<nLength; ++i)
1414         {
1415             if (src[i] == '\0')
1416                 return pData[i];
1417             int retval = int(::towlower(pData[i])) - ::towlower(uint8_t(src[i]));
1418             if (retval != 0)
1419                 return retval;
1420         }
1421         return -int(uint8_t(src[i]));
1422     }
1423 
compare_to_nocase(const lsp_wchar_t * src,size_t len) const1424     int LSPString::compare_to_nocase(const lsp_wchar_t *src, size_t len) const
1425     {
1426         ssize_t n = (nLength > len) ? len : nLength;
1427         const lsp_wchar_t *a = pData, *b = src;
1428 
1429         while (n--)
1430         {
1431             int retval = int(::towlower(*(a++))) - int(::towlower(*(b++)));
1432             if (retval != 0)
1433                 return retval;
1434         }
1435 
1436         if (a < &pData[nLength])
1437             return int(*a);
1438         else if (b < &src[len])
1439             return -int(*b);
1440 
1441         return 0;
1442     }
1443 
compare_to_nocase(const lsp_wchar_t * src) const1444     int LSPString::compare_to_nocase(const lsp_wchar_t *src) const
1445     {
1446         return compare_to_nocase(src, xlen(src));
1447     }
1448 
compare_to_utf8_nocase(const char * src) const1449     int LSPString::compare_to_utf8_nocase(const char *src) const
1450     {
1451         LSPString tmp;
1452         return (tmp.set_utf8(src)) ? compare_to_nocase(&tmp) : 0;
1453     }
1454 
tolower()1455     size_t LSPString::tolower()
1456     {
1457         for (size_t i=0; i<nLength; ++i)
1458             pData[i] = towlower(pData[i]);
1459         return nLength;
1460     }
1461 
tolower(ssize_t first)1462     size_t LSPString::tolower(ssize_t first)
1463     {
1464         XSAFE_TRANS(first, nLength, 0);
1465         ssize_t n = nLength - first;
1466         lsp_wchar_t *ptr = &pData[first];
1467         for (ssize_t i=0; i<n; ++i)
1468             ptr[i] = towlower(ptr[i]);
1469         return (n >= 0) ? n : 0;
1470     }
1471 
tolower(ssize_t first,ssize_t last)1472     size_t LSPString::tolower(ssize_t first, ssize_t last)
1473     {
1474         XSAFE_TRANS(first, nLength, 0);
1475         XSAFE_TRANS(last, nLength, 0);
1476         if (last < first)
1477         {
1478             ssize_t tmp = last;
1479             last = first;
1480             first = tmp;
1481         }
1482         ssize_t n = last - first;
1483         lsp_wchar_t *ptr = &pData[first];
1484         for (; first < last; ++first)
1485             ptr[first] = towlower(ptr[first]);
1486         return n;
1487     }
1488 
toupper()1489     size_t LSPString::toupper()
1490     {
1491         for (size_t i=0; i<nLength; ++i)
1492             pData[i] = towupper(pData[i]);
1493         return nLength;
1494     }
1495 
toupper(ssize_t first)1496     size_t LSPString::toupper(ssize_t first)
1497     {
1498         XSAFE_TRANS(first, nLength, 0);
1499         ssize_t n = nLength - first;
1500         lsp_wchar_t *ptr = &pData[first];
1501         for (ssize_t i=0; i<n; ++i)
1502             ptr[i] = towupper(ptr[i]);
1503         return (n >= 0) ? n : 0;
1504     }
1505 
toupper(ssize_t first,ssize_t last)1506     size_t LSPString::toupper(ssize_t first, ssize_t last)
1507     {
1508         XSAFE_TRANS(first, nLength, 0);
1509         XSAFE_TRANS(last, nLength, 0);
1510         if (last < first)
1511         {
1512             ssize_t tmp = last;
1513             last = first;
1514             first = tmp;
1515         }
1516         ssize_t n = last - first;
1517         lsp_wchar_t *ptr = &pData[first];
1518         for (; first < last; ++first)
1519             ptr[first] = towupper(ptr[first]);
1520         return n;
1521     }
1522 
equals(const lsp_wchar_t * src,size_t len) const1523     bool LSPString::equals(const lsp_wchar_t *src, size_t len) const
1524     {
1525         if (nLength != len)
1526             return false;
1527         if (nLength <= 0)
1528             return true;
1529 
1530         return xcmp(pData, src, nLength) == 0;
1531     }
1532 
equals(const lsp_wchar_t * src) const1533     bool LSPString::equals(const lsp_wchar_t *src) const
1534     {
1535         return equals(src, xlen(src));
1536     }
1537 
equals_nocase(const lsp_wchar_t * src,size_t len) const1538     bool LSPString::equals_nocase(const lsp_wchar_t *src, size_t len) const
1539     {
1540         if (nLength != len)
1541             return false;
1542 
1543         const lsp_wchar_t *a = pData, *b = src;
1544         for (size_t i=nLength; i>0; --i)
1545         {
1546             if (towlower(*(a++)) != towlower(*(b++)))
1547                 return false;
1548         }
1549 
1550         return true;
1551     }
1552 
equals_nocase(const lsp_wchar_t * src) const1553     bool LSPString::equals_nocase(const lsp_wchar_t *src) const
1554     {
1555         return equals_nocase(src, xlen(src));
1556     }
1557 
set_utf8(const char * s,size_t n)1558     bool LSPString::set_utf8(const char *s, size_t n)
1559     {
1560         LSPString   tmp;
1561         lsp_wchar_t ch;
1562 
1563         while (lsp_utf32_t(ch = read_utf8_streaming(&s, &n, true)) != LSP_UTF32_EOF)
1564         {
1565             // Append code point
1566             if (!tmp.append(ch))
1567                 return false;
1568         }
1569         if (n > 0)
1570             return false;
1571 
1572         tmp.swap(this);
1573         return true;
1574     }
1575 
set_utf16(const lsp_utf16_t * s)1576     bool LSPString::set_utf16(const lsp_utf16_t *s)
1577     {
1578         size_t len = 0;
1579         while (s[len] != 0)
1580             ++len;
1581 
1582         return set_utf16(s, len);
1583     }
1584 
set_utf16(const lsp_utf16_t * s,size_t n)1585     bool LSPString::set_utf16(const lsp_utf16_t *s, size_t n)
1586     {
1587         LSPString   tmp;
1588         lsp_wchar_t ch;
1589 
1590         while (lsp_utf32_t(ch = read_utf16_streaming(&s, &n, true)) != LSP_UTF32_EOF)
1591         {
1592             // Append code point
1593             if (!tmp.append(ch))
1594                 return false;
1595         }
1596         if (n > 0)
1597             return false;
1598 
1599         tmp.swap(this);
1600         return true;
1601     }
1602 
1603 #if defined(PLATFORM_WINDOWS)
set_native(const char * s,size_t n,const char * charset)1604     bool LSPString::set_native(const char *s, size_t n, const char *charset)
1605     {
1606         if (s == NULL)
1607             return false;
1608         else if (n == 0)
1609         {
1610             nLength = 0;
1611             return true;
1612         }
1613 
1614         // Get codepage
1615         ssize_t cp = codepage_from_name(charset);
1616         if (cp < 0)
1617             return false;
1618 
1619         // Estimate size of string in memory
1620         ssize_t slen = multibyte_to_widechar(cp, const_cast<CHAR *>(s), &n, NULL, NULL);
1621         if (slen <= 0)
1622             return false;
1623 
1624         // Perform native -> utf-16 encoding
1625         WCHAR *buf = reinterpret_cast<WCHAR *>(::malloc(slen * sizeof(WCHAR)));
1626         if (buf == NULL)
1627             return false;
1628 
1629         size_t bytes  = slen;
1630         slen    = multibyte_to_widechar(cp, const_cast<CHAR *>(s), &n, buf, &bytes);
1631         if (slen <= 0)
1632         {
1633             free(buf);
1634             return false;
1635         }
1636 
1637         // Set encoded utf-16 values
1638         bool res = set_utf16(buf, slen >> 1);
1639         free(buf);
1640 
1641         return res;
1642     }
1643 #else
set_native(const char * s,size_t n,const char * charset)1644     bool LSPString::set_native(const char *s, size_t n, const char *charset)
1645     {
1646         if (s == NULL)
1647             return false;
1648         else if (n == 0)
1649         {
1650             nLength = 0;
1651             return true;
1652         }
1653 
1654         char buf[BUF_SIZE];
1655         LSPString temp;
1656 
1657         // Open conversion
1658         iconv_t cd = init_iconv_to_wchar_t(charset);
1659         if (cd == iconv_t(-1))
1660             return set_utf8(s, n);
1661 
1662         size_t insize   = (n < 0) ? strlen(s) : n;
1663         size_t outsize  = BUF_SIZE;
1664         char *inbuf     = const_cast<char *>(s);
1665         char *outbuf    = buf;
1666 
1667         while (insize > 0)
1668         {
1669             // Do the conversion
1670             size_t nconv = iconv(cd, &inbuf, &insize, &outbuf, &outsize);
1671 
1672             if (nconv == (size_t) -1)
1673             {
1674                 switch (errno)
1675                 {
1676                     case E2BIG:
1677                     case EINVAL:
1678                         break;
1679                     default:
1680                         iconv_close(cd);
1681                         return false;
1682                 }
1683             }
1684 
1685             // Append set of converted characters to string
1686             ssize_t n_chars = (BUF_SIZE - outsize) / sizeof(lsp_wchar_t);
1687             if (n_chars > 0)
1688             {
1689                 if (!temp.append(reinterpret_cast<lsp_wchar_t *>(&buf[0]), n_chars))
1690                 {
1691                     iconv_close(cd);
1692                     return false;
1693                 }
1694             }
1695 
1696             size_t right = n_chars * sizeof(lsp_wchar_t);
1697             ssize_t tail = (outsize - right) % sizeof(lsp_wchar_t);
1698             if (tail > 0)
1699             {
1700                 // If there is a tail, copy it to the start of buffer
1701                 ::memmove(buf, &buf[right], tail);
1702                 outbuf  = &buf[tail];
1703                 outsize = BUF_SIZE - tail;
1704             }
1705             else
1706             {
1707                 // Otherwise just reset buffer's pointer
1708                 outbuf  = buf;
1709                 outsize = BUF_SIZE;
1710             }
1711         }
1712 
1713         // Close descriptor
1714         iconv_close(cd);
1715         take(&temp);
1716         return true;
1717     }
1718 #endif /* PLATFORM_WINDOWS */
1719 
set_ascii(const char * s,size_t n)1720     bool LSPString::set_ascii(const char *s, size_t n)
1721     {
1722         LSPString   tmp;
1723         if (!tmp.reserve(n))
1724             return false;
1725 
1726         acopy(tmp.pData, s, n);
1727         take(&tmp);
1728         nLength = n;
1729         return true;
1730     }
1731 
get_utf8(ssize_t first,ssize_t last) const1732     const char *LSPString::get_utf8(ssize_t first, ssize_t last) const
1733     {
1734         XSAFE_TRANS(first, nLength, NULL);
1735         XSAFE_TRANS(last, nLength, NULL);
1736         if (first >= last)
1737             return (last == first) ? "" : NULL;
1738 
1739         if (pTemp != NULL)
1740             pTemp->nOffset      = 0;
1741 
1742         char temp[BUF_SIZE + 16];
1743         char *th = temp, *tt = &temp[BUF_SIZE];
1744 
1745         for (ssize_t i=first; i<last; ++i)
1746         {
1747             lsp_wchar_t ch = pData[i];
1748             write_utf8_codepoint(&th, ch);
1749 
1750             if (th >= tt)
1751             {
1752                 if (!append_temp(temp, th - temp))
1753                     return NULL;
1754                 th  = temp;
1755             }
1756         }
1757 
1758         *(th++) = '\0';
1759         if (!append_temp(temp, th - temp))
1760             return NULL;
1761 
1762         return pTemp->pData;
1763     }
1764 
get_utf16(ssize_t first,ssize_t last) const1765     const lsp_utf16_t *LSPString::get_utf16(ssize_t first, ssize_t last) const
1766     {
1767         XSAFE_TRANS(first, nLength, NULL);
1768         XSAFE_TRANS(last, nLength, NULL);
1769         if (first >= last)
1770             return (last == first) ? &UTF16_NULL : NULL;
1771 
1772         if (pTemp != NULL)
1773             pTemp->nOffset      = 0;
1774 
1775         lsp_utf16_t temp[BUF_SIZE + 8];
1776         lsp_utf16_t *th = temp, *tt = &temp[BUF_SIZE];
1777 
1778         for (ssize_t i=first; i<last; ++i)
1779         {
1780             lsp_wchar_t ch = pData[i];
1781             write_utf16_codepoint(&th, ch);
1782 
1783             if (th >= tt)
1784             {
1785                 if (!append_temp(reinterpret_cast<char *>(temp), (th - temp) * sizeof(lsp_utf16_t)))
1786                     return NULL;
1787                 th  = temp;
1788             }
1789         }
1790 
1791         *(th++) = '\0';
1792         if (!append_temp(reinterpret_cast<char *>(temp), (th - temp) * sizeof(lsp_utf16_t)))
1793             return NULL;
1794 
1795         return reinterpret_cast<lsp_utf16_t *>(pTemp->pData);
1796     }
1797 
get_ascii(ssize_t first,ssize_t last) const1798     const char *LSPString::get_ascii(ssize_t first, ssize_t last) const
1799     {
1800         XSAFE_TRANS(first, nLength, NULL);
1801         XSAFE_TRANS(last, nLength, NULL);
1802         if (first >= last)
1803             return (last == first) ? "" : NULL;
1804 
1805         if (!resize_temp(last - first + 1))
1806             return NULL;
1807 
1808         lsp_wchar_t *p  = &pData[first];
1809         char *dst       = pTemp->pData;
1810 
1811         for (; first < last; ++first)
1812         {
1813             lsp_wchar_t c   = *(p++);
1814             *(dst++)        = (c <= 0x7f) ? c : 0xff;
1815         }
1816 
1817         *(dst++)        = '\0';
1818         pTemp->nOffset  = dst - pTemp->pData;
1819 
1820         return pTemp->pData;
1821     }
1822 
1823 #if defined(PLATFORM_WINDOWS)
get_native(ssize_t first,ssize_t last,const char * charset) const1824     const char *LSPString::get_native(ssize_t first, ssize_t last, const char *charset) const
1825     {
1826         XSAFE_TRANS(first, nLength, NULL);
1827         XSAFE_TRANS(last, nLength, NULL);
1828         ssize_t length = last - first;
1829         if (length <= 0)
1830             return (length == 0) ? "" : NULL;
1831 
1832         // Get codepage
1833         ssize_t cp = codepage_from_name(charset);
1834         if (cp < 0)
1835             return NULL;
1836 
1837         // Estimate number of bytes required
1838         lsp_utf16_t *buf    = const_cast<lsp_utf16_t *>(get_utf16(first, last));
1839         if (buf == NULL)
1840             return NULL;
1841 
1842         // Drop temporary data because it is stored in buf variable and can not be reused
1843         pTemp->pData        = NULL;
1844         pTemp->nLength      = 0;
1845         pTemp->nOffset      = 0;
1846 
1847         size_t slen         = length;
1848         size_t res = widechar_to_multibyte(cp, buf, &slen, NULL, NULL) + 4; // + terminating 0
1849         if ((res <= 0) || (!resize_temp(res)))
1850         {
1851             free(buf);
1852             return NULL;
1853         }
1854 
1855         // We have enough space for saving data
1856         size_t n = res;
1857         res = widechar_to_multibyte(cp, buf, &slen, pTemp->pData, &n);
1858         if (res <= 0)
1859         {
1860             free(buf);
1861             return NULL;
1862         }
1863 
1864         // Append terminating zero
1865         pTemp->pData[res++] = '\0';
1866         pTemp->pData[res++] = '\0';
1867         pTemp->pData[res++] = '\0';
1868         pTemp->pData[res]   = '\0';
1869 
1870         free(buf);
1871         return pTemp->pData;
1872     }
1873 #else
get_native(ssize_t first,ssize_t last,const char * charset) const1874     const char *LSPString::get_native(ssize_t first, ssize_t last, const char *charset) const
1875     {
1876         XSAFE_TRANS(first, nLength, NULL);
1877         XSAFE_TRANS(last, nLength, NULL);
1878         if (first >= last)
1879             return (last == first) ? "" : NULL;
1880 
1881         // Open conversion
1882         iconv_t cd = init_iconv_from_wchar_t(charset);
1883         if (cd == iconv_t(-1))
1884             return get_utf8(first, last);
1885 
1886         // Analyze temp
1887         size_t outsize  = 0;
1888         char *outbuf    = NULL;
1889         if (pTemp != NULL)
1890         {
1891             pTemp->nOffset      = 0;
1892             outsize             = pTemp->nLength;
1893             outbuf              = pTemp->pData;
1894         }
1895 
1896         size_t insize   = (last - first) * sizeof(lsp_wchar_t);
1897         char *inbuf     = reinterpret_cast<char *>(const_cast<lsp_wchar_t *>(&pData[first]));
1898 
1899         while (insize > 0)
1900         {
1901             // Reserve space if there is not enough space
1902             if (outsize < 16)
1903             {
1904                 // Try to grow the temprary buffer
1905                 if (!grow_temp(BUF_SIZE))
1906                 {
1907                     iconv_close(cd);
1908                     return NULL;
1909                 }
1910 
1911                 // Initialize location of buffers to store data
1912                 outsize         = pTemp->nLength - pTemp->nOffset;
1913                 outbuf          = &pTemp->pData[pTemp->nOffset];
1914             }
1915 
1916             // Do the conversion
1917             size_t nconv = iconv(cd, &inbuf, &insize, &outbuf, &outsize);
1918             if (nconv == (size_t) -1)
1919             {
1920                 int err_code = errno;
1921                 switch (err_code)
1922                 {
1923                     case E2BIG:
1924                     case EINVAL:
1925                         break;
1926                     default:
1927                         iconv_close (cd);
1928                         return NULL;
1929                 }
1930             }
1931 
1932             // Update pointer
1933             pTemp->nOffset      = pTemp->nLength - outsize;
1934         }
1935 
1936         // Close the iconv descriptor
1937         iconv_close(cd);
1938 
1939         // Append zeros at the end to make compatible with C-strings
1940         if (!append_temp("\x00\x00\x00\x00", 4))
1941             return NULL;
1942 
1943         return pTemp->pData;
1944     }
1945 #endif /* PLATFORM_WINDOWS */
1946 
match(const LSPString * s,size_t index) const1947     size_t LSPString::match(const LSPString *s, size_t index) const
1948     {
1949         if (index >= nLength)
1950             return 0;
1951         size_t i=0, n = lsp_min(s->nLength, nLength - index);
1952 
1953         for (; i < n; ++i)
1954         {
1955             if (pData[i] != s->pData[i])
1956                 return i;
1957         }
1958         return i;
1959     }
1960 
match_nocase(const LSPString * s,size_t index) const1961     size_t LSPString::match_nocase(const LSPString *s, size_t index) const
1962     {
1963         if (index >= nLength)
1964             return 0;
1965         size_t i=0, n = lsp_min(s->nLength, nLength - index);
1966 
1967         for (; i < n; ++i)
1968         {
1969             if (towlower(pData[i]) != towlower(s->pData[i]))
1970                 return i;
1971         }
1972         return i;
1973     }
1974 
clone_utf8(size_t * bytes,ssize_t first,ssize_t last) const1975     char *LSPString::clone_utf8(size_t *bytes, ssize_t first, ssize_t last) const
1976     {
1977         const char *utf8 = get_utf8(first, last);
1978         size_t offset = (pTemp != NULL) ? pTemp->nOffset : 0;
1979         char *ptr = (utf8 != NULL) ? reinterpret_cast<char *>(lsp_memdup(utf8, offset)) : NULL;
1980         if (bytes != NULL)
1981             *bytes = (ptr != NULL) ? offset : 0;
1982         return ptr;
1983     }
1984 
clone_utf16(size_t * bytes,ssize_t first,ssize_t last) const1985     lsp_utf16_t *LSPString::clone_utf16(size_t *bytes, ssize_t first, ssize_t last) const
1986     {
1987         const lsp_utf16_t *utf16 = get_utf16(first, last);
1988         size_t offset = (pTemp != NULL) ? pTemp->nOffset : 0;
1989         lsp_utf16_t *ptr = (utf16 != NULL) ? reinterpret_cast<lsp_utf16_t *>(lsp_memdup(utf16, offset)) : NULL;
1990         if (bytes != NULL)
1991             *bytes = (ptr != NULL) ? offset : 0;
1992         return ptr;
1993     }
1994 
clone_ascii(size_t * bytes,ssize_t first,ssize_t last) const1995     char *LSPString::clone_ascii(size_t *bytes, ssize_t first, ssize_t last) const
1996     {
1997         const char *ascii = get_ascii(first, last);
1998         size_t offset = (pTemp != NULL) ? pTemp->nOffset : 0;
1999         char *ptr = (ascii != NULL) ? reinterpret_cast<char *>(lsp_memdup(ascii, offset)) : NULL;
2000         if (bytes != NULL)
2001             *bytes = (ptr != NULL) ? offset : 0;
2002         return ptr;
2003     }
2004 
clone_native(size_t * bytes,ssize_t first,ssize_t last,const char * charset) const2005     char *LSPString::clone_native(size_t *bytes, ssize_t first, ssize_t last, const char *charset) const
2006     {
2007         const char *native = get_native(first, last, charset);
2008         size_t offset = (pTemp != NULL) ? pTemp->nOffset : 0;
2009         char *ptr = (native != NULL) ? reinterpret_cast<char *>(lsp_memdup(native, offset)) : NULL;
2010         if (bytes != NULL)
2011             *bytes = (ptr != NULL) ? offset : 0;
2012         return ptr;
2013     }
2014 
append_temp(const char * p,size_t n) const2015     bool LSPString::append_temp(const char *p, size_t n) const
2016     {
2017         ssize_t free = (pTemp != NULL) ? pTemp->nLength - pTemp->nOffset : -1;
2018 
2019         if (free < ssize_t(n))
2020         {
2021             size_t resize   = n + (n >> 1);
2022             if (pTemp != NULL)
2023                 resize         += pTemp->nLength;
2024 
2025             if (!resize_temp(resize))
2026                 return false;
2027         }
2028 
2029         memcpy(&pTemp->pData[pTemp->nOffset], p, n * sizeof(char));
2030         pTemp->nOffset     += n;
2031 
2032         return true;
2033     }
2034 
grow_temp(size_t n) const2035     bool LSPString::grow_temp(size_t n) const
2036     {
2037         if (pTemp == NULL)
2038         {
2039             pTemp = static_cast<buffer_t *>(malloc(sizeof(buffer_t)));
2040             if (pTemp == NULL)
2041                 return false;
2042             pTemp->nLength  = 0;
2043             pTemp->nOffset  = 0;
2044             pTemp->pData    = 0;
2045         }
2046 
2047         char *xc        = static_cast<char *>(realloc(pTemp->pData, (pTemp->nLength + n)*sizeof(char)));
2048         if (xc == NULL)
2049             return false;
2050 
2051         pTemp->pData    = xc;
2052         pTemp->nLength += n;
2053         return true;
2054     }
2055 
resize_temp(size_t n) const2056     bool LSPString::resize_temp(size_t n) const
2057     {
2058         if (pTemp == NULL)
2059         {
2060             pTemp = static_cast<buffer_t *>(malloc(sizeof(buffer_t)));
2061             if (pTemp == NULL)
2062                 return false;
2063             pTemp->nLength  = 0;
2064             pTemp->nOffset  = 0;
2065             pTemp->pData    = 0;
2066         }
2067 
2068         char *xc        = static_cast<char *>(realloc(pTemp->pData, n*sizeof(char)));
2069         if (xc == NULL)
2070             return false;
2071 
2072         pTemp->pData    = xc;
2073         pTemp->nLength  = n;
2074         return true;
2075     }
2076 
count(lsp_wchar_t ch) const2077     size_t LSPString::count(lsp_wchar_t ch) const
2078     {
2079         size_t n = 0;
2080         for (size_t i=0; i<nLength; ++i)
2081             if (pData[i] == ch)
2082                 ++n;
2083         return n;
2084     }
2085 
count(lsp_wchar_t ch,ssize_t first) const2086     size_t LSPString::count(lsp_wchar_t ch, ssize_t first) const
2087     {
2088         XSAFE_TRANS(first, nLength, 0);
2089 
2090         size_t n = 0;
2091         for (size_t i=first; i<nLength; ++i)
2092             if (pData[i] == ch)
2093                 ++n;
2094         return n;
2095     }
2096 
count(lsp_wchar_t ch,ssize_t first,ssize_t last) const2097     size_t LSPString::count(lsp_wchar_t ch, ssize_t first, ssize_t last) const
2098     {
2099         XSAFE_TRANS(first, nLength, 0);
2100         XSAFE_TRANS(last, nLength, 0);
2101 
2102         size_t n = 0;
2103         if (first < last)
2104         {
2105             for (ssize_t i=first; i<last; ++i)
2106                 if (pData[i] == ch)
2107                     ++n;
2108         }
2109         else
2110         {
2111             for (ssize_t i=last; i<first; ++i)
2112                 if (pData[i] == ch)
2113                     ++n;
2114         }
2115         return n;
2116     }
2117 
fmt_append_native(const char * fmt...)2118     bool LSPString::fmt_append_native(const char *fmt...)
2119     {
2120         LSPString tmp;
2121         va_list vl;
2122 
2123         va_start(vl, fmt);
2124         bool res = tmp.vfmt_native(fmt, vl);
2125         va_end(vl);
2126         if (res)
2127             res = append(&tmp);
2128         return res;
2129     }
2130 
fmt_preend_native(const char * fmt...)2131     bool LSPString::fmt_preend_native(const char *fmt...)
2132     {
2133         LSPString tmp;
2134         va_list vl;
2135 
2136         va_start(vl, fmt);
2137         bool res = tmp.vfmt_native(fmt, vl);
2138         va_end(vl);
2139         if (res)
2140             res = prepend(&tmp);
2141         return res;
2142     }
2143 
fmt_native(const char * fmt...)2144     bool LSPString::fmt_native(const char *fmt...)
2145     {
2146         va_list vl;
2147         va_start(vl, fmt);
2148         bool res = vfmt_native(fmt, vl);
2149         va_end(vl);
2150 
2151         return res;
2152     }
2153 
vfmt_append_native(const char * fmt,va_list args)2154     bool LSPString::vfmt_append_native(const char *fmt, va_list args)
2155     {
2156         LSPString tmp;
2157         if (!tmp.vfmt_native(fmt, args))
2158             return false;
2159         return append(&tmp);
2160     }
2161 
vfmt_preend_native(const char * fmt,va_list args)2162     bool LSPString::vfmt_preend_native(const char *fmt, va_list args)
2163     {
2164         LSPString tmp;
2165         if (!tmp.vfmt_native(fmt, args))
2166             return false;
2167         return prepend(&tmp);
2168     }
2169 
vfmt_native(const char * fmt,va_list args)2170     bool LSPString::vfmt_native(const char *fmt, va_list args)
2171     {
2172         char *ptr = NULL;
2173         int count = vasprintf(&ptr, fmt, args);
2174         if (ptr == NULL)
2175             return false;
2176 
2177         bool res = set_native(ptr, count);
2178         free(ptr);
2179         return res;
2180     }
2181 
fmt_append_ascii(const char * fmt...)2182     bool LSPString::fmt_append_ascii(const char *fmt...)
2183     {
2184         LSPString tmp;
2185         va_list vl;
2186 
2187         va_start(vl, fmt);
2188         bool res = tmp.vfmt_ascii(fmt, vl);
2189         va_end(vl);
2190         if (res)
2191             res = append(&tmp);
2192         return res;
2193     }
2194 
fmt_prepend_ascii(const char * fmt...)2195     bool LSPString::fmt_prepend_ascii(const char *fmt...)
2196     {
2197         LSPString tmp;
2198         va_list vl;
2199 
2200         va_start(vl, fmt);
2201         bool res = tmp.vfmt_ascii(fmt, vl);
2202         va_end(vl);
2203         if (res)
2204             res = prepend(&tmp);
2205         return res;
2206     }
2207 
fmt_ascii(const char * fmt...)2208     bool LSPString::fmt_ascii(const char *fmt...)
2209     {
2210         va_list vl;
2211         va_start(vl, fmt);
2212         bool res = vfmt_ascii(fmt, vl);
2213         va_end(vl);
2214 
2215         return res;
2216     }
2217 
vfmt_append_ascii(const char * fmt,va_list args)2218     bool LSPString::vfmt_append_ascii(const char *fmt, va_list args)
2219     {
2220         LSPString tmp;
2221         if (!tmp.vfmt_ascii(fmt, args))
2222             return false;
2223         return append(&tmp);
2224     }
2225 
vfmt_prepend_ascii(const char * fmt,va_list args)2226     bool LSPString::vfmt_prepend_ascii(const char *fmt, va_list args)
2227     {
2228         LSPString tmp;
2229         if (!tmp.vfmt_ascii(fmt, args))
2230             return false;
2231         return prepend(&tmp);
2232     }
2233 
vfmt_ascii(const char * fmt,va_list args)2234     bool LSPString::vfmt_ascii(const char *fmt, va_list args)
2235     {
2236         char *ptr = NULL;
2237         int count = vasprintf(&ptr, fmt, args);
2238         if (ptr == NULL)
2239             return false;
2240 
2241         bool res = set_ascii(ptr, count);
2242         free(ptr);
2243         return res;
2244     }
2245 
fmt_append_utf8(const char * fmt...)2246     bool LSPString::fmt_append_utf8(const char *fmt...)
2247     {
2248         LSPString tmp;
2249         va_list vl;
2250 
2251         va_start(vl, fmt);
2252         bool res = tmp.vfmt_utf8(fmt, vl);
2253         va_end(vl);
2254         if (res)
2255             res = append(&tmp);
2256         return res;
2257     }
2258 
fmt_prepend_utf8(const char * fmt...)2259     bool LSPString::fmt_prepend_utf8(const char *fmt...)
2260     {
2261         LSPString tmp;
2262         va_list vl;
2263 
2264         va_start(vl, fmt);
2265         bool res = tmp.vfmt_utf8(fmt, vl);
2266         va_end(vl);
2267         if (res)
2268             res = prepend(&tmp);
2269         return res;
2270     }
2271 
fmt_utf8(const char * fmt...)2272     bool LSPString::fmt_utf8(const char *fmt...)
2273     {
2274         va_list vl;
2275         va_start(vl, fmt);
2276         bool res = vfmt_utf8(fmt, vl);
2277         va_end(vl);
2278 
2279         return res;
2280     }
2281 
vfmt_append_utf8(const char * fmt,va_list args)2282     bool LSPString::vfmt_append_utf8(const char *fmt, va_list args)
2283     {
2284         LSPString tmp;
2285         if (!tmp.vfmt_utf8(fmt, args))
2286             return false;
2287         return append(&tmp);
2288     }
2289 
vfmt_prepend_utf8(const char * fmt,va_list args)2290     bool LSPString::vfmt_prepend_utf8(const char *fmt, va_list args)
2291     {
2292         LSPString tmp;
2293         if (!tmp.vfmt_utf8(fmt, args))
2294             return false;
2295         return prepend(&tmp);
2296     }
2297 
vfmt_utf8(const char * fmt,va_list args)2298     bool LSPString::vfmt_utf8(const char *fmt, va_list args)
2299     {
2300         char *ptr = NULL;
2301         int count = vasprintf(&ptr, fmt, args);
2302         if (ptr == NULL)
2303             return false;
2304 
2305         bool res = set_utf8(ptr, count);
2306         free(ptr);
2307         return res;
2308     }
2309 } /* namespace lsp */
2310