Move implementation of doubleToString to sal/rtl/strtmpl.hxx
[LibreOffice.git] / sal / rtl / strtmpl.hxx
bloba3c2b44898caed3713eea75c308831242c0ba0d6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #pragma once
22 #include <algorithm>
23 #include <cassert>
24 #include <cmath>
25 #include <cstdlib>
26 #include <cstring>
27 #include <cwchar>
28 #include <limits>
29 #include <limits>
30 #include <string_view>
31 #include <type_traits>
32 #include <utility>
34 #include "strimp.hxx"
36 #include <o3tl/safeint.hxx>
37 #include <osl/diagnose.h>
38 #include <sal/log.hxx>
39 #include <rtl/character.hxx>
40 #include <rtl/math.h>
41 #include <rtl/string.h>
42 #include <rtl/ustring.h>
44 #include <dragonbox/dragonbox.h>
46 void internRelease(rtl_uString*);
48 namespace rtl::str
50 template <typename C> auto IMPL_RTL_USTRCODE(C c) { return std::make_unsigned_t<C>(c); }
52 // Wrappers around null-terminated/known-length strings, that allow to generalize algorithms
53 // without overhead (e.g., without need to get length of null-terminated strings).
55 template <typename C> struct null_terminated
57 C* p;
58 null_terminated(C* pStr)
59 : p(pStr)
61 assert(pStr);
63 auto begin() const { return p; }
64 struct EndDetector
66 friend bool operator==(EndDetector, C* iter) { return *iter == 0; }
67 friend bool operator==(C* iter, EndDetector) { return *iter == 0; }
68 friend bool operator!=(EndDetector, C* iter) { return *iter != 0; }
69 friend bool operator!=(C* iter, EndDetector) { return *iter != 0; }
71 static auto end() { return EndDetector{}; }
74 template <typename C> struct with_length
76 C* p;
77 sal_Int32 len;
78 with_length(C* pStr, sal_Int32 nLength)
79 : p(pStr)
80 , len(nLength)
82 assert(len >= 0);
84 auto begin() const { return p; }
85 auto end() const { return p + len; }
88 struct ToAsciiLower
90 template <typename C> static bool Applicable(C c)
92 return rtl::isAsciiUpperCase(IMPL_RTL_USTRCODE(c));
94 template <typename C> static C Replace(C c)
96 return rtl::toAsciiLowerCase(IMPL_RTL_USTRCODE(c));
98 } constexpr toAsciiLower;
100 struct ToAsciiUpper
102 template <typename C> static bool Applicable(C c)
104 return rtl::isAsciiLowerCase(IMPL_RTL_USTRCODE(c));
106 template <typename C> static C Replace(C c)
108 return rtl::toAsciiUpperCase(IMPL_RTL_USTRCODE(c));
110 } constexpr toAsciiUpper;
112 template <typename C> struct FromTo
114 C from;
115 C to;
116 FromTo(C cFrom, C cTo) : from(cFrom), to(cTo) {}
117 C Replace(C c) const { return c == from ? to : c; }
120 template <typename C> void Copy(C* _pDest, const C* _pSrc, sal_Int32 _nCount)
122 // take advantage of builtin optimisations
123 std::copy(_pSrc, _pSrc + _nCount, _pDest);
126 template <typename C> void CopyBackward(C* _pDest, const C* _pSrc, sal_Int32 _nCount)
128 // take advantage of builtin optimisations
129 std::copy_backward(_pSrc, _pSrc + _nCount, _pDest + _nCount);
132 inline void Copy(sal_Unicode* _pDest, const char* _pSrc, sal_Int32 _nCount)
134 std::transform(_pSrc, _pSrc + _nCount, _pDest,
135 [](char c)
137 assert(rtl::isAscii(static_cast<unsigned char>(c)));
138 SAL_WARN_IF(c == '\0', "rtl.string", "Found embedded \\0 ASCII character");
139 return static_cast<unsigned char>(c);
143 /* ======================================================================= */
144 /* C-String functions which could be used without the String-Class */
145 /* ======================================================================= */
147 template <typename T> sal_Int32 getLength( const T* pStr )
149 assert(pStr);
150 if constexpr (std::is_class_v<T>)
152 return pStr->length;
154 else
156 // take advantage of builtin optimisations
157 return std::char_traits<T>::length(pStr);
161 /* ----------------------------------------------------------------------- */
163 template <typename C> void warnIfCharAndNotAscii(C c)
165 if constexpr (sizeof(c) == sizeof(char))
166 SAL_WARN_IF(!rtl::isAscii(static_cast<unsigned char>(c)), "rtl.string",
167 "Found non-ASCII char");
170 template <typename C1, typename C2> void warnIfOneIsCharAndNotAscii(C1 c1, C2 c2)
172 if constexpr (sizeof(c1) != sizeof(c2))
174 warnIfCharAndNotAscii(c1);
175 warnIfCharAndNotAscii(c2);
179 struct CompareNormal
181 template <typename C1, typename C2> static sal_Int32 compare(C1 c1, C2 c2)
183 warnIfOneIsCharAndNotAscii(c1, c2);
184 return static_cast<sal_Int32>(IMPL_RTL_USTRCODE(c1))
185 - static_cast<sal_Int32>(IMPL_RTL_USTRCODE(c2));
189 struct CompareIgnoreAsciiCase
191 template <typename C1, typename C2> static sal_Int32 compare(C1 c1, C2 c2)
193 warnIfOneIsCharAndNotAscii(c1, c2);
194 return rtl::compareIgnoreAsciiCase(IMPL_RTL_USTRCODE(c1), IMPL_RTL_USTRCODE(c2));
198 /* ----------------------------------------------------------------------- */
200 struct NoShortening
202 constexpr bool operator>=(int) { return true; } // for assert
203 constexpr bool operator==(int) { return false; } // for loop break check
204 constexpr void operator--() {} // for decrement in loop
205 } constexpr noShortening;
207 template <class S1, class S2, class Compare, typename Shorten_t>
208 sal_Int32 compare(S1 s1, S2 s2, Compare, Shorten_t shortenedLength)
210 static_assert(std::is_same_v<Shorten_t, NoShortening> || std::is_same_v<Shorten_t, sal_Int32>);
211 assert(shortenedLength >= 0);
212 auto pStr1 = s1.begin();
213 const auto end1 = s1.end();
214 auto pStr2 = s2.begin();
215 const auto end2 = s2.end();
216 for (;;)
218 if (shortenedLength == 0)
219 return 0;
220 if (pStr2 == end2)
221 return pStr1 == end1 ? 0 : 1;
222 if (pStr1 == end1)
223 return -1;
224 if (const sal_Int32 nRet = Compare::compare(*pStr1, *pStr2))
225 return nRet;
226 --shortenedLength;
227 ++pStr1;
228 ++pStr2;
232 // take advantage of builtin optimisations
233 template <typename C, std::enable_if_t<sizeof(C) == sizeof(wchar_t), int> = 0>
234 sal_Int32 compare(null_terminated<C> s1, null_terminated<C> s2, CompareNormal, NoShortening)
236 return wcscmp(reinterpret_cast<wchar_t const*>(s1.p), reinterpret_cast<wchar_t const*>(s2.p));
238 template <typename C, std::enable_if_t<sizeof(C) == sizeof(char), int> = 0>
239 sal_Int32 compare(null_terminated<C> s1, null_terminated<C> s2, CompareNormal, NoShortening)
241 return strcmp(reinterpret_cast<char const*>(s1.p), reinterpret_cast<char const*>(s2.p));
243 template <typename C>
244 sal_Int32 compare(with_length<C> s1, with_length<C> s2, CompareNormal, NoShortening)
246 std::basic_string_view sv1(s1.p, s1.len);
247 return sv1.compare(std::basic_string_view(s2.p, s2.len));
249 template <typename C1, typename C2, class Compare>
250 sal_Int32 compare(with_length<C1> s1, with_length<C2> s2, Compare cf, sal_Int32 nShortenedLength)
252 assert(nShortenedLength >= 0);
253 s1.len = std::min(s1.len, nShortenedLength);
254 s2.len = std::min(s2.len, nShortenedLength);
255 return compare(s1, s2, cf, noShortening);
258 /* ----------------------------------------------------------------------- */
260 template <typename C1, typename C2, class Compare>
261 sal_Int32 reverseCompare_WithLengths(const C1* pStr1, sal_Int32 nStr1Len,
262 const C2* pStr2, sal_Int32 nStr2Len, Compare)
264 assert(pStr1 || nStr1Len == 0);
265 assert(nStr1Len >= 0);
266 assert(pStr2 || nStr2Len == 0);
267 assert(nStr2Len >= 0);
268 const C1* pStr1Run = pStr1+nStr1Len;
269 const C2* pStr2Run = pStr2+nStr2Len;
270 while ((pStr1 < pStr1Run) && (pStr2 < pStr2Run))
272 pStr1Run--;
273 pStr2Run--;
274 if (const sal_Int32 nRet = Compare::compare(*pStr1Run, *pStr2Run))
275 return nRet;
278 return nStr1Len - nStr2Len;
281 /* ----------------------------------------------------------------------- */
283 template <typename IMPL_RTL_STRCODE>
284 sal_Int32 hashCode_WithLength(const IMPL_RTL_STRCODE*, sal_Int32);
286 template <typename IMPL_RTL_STRCODE> sal_Int32 hashCode( const IMPL_RTL_STRCODE* pStr )
288 return hashCode_WithLength( pStr, getLength( pStr ) );
291 /* ----------------------------------------------------------------------- */
293 template <typename IMPL_RTL_STRCODE>
294 sal_Int32 hashCode_WithLength ( const IMPL_RTL_STRCODE* pStr,
295 sal_Int32 nLen )
297 assert(nLen >= 0);
298 sal_uInt32 h = static_cast<sal_uInt32>(nLen);
299 while ( nLen > 0 )
301 h = (h*37U) + IMPL_RTL_USTRCODE( *pStr );
302 pStr++;
303 nLen--;
305 return static_cast<sal_Int32>(h);
308 /* ----------------------------------------------------------------------- */
310 template <typename IMPL_RTL_STRCODE>
311 sal_Int32 indexOfChar ( const IMPL_RTL_STRCODE* pStr,
312 IMPL_RTL_STRCODE c )
314 assert(pStr);
315 if (!c)
316 return -1; // Unifies behavior of strchr/wcschr and unoptimized algorithm wrt '\0'
318 if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(char))
320 // take advantage of builtin optimisations
321 const IMPL_RTL_STRCODE* p = strchr(pStr, c);
322 return p ? p - pStr : -1;
324 else if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(wchar_t))
326 // take advantage of builtin optimisations
327 wchar_t const * p = wcschr(reinterpret_cast<wchar_t const *>(pStr), static_cast<wchar_t>(c));
328 return p ? p - reinterpret_cast<wchar_t const *>(pStr) : -1;
330 else
332 const IMPL_RTL_STRCODE* pTempStr = pStr;
333 while ( *pTempStr )
335 if ( *pTempStr == c )
336 return pTempStr-pStr;
338 pTempStr++;
341 return -1;
345 /* ----------------------------------------------------------------------- */
347 template <typename IMPL_RTL_STRCODE>
348 sal_Int32 indexOfChar_WithLength ( const IMPL_RTL_STRCODE* pStr,
349 sal_Int32 nLen,
350 IMPL_RTL_STRCODE c )
352 // assert(nLen >= 0);
353 if (nLen <= 0)
354 return -1;
355 // take advantage of builtin optimisations
356 using my_string_view = std::basic_string_view<IMPL_RTL_STRCODE>;
357 my_string_view v(pStr, nLen);
358 typename my_string_view::size_type idx = v.find(c);
359 return idx == my_string_view::npos ? -1 : idx;
362 /* ----------------------------------------------------------------------- */
364 template <typename IMPL_RTL_STRCODE>
365 sal_Int32 lastIndexOfChar_WithLength(const IMPL_RTL_STRCODE*, sal_Int32, IMPL_RTL_STRCODE);
367 template <typename IMPL_RTL_STRCODE>
368 sal_Int32 lastIndexOfChar ( const IMPL_RTL_STRCODE* pStr,
369 IMPL_RTL_STRCODE c )
371 assert(pStr);
372 if (!c)
373 return -1; // Unifies behavior of strrchr/wcsrchr and lastIndexOfChar_WithLength wrt '\0'
375 if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(char))
377 // take advantage of builtin optimisations
378 const IMPL_RTL_STRCODE* p = strrchr(pStr, c);
379 return p ? p - pStr : -1;
381 else if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(wchar_t))
383 // take advantage of builtin optimisations
384 wchar_t const * p = wcsrchr(reinterpret_cast<wchar_t const *>(pStr), static_cast<wchar_t>(c));
385 return p ? p - reinterpret_cast<wchar_t const *>(pStr) : -1;
387 else
389 return lastIndexOfChar_WithLength( pStr, getLength( pStr ), c );
393 /* ----------------------------------------------------------------------- */
395 template <typename IMPL_RTL_STRCODE>
396 sal_Int32 lastIndexOfChar_WithLength ( const IMPL_RTL_STRCODE* pStr,
397 sal_Int32 nLen,
398 IMPL_RTL_STRCODE c )
400 assert(nLen >= 0);
401 // take advantage of builtin optimisations
402 using my_string_view = std::basic_string_view<IMPL_RTL_STRCODE>;
403 my_string_view v(pStr, nLen);
404 typename my_string_view::size_type idx = v.rfind(c);
405 return idx == my_string_view::npos ? -1 : idx;
408 /* ----------------------------------------------------------------------- */
410 template <typename IMPL_RTL_STRCODE>
411 sal_Int32 indexOfStr_WithLength(const IMPL_RTL_STRCODE*, sal_Int32, const IMPL_RTL_STRCODE*,
412 sal_Int32);
414 template <typename IMPL_RTL_STRCODE>
415 sal_Int32 indexOfStr ( const IMPL_RTL_STRCODE* pStr,
416 const IMPL_RTL_STRCODE* pSubStr )
418 assert(pStr);
419 assert(pSubStr);
420 /* an empty SubString is always not findable */
421 if (*pSubStr == 0)
422 return -1;
423 if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(char))
425 // take advantage of builtin optimisations
426 const IMPL_RTL_STRCODE* p = strstr(pStr, pSubStr);
427 return p ? p - pStr : -1;
429 else if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(wchar_t))
431 // take advantage of builtin optimisations
432 wchar_t const * p = wcsstr(reinterpret_cast<wchar_t const *>(pStr), reinterpret_cast<wchar_t const *>(pSubStr));
433 return p ? p - reinterpret_cast<wchar_t const *>(pStr) : -1;
435 else
437 return indexOfStr_WithLength( pStr, getLength( pStr ),
438 pSubStr, getLength( pSubStr ) );
442 /* ----------------------------------------------------------------------- */
444 template <typename IMPL_RTL_STRCODE>
445 sal_Int32 indexOfStr_WithLength ( const IMPL_RTL_STRCODE* pStr,
446 sal_Int32 nStrLen,
447 const IMPL_RTL_STRCODE* pSubStr,
448 sal_Int32 nSubLen )
450 assert(nStrLen >= 0);
451 assert(nSubLen >= 0);
452 /* an empty SubString is always not findable */
453 if ( nSubLen == 0 )
454 return -1;
455 // take advantage of builtin optimisations
456 using my_string_view = std::basic_string_view<IMPL_RTL_STRCODE>;
457 my_string_view v(pStr, nStrLen);
458 auto idx = nSubLen == 1 ? v.find(*pSubStr) : v.find(pSubStr, 0, nSubLen);
459 return idx == my_string_view::npos ? -1 : idx;
462 inline sal_Int32 indexOfStr_WithLength(const sal_Unicode* pStr, sal_Int32 nStrLen,
463 const char* pSubStr, sal_Int32 nSubLen)
465 assert(nStrLen >= 0);
466 assert(nSubLen >= 0);
467 if (nSubLen > 0 && nSubLen <= nStrLen)
469 sal_Unicode const* end = pStr + nStrLen;
470 sal_Unicode const* cursor = pStr;
472 while (cursor < end)
474 cursor = std::char_traits<sal_Unicode>::find(cursor, end - cursor, *pSubStr);
475 if (!cursor || (end - cursor < nSubLen))
477 /* no enough left to actually have a match */
478 break;
480 /* now it is worth trying a full match */
481 if (nSubLen == 1 || rtl_ustr_asciil_reverseEquals_WithLength(cursor, pSubStr, nSubLen))
483 return cursor - pStr;
485 cursor += 1;
488 return -1;
491 /* ----------------------------------------------------------------------- */
493 template <typename IMPL_RTL_STRCODE>
494 sal_Int32 lastIndexOfStr_WithLength(const IMPL_RTL_STRCODE*, sal_Int32, const IMPL_RTL_STRCODE*,
495 sal_Int32);
497 template <typename IMPL_RTL_STRCODE>
498 sal_Int32 lastIndexOfStr ( const IMPL_RTL_STRCODE* pStr,
499 const IMPL_RTL_STRCODE* pSubStr )
501 return lastIndexOfStr_WithLength( pStr, getLength( pStr ),
502 pSubStr, getLength( pSubStr ) );
505 /* ----------------------------------------------------------------------- */
507 template <typename IMPL_RTL_STRCODE>
508 sal_Int32 lastIndexOfStr_WithLength ( const IMPL_RTL_STRCODE* pStr,
509 sal_Int32 nStrLen,
510 const IMPL_RTL_STRCODE* pSubStr,
511 sal_Int32 nSubLen )
513 assert(nStrLen >= 0);
514 assert(nSubLen >= 0);
515 /* an empty SubString is always not findable */
516 if ( nSubLen == 0 )
517 return -1;
518 // take advantage of builtin optimisations
519 using my_string_view = std::basic_string_view<IMPL_RTL_STRCODE>;
520 my_string_view v(pStr, nStrLen);
521 my_string_view needle(pSubStr, nSubLen);
522 typename my_string_view::size_type idx = v.rfind(needle);
523 return idx == my_string_view::npos ? -1 : idx;
526 /* ----------------------------------------------------------------------- */
528 template <class S, class Replacer> void replaceChars(S str, Replacer replacer)
530 for (auto& rChar : str)
531 rChar = replacer.Replace(rChar);
534 /* ----------------------------------------------------------------------- */
536 template <typename IMPL_RTL_STRCODE> sal_Int32 trim_WithLength(IMPL_RTL_STRCODE*, sal_Int32);
538 template <typename IMPL_RTL_STRCODE> sal_Int32 trim( IMPL_RTL_STRCODE* pStr )
540 return trim_WithLength( pStr, getLength( pStr ) );
543 /* ----------------------------------------------------------------------- */
545 template <typename IMPL_RTL_STRCODE>
546 std::basic_string_view<IMPL_RTL_STRCODE> trimView( IMPL_RTL_STRCODE* pStr, sal_Int32 nLen )
548 assert(nLen >= 0);
549 sal_Int32 nPreSpaces = 0;
550 sal_Int32 nPostSpaces = 0;
551 sal_Int32 nIndex = nLen-1;
553 while ( (nPreSpaces < nLen) && rtl_ImplIsWhitespace( IMPL_RTL_USTRCODE(*(pStr+nPreSpaces)) ) )
554 nPreSpaces++;
556 while ( (nIndex > nPreSpaces) && rtl_ImplIsWhitespace( IMPL_RTL_USTRCODE(*(pStr+nIndex)) ) )
558 nPostSpaces++;
559 nIndex--;
562 return { pStr + nPreSpaces, static_cast<size_t>(nLen - nPostSpaces - nPreSpaces) };
565 /* ----------------------------------------------------------------------- */
567 template <typename IMPL_RTL_STRCODE>
568 sal_Int32 trim_WithLength( IMPL_RTL_STRCODE* pStr, sal_Int32 nLen )
570 const auto view = trimView(pStr, nLen);
572 if (static_cast<sal_Int32>(view.size()) != nLen)
574 nLen = static_cast<sal_Int32>(view.size());
575 if (view.data() != pStr)
576 Copy(pStr, view.data(), nLen);
577 *(pStr+nLen) = 0;
580 return nLen;
583 /* ----------------------------------------------------------------------- */
585 template <typename IMPL_RTL_STRCODE> sal_Int32 valueOfBoolean( IMPL_RTL_STRCODE* pStr, sal_Bool b )
587 assert(pStr);
588 if ( b )
590 *pStr = 't';
591 pStr++;
592 *pStr = 'r';
593 pStr++;
594 *pStr = 'u';
595 pStr++;
596 *pStr = 'e';
597 pStr++;
598 *pStr = 0;
599 return 4;
601 else
603 *pStr = 'f';
604 pStr++;
605 *pStr = 'a';
606 pStr++;
607 *pStr = 'l';
608 pStr++;
609 *pStr = 's';
610 pStr++;
611 *pStr = 'e';
612 pStr++;
613 *pStr = 0;
614 return 5;
618 /* ----------------------------------------------------------------------- */
620 template <typename IMPL_RTL_STRCODE>
621 sal_Int32 valueOfChar ( IMPL_RTL_STRCODE* pStr,
622 IMPL_RTL_STRCODE c )
624 assert(pStr);
625 *pStr++ = c;
626 *pStr = 0;
627 return 1;
630 /* ----------------------------------------------------------------------- */
632 template <sal_Int32 maxLen, typename IMPL_RTL_STRCODE, typename T>
633 sal_Int32 valueOfInt ( IMPL_RTL_STRCODE* pStr,
634 T n,
635 sal_Int16 nRadix )
637 assert(pStr);
638 assert( nRadix >= RTL_STR_MIN_RADIX && nRadix <= RTL_STR_MAX_RADIX );
639 char aBuf[maxLen];
640 char* pBuf = aBuf;
641 sal_Int32 nLen = 0;
642 using uT = std::make_unsigned_t<T>;
643 uT nValue;
645 /* Radix must be valid */
646 if ( (nRadix < RTL_STR_MIN_RADIX) || (nRadix > RTL_STR_MAX_RADIX) )
647 nRadix = 10;
649 if constexpr (std::is_signed_v<T>)
651 /* is value negative */
652 if ( n < 0 )
654 *pStr = '-';
655 pStr++;
656 nLen++;
657 nValue = n == std::numeric_limits<T>::min() ? static_cast<uT>(n) : -n;
659 else
660 nValue = n;
662 else
663 nValue = n;
665 /* create a recursive buffer with all values, except the last one */
668 char nDigit = static_cast<char>(nValue % nRadix);
669 nValue /= nRadix;
670 if ( nDigit > 9 )
671 *pBuf = (nDigit-10) + 'a';
672 else
673 *pBuf = (nDigit + '0' );
674 pBuf++;
676 while ( nValue > 0 );
678 /* copy the values in the right direction into the destination buffer */
681 pBuf--;
682 *pStr = *pBuf;
683 pStr++;
684 nLen++;
686 while ( pBuf != aBuf );
687 *pStr = 0;
689 return nLen;
692 /* ----------------------------------------------------------------------- */
694 template <typename IMPL_RTL_STRCODE> sal_Bool toBoolean( const IMPL_RTL_STRCODE* pStr )
696 assert(pStr);
697 if ( *pStr == '1' )
698 return true;
700 if ( (*pStr == 'T') || (*pStr == 't') )
702 pStr++;
703 if ( (*pStr == 'R') || (*pStr == 'r') )
705 pStr++;
706 if ( (*pStr == 'U') || (*pStr == 'u') )
708 pStr++;
709 if ( (*pStr == 'E') || (*pStr == 'e') )
710 return true;
715 return false;
718 /* ----------------------------------------------------------------------- */
720 template <typename T, class Iter> inline bool HandleSignChar(Iter& iter)
722 if constexpr (std::numeric_limits<T>::is_signed)
724 if (*iter == '-')
726 ++iter;
727 return true;
730 if (*iter == '+')
731 ++iter;
732 return false;
735 template <typename T> std::pair<T, sal_Int16> DivMod(sal_Int16 nRadix, [[maybe_unused]] bool bNeg)
737 if constexpr (std::numeric_limits<T>::is_signed)
738 if (bNeg)
739 return { -(std::numeric_limits<T>::min() / nRadix),
740 -(std::numeric_limits<T>::min() % nRadix) };
741 return { std::numeric_limits<T>::max() / nRadix, std::numeric_limits<T>::max() % nRadix };
744 template <typename T, class S> T toInt(S str, sal_Int16 nRadix)
746 assert( nRadix >= RTL_STR_MIN_RADIX && nRadix <= RTL_STR_MAX_RADIX );
748 if ( (nRadix < RTL_STR_MIN_RADIX) || (nRadix > RTL_STR_MAX_RADIX) )
749 nRadix = 10;
751 auto pStr = str.begin();
752 const auto end = str.end();
754 /* Skip whitespaces */
755 while (pStr != end && rtl_ImplIsWhitespace(IMPL_RTL_USTRCODE(*pStr)))
756 pStr++;
757 if (pStr == end)
758 return 0;
760 const bool bNeg = HandleSignChar<T>(pStr);
761 const auto& [nDiv, nMod] = DivMod<T>(nRadix, bNeg);
762 assert(nDiv > 0);
764 std::make_unsigned_t<T> n = 0;
765 while (pStr != end)
767 sal_Int16 nDigit = rtl_ImplGetDigit(IMPL_RTL_USTRCODE(*pStr), nRadix);
768 if ( nDigit < 0 )
769 break;
770 if (static_cast<std::make_unsigned_t<T>>(nMod < nDigit ? nDiv - 1 : nDiv) < n)
771 return 0;
773 n *= nRadix;
774 n += nDigit;
776 pStr++;
779 if constexpr (std::numeric_limits<T>::is_signed)
780 if (bNeg)
781 return n == static_cast<std::make_unsigned_t<T>>(std::numeric_limits<T>::min())
782 ? std::numeric_limits<T>::min()
783 : -static_cast<T>(n);
784 return static_cast<T>(n);
787 /* ======================================================================= */
788 /* Internal String-Class help functions */
789 /* ======================================================================= */
791 template <class STRINGDATA> using STRCODE = std::remove_extent_t<decltype(STRINGDATA::buffer)>;
792 template <typename C> struct STRINGDATA_;
793 template <> struct STRINGDATA_<char>
795 using T = rtl_String;
797 template <> struct STRINGDATA_<sal_Unicode>
799 using T = rtl_uString;
801 template <typename C> using STRINGDATA = typename STRINGDATA_<C>::T;
803 template <typename IMPL_RTL_STRINGDATA> IMPL_RTL_STRINGDATA* Alloc( sal_Int32 nLen )
805 IMPL_RTL_STRINGDATA * pData
806 = (sal::static_int_cast< sal_uInt32 >(nLen)
807 <= ((SAL_MAX_UINT32 - sizeof (IMPL_RTL_STRINGDATA))
808 / sizeof (STRCODE<IMPL_RTL_STRINGDATA>)))
809 ? static_cast<IMPL_RTL_STRINGDATA *>(rtl_allocateString(
810 sizeof (IMPL_RTL_STRINGDATA) + nLen * sizeof (STRCODE<IMPL_RTL_STRINGDATA>)))
811 : nullptr;
812 if (pData != nullptr) {
813 pData->refCount = 1;
814 pData->length = nLen;
815 pData->buffer[nLen] = 0;
817 return pData;
820 /* ======================================================================= */
821 /* String-Class functions */
822 /* ======================================================================= */
824 template <typename IMPL_RTL_STRINGDATA> void acquire(IMPL_RTL_STRINGDATA * pThis)
826 if (!SAL_STRING_IS_STATIC (pThis))
827 osl_atomic_increment( &((pThis)->refCount) );
830 /* ----------------------------------------------------------------------- */
832 template <typename IMPL_RTL_STRINGDATA> void release( IMPL_RTL_STRINGDATA* pThis )
834 if (SAL_UNLIKELY(SAL_STRING_IS_STATIC (pThis)))
835 return;
837 /* OString doesn't have an 'intern' */
838 if constexpr (sizeof(STRCODE<IMPL_RTL_STRINGDATA>) == sizeof(sal_Unicode))
840 if (SAL_STRING_IS_INTERN (pThis))
842 internRelease (pThis);
843 return;
847 if ( !osl_atomic_decrement( &(pThis->refCount) ) )
849 RTL_LOG_STRING_DELETE( pThis );
850 rtl_freeString( pThis );
854 /* ----------------------------------------------------------------------- */
856 /* static data to be referenced by all empty strings
857 * the refCount is predefined to 1 and must never become 0 !
859 template <typename IMPL_RTL_STRINGDATA> struct EmptyStringImpl
861 static IMPL_RTL_STRINGDATA data;
864 template <>
865 inline rtl_uString EmptyStringImpl<rtl_uString>::data = {
866 sal_Int32(SAL_STRING_INTERN_FLAG | SAL_STRING_STATIC_FLAG | 1), /* sal_Int32 refCount; */
867 0, /* sal_Int32 length; */
868 { 0 } /* sal_Unicode buffer[1]; */
871 template <>
872 inline rtl_String EmptyStringImpl<rtl_String>::data = {
873 SAL_STRING_STATIC_FLAG | 1, /* sal_Int32 refCount; */
874 0, /* sal_Int32 length; */
875 { 0 } /* char buffer[1]; */
878 template <typename IMPL_RTL_STRINGDATA> void new_( IMPL_RTL_STRINGDATA** ppThis )
880 assert(ppThis);
881 if ( *ppThis)
882 release( *ppThis );
884 *ppThis = &EmptyStringImpl<IMPL_RTL_STRINGDATA>::data;
887 /* ----------------------------------------------------------------------- */
889 template <typename IMPL_RTL_STRINGDATA>
890 void new_WithLength( IMPL_RTL_STRINGDATA** ppThis, sal_Int32 nLen )
892 assert(ppThis);
893 assert(nLen >= 0);
894 if ( nLen <= 0 )
895 new_( ppThis );
896 else
898 if ( *ppThis)
899 release( *ppThis );
901 *ppThis = Alloc<IMPL_RTL_STRINGDATA>( nLen );
902 assert(*ppThis != nullptr);
903 (*ppThis)->length = 0;
904 (*ppThis)->buffer[0] = 0;
908 /* ----------------------------------------------------------------------- */
910 template <typename IMPL_RTL_STRINGDATA, typename C>
911 void newFromStr_WithLength(IMPL_RTL_STRINGDATA** ppThis, const C* pCharStr, sal_Int32 nLen,
912 sal_Int32 allocExtra = 0)
914 assert(ppThis);
915 assert(nLen >= 0);
916 assert(pCharStr || nLen == 0);
917 assert(allocExtra >= 0);
919 if (nLen + allocExtra == 0)
920 return new_(ppThis);
922 IMPL_RTL_STRINGDATA* pOrg = *ppThis;
923 *ppThis = Alloc<IMPL_RTL_STRINGDATA>(nLen + allocExtra);
924 assert(*ppThis != nullptr);
925 if (nLen > 0)
926 Copy((*ppThis)->buffer, pCharStr, nLen);
927 if (allocExtra > 0)
929 (*ppThis)->length = nLen;
930 (*ppThis)->buffer[nLen] = 0;
933 RTL_LOG_STRING_NEW(*ppThis);
935 /* must be done last, if pCharStr belongs to *ppThis */
936 if (pOrg)
937 release(pOrg);
940 template <typename IMPL_RTL_STRINGDATA>
941 void newFromString ( IMPL_RTL_STRINGDATA** ppThis,
942 const IMPL_RTL_STRINGDATA* pStr )
944 assert(pStr);
946 newFromStr_WithLength(ppThis, pStr->buffer, pStr->length);
949 /* ----------------------------------------------------------------------- */
951 template <typename IMPL_RTL_STRINGDATA>
952 void newFromStr ( IMPL_RTL_STRINGDATA** ppThis,
953 const STRCODE<IMPL_RTL_STRINGDATA>* pCharStr )
955 #if OSL_DEBUG_LEVEL > 0
956 //TODO: For now, only abort in non-production debug builds; once all places that rely on the
957 // undocumented newFromStr behavior of treating a null pCharStr like an empty string have been
958 // found and fixed, drop support for that behavior and turn this into a general assert:
959 if (pCharStr == nullptr) {
960 std::abort();
962 #endif
964 newFromStr_WithLength(ppThis, pCharStr, pCharStr ? getLength(pCharStr) : 0);
967 /* ----------------------------------------------------------------------- */
969 template <typename IMPL_RTL_STRINGDATA> void assign(IMPL_RTL_STRINGDATA**, IMPL_RTL_STRINGDATA*);
971 template <typename IMPL_RTL_STRINGDATA>
972 void newFromSubString ( IMPL_RTL_STRINGDATA** ppThis,
973 const IMPL_RTL_STRINGDATA* pFrom,
974 sal_Int32 beginIndex,
975 sal_Int32 count )
977 assert(ppThis);
978 if ( beginIndex == 0 && count == pFrom->length )
979 return assign(ppThis, const_cast<IMPL_RTL_STRINGDATA*>(pFrom));
980 if ( count < 0 || beginIndex < 0 || beginIndex + count > pFrom->length )
982 assert(false); // fail fast at least in debug builds
983 return newFromStr_WithLength(ppThis, "!!br0ken!!", 10);
986 newFromStr_WithLength( ppThis, pFrom->buffer + beginIndex, count );
989 /* ----------------------------------------------------------------------- */
991 template <typename IMPL_RTL_STRINGDATA>
992 void assign ( IMPL_RTL_STRINGDATA** ppThis,
993 IMPL_RTL_STRINGDATA* pStr )
995 assert(ppThis);
996 /* must be done at first, if pStr == *ppThis */
997 acquire( pStr );
999 if ( *ppThis )
1000 release( *ppThis );
1002 *ppThis = pStr;
1005 /* ----------------------------------------------------------------------- */
1007 template <typename IMPL_RTL_STRINGDATA> auto* getStr( IMPL_RTL_STRINGDATA* pThis )
1009 assert(pThis);
1010 return pThis->buffer;
1013 /* ----------------------------------------------------------------------- */
1015 enum ThrowPolicy { NoThrow, Throw };
1017 template <ThrowPolicy throwPolicy, typename IMPL_RTL_STRINGDATA, typename C1, typename C2>
1018 void newConcat(IMPL_RTL_STRINGDATA** ppThis, const C1* pLeft, sal_Int32 nLeftLength,
1019 const C2* pRight, sal_Int32 nRightLength)
1021 assert(ppThis);
1022 assert(nLeftLength >= 0);
1023 assert(pLeft || nLeftLength == 0);
1024 assert(nRightLength >= 0);
1025 assert(pRight || nRightLength == 0);
1026 IMPL_RTL_STRINGDATA* pOrg = *ppThis;
1028 if (nLeftLength > std::numeric_limits<sal_Int32>::max() - nRightLength)
1030 if constexpr (throwPolicy == NoThrow)
1031 *ppThis = nullptr;
1032 else
1034 #if !defined(__COVERITY__)
1035 throw std::length_error("newConcat");
1036 #else
1037 //coverity doesn't report std::bad_alloc as an unhandled exception when
1038 //potentially thrown from destructors but does report std::length_error
1039 throw std::bad_alloc();
1040 #endif
1043 else
1045 auto* pTempStr = Alloc<IMPL_RTL_STRINGDATA>(nLeftLength + nRightLength);
1046 OSL_ASSERT(pTempStr != nullptr);
1047 *ppThis = pTempStr;
1048 if (*ppThis != nullptr) {
1049 if (nLeftLength)
1050 Copy( pTempStr->buffer, pLeft, nLeftLength );
1051 if (nRightLength)
1052 Copy( pTempStr->buffer+nLeftLength, pRight, nRightLength );
1054 RTL_LOG_STRING_NEW( *ppThis );
1058 /* must be done last, if left or right == *ppThis */
1059 if ( pOrg )
1060 release( pOrg );
1063 template<typename IMPL_RTL_STRINGDATA, typename IMPL_RTL_STRCODE>
1064 void newConcat(IMPL_RTL_STRINGDATA** ppThis, IMPL_RTL_STRINGDATA* pLeft,
1065 const IMPL_RTL_STRCODE* pRight, sal_Int32 nRightLength)
1067 assert(pLeft != nullptr);
1068 if (nRightLength == 0)
1069 assign(ppThis, pLeft);
1070 else
1071 newConcat<Throw>(ppThis, pLeft->buffer, pLeft->length, pRight, nRightLength);
1074 template <typename IMPL_RTL_STRINGDATA>
1075 void newConcat ( IMPL_RTL_STRINGDATA** ppThis,
1076 IMPL_RTL_STRINGDATA* pLeft,
1077 IMPL_RTL_STRINGDATA* pRight )
1079 /* Test for 0-Pointer - if not, change newReplaceStrAt! */
1080 if ( !pRight || !pRight->length )
1082 assert(pLeft != nullptr);
1083 assign(ppThis, pLeft);
1085 else if ( !pLeft || !pLeft->length )
1086 assign(ppThis, pRight);
1087 else
1088 newConcat<NoThrow>(ppThis, pLeft->buffer, pLeft->length, pRight->buffer, pRight->length);
1091 /* ----------------------------------------------------------------------- */
1093 template <typename IMPL_RTL_STRINGDATA>
1094 void ensureCapacity ( IMPL_RTL_STRINGDATA** ppThis,
1095 sal_Int32 size )
1097 assert(ppThis);
1098 IMPL_RTL_STRINGDATA* const pOrg = *ppThis;
1099 if ( pOrg->refCount == 1 && pOrg->length >= size )
1100 return;
1101 assert( pOrg->length <= size ); // do not truncate
1102 auto* pTempStr = Alloc<IMPL_RTL_STRINGDATA>( size );
1103 Copy( pTempStr->buffer, pOrg->buffer, pOrg->length );
1104 // right now the length is still the same as of the original
1105 pTempStr->length = pOrg->length;
1106 pTempStr->buffer[ pOrg->length ] = '\0';
1107 *ppThis = pTempStr;
1108 RTL_LOG_STRING_NEW( *ppThis );
1110 release( pOrg );
1113 /* ----------------------------------------------------------------------- */
1115 template <typename IMPL_RTL_STRINGDATA, typename IMPL_RTL_STRCODE>
1116 void newReplaceStrAt ( IMPL_RTL_STRINGDATA** ppThis,
1117 IMPL_RTL_STRINGDATA* pStr,
1118 sal_Int32 nIndex,
1119 sal_Int32 nCount,
1120 const IMPL_RTL_STRCODE* pNewSubStr,
1121 sal_Int32 nNewSubStrLen )
1123 assert(ppThis);
1124 assert(nIndex >= 0 && nIndex <= pStr->length);
1125 assert(nCount >= 0);
1126 assert(nCount <= pStr->length - nIndex);
1127 assert(pNewSubStr != nullptr || nNewSubStrLen == 0);
1128 assert(nNewSubStrLen >= 0);
1129 /* Append? */
1130 if ( nIndex >= pStr->length )
1131 return newConcat(ppThis, pStr, pNewSubStr, nNewSubStrLen);
1133 /* not more than the String length could be deleted */
1134 if ( nCount >= pStr->length-nIndex )
1136 /* Assign of NewSubStr? */
1137 if (nIndex == 0)
1138 return newFromStr_WithLength( ppThis, pNewSubStr, nNewSubStrLen );
1140 nCount = pStr->length - nIndex;
1143 /* Assign of Str? */
1144 if ( !nCount && !nNewSubStrLen )
1145 return assign(ppThis, pStr);
1147 IMPL_RTL_STRINGDATA* pOrg = *ppThis;
1149 /* Alloc New Buffer */
1150 *ppThis = Alloc<IMPL_RTL_STRINGDATA>(pStr->length - nCount + nNewSubStrLen);
1151 assert(*ppThis != nullptr);
1152 auto* pBuffer = (*ppThis)->buffer;
1153 if ( nIndex )
1155 Copy( pBuffer, pStr->buffer, nIndex );
1156 pBuffer += nIndex;
1158 if ( nNewSubStrLen )
1160 Copy( pBuffer, pNewSubStr, nNewSubStrLen );
1161 pBuffer += nNewSubStrLen;
1163 Copy( pBuffer, pStr->buffer+nIndex+nCount, pStr->length-nIndex-nCount );
1165 RTL_LOG_STRING_NEW( *ppThis );
1166 /* must be done last, if pStr or pNewSubStr == *ppThis */
1167 if ( pOrg )
1168 release( pOrg );
1171 /* ----------------------------------------------------------------------- */
1173 template <typename IMPL_RTL_STRINGDATA>
1174 void newReplaceStrAt ( IMPL_RTL_STRINGDATA** ppThis,
1175 IMPL_RTL_STRINGDATA* pStr,
1176 sal_Int32 nIndex,
1177 sal_Int32 nCount,
1178 IMPL_RTL_STRINGDATA* pNewSubStr )
1180 assert(ppThis);
1181 assert(nIndex >= 0 && nIndex <= pStr->length);
1182 assert(nCount >= 0);
1183 assert(nCount <= pStr->length - nIndex);
1184 /* Append? */
1185 if (nIndex >= pStr->length)
1187 /* newConcat test, if pNewSubStr is 0 */
1188 newConcat(ppThis, pStr, pNewSubStr);
1189 return;
1192 /* not more than the String length could be deleted */
1193 if (nCount >= pStr->length-nIndex)
1195 /* Assign of NewSubStr? */
1196 if (nIndex == 0)
1198 if (!pNewSubStr)
1199 return new_(ppThis);
1200 else
1201 return assign(ppThis, pNewSubStr);
1203 nCount = pStr->length - nIndex;
1206 /* Assign of Str? */
1207 if (!nCount && (!pNewSubStr || !pNewSubStr->length))
1209 assign(ppThis, pStr);
1210 return;
1213 const auto* pNewSubStrBuf = pNewSubStr ? pNewSubStr->buffer : nullptr;
1214 const sal_Int32 nNewSubStrLength = pNewSubStr ? pNewSubStr->length : 0;
1215 newReplaceStrAt(ppThis, pStr, nIndex, nCount, pNewSubStrBuf, nNewSubStrLength);
1218 /* ----------------------------------------------------------------------- */
1220 template <class Traits, typename IMPL_RTL_STRINGDATA>
1221 void newReplaceChars(IMPL_RTL_STRINGDATA** ppThis, IMPL_RTL_STRINGDATA* pStr)
1223 assert(ppThis);
1224 assert(pStr);
1226 const auto pEnd = pStr->buffer + pStr->length;
1227 auto pCharStr = std::find_if(pStr->buffer, pEnd, [](auto c) { return Traits::Applicable(c); });
1228 if (pCharStr != pEnd)
1230 IMPL_RTL_STRINGDATA* pOrg = *ppThis;
1231 *ppThis = Alloc<IMPL_RTL_STRINGDATA>(pStr->length);
1232 assert(*ppThis != nullptr);
1233 auto* pNewCharStr = (*ppThis)->buffer;
1234 /* Copy String */
1235 const sal_Int32 nCount = pCharStr - pStr->buffer;
1236 Copy(pNewCharStr, pStr->buffer, nCount);
1237 pNewCharStr += nCount;
1238 /* replace/copy rest of the string */
1241 *pNewCharStr = Traits::Replace(*pCharStr);
1242 pNewCharStr++;
1243 pCharStr++;
1244 } while (pCharStr != pEnd);
1246 RTL_LOG_STRING_NEW(*ppThis);
1247 /* must be done last, if pStr == *ppThis */
1248 if (pOrg)
1249 release(pOrg);
1251 else
1252 assign(ppThis, pStr);
1255 /* ----------------------------------------------------------------------- */
1257 template <typename IMPL_RTL_STRINGDATA>
1258 void newTrim ( IMPL_RTL_STRINGDATA** ppThis,
1259 IMPL_RTL_STRINGDATA* pStr )
1261 assert(pStr);
1262 const auto view = trimView(pStr->buffer, pStr->length);
1264 if (static_cast<sal_Int32>(view.size()) == pStr->length)
1265 assign(ppThis, pStr);
1266 else
1267 newFromStr_WithLength(ppThis, view.data(), view.size());
1270 /* ----------------------------------------------------------------------- */
1272 template <typename IMPL_RTL_STRINGDATA>
1273 sal_Int32 getToken ( IMPL_RTL_STRINGDATA** ppThis,
1274 IMPL_RTL_STRINGDATA* pStr,
1275 sal_Int32 nToken,
1276 STRCODE<IMPL_RTL_STRINGDATA> cTok,
1277 sal_Int32 nIndex )
1279 assert(ppThis);
1280 assert(pStr);
1281 assert(nIndex <= pStr->length);
1283 // Set ppThis to an empty string and return -1 if either nToken or nIndex is
1284 // negative:
1285 if (nIndex >= 0 && nToken >= 0)
1287 const auto* pOrgCharStr = pStr->buffer;
1288 const auto* pCharStr = pOrgCharStr + nIndex;
1289 sal_Int32 nLen = pStr->length - nIndex;
1290 sal_Int32 nTokCount = 0;
1291 const auto* pCharStrStart = pCharStr;
1292 while (nLen > 0)
1294 if (*pCharStr == cTok)
1296 nTokCount++;
1298 if (nTokCount > nToken)
1299 break;
1300 if (nTokCount == nToken)
1301 pCharStrStart = pCharStr + 1;
1304 pCharStr++;
1305 nLen--;
1307 if (nTokCount >= nToken)
1309 newFromStr_WithLength(ppThis, pCharStrStart, pCharStr - pCharStrStart);
1310 if (nLen > 0)
1311 return pCharStr - pOrgCharStr + 1;
1312 else
1313 return -1;
1317 new_(ppThis);
1318 return -1;
1321 /* ======================================================================= */
1322 /* String buffer help functions */
1323 /* ======================================================================= */
1325 template <class IMPL_RTL_STRINGDATA>
1326 void stringbuffer_newFromStr_WithLength(IMPL_RTL_STRINGDATA** ppThis,
1327 const STRCODE<IMPL_RTL_STRINGDATA>* pStr, sal_Int32 count)
1329 assert(ppThis);
1330 assert(count >= 0);
1331 if (!pStr)
1332 count = 0; // Because old code didn't care about count when !pStr
1334 newFromStr_WithLength(ppThis, pStr, count, 16);
1337 template <class IMPL_RTL_STRINGDATA>
1338 sal_Int32 stringbuffer_newFromStringBuffer(IMPL_RTL_STRINGDATA** ppThis, sal_Int32 capacity,
1339 IMPL_RTL_STRINGDATA* pStr)
1341 assert(capacity >= 0);
1342 assert(pStr);
1344 if (capacity < pStr->length)
1345 capacity = pStr->length;
1347 newFromStr_WithLength(ppThis, pStr->buffer, pStr->length, capacity - pStr->length);
1348 return capacity;
1351 template <class IMPL_RTL_STRINGDATA>
1352 void stringbuffer_ensureCapacity(IMPL_RTL_STRINGDATA** ppThis, sal_Int32* capacity,
1353 sal_Int32 minimumCapacity)
1355 assert(ppThis);
1356 assert(capacity && *capacity >= 0);
1357 // assert(minimumCapacity >= 0); // It was commented out in rtl_stringbuffer_ensureCapacity
1358 if (minimumCapacity <= *capacity)
1359 return;
1361 const auto nLength = (*ppThis)->length;
1362 *capacity = (nLength + 1) * 2;
1363 if (minimumCapacity > *capacity)
1364 *capacity = minimumCapacity;
1366 newFromStr_WithLength(ppThis, (*ppThis)->buffer, nLength, *capacity - nLength);
1369 template <class IMPL_RTL_STRINGDATA, typename C>
1370 void stringbuffer_insert(IMPL_RTL_STRINGDATA** ppThis, sal_Int32* capacity, sal_Int32 offset,
1371 const C* pStr, sal_Int32 len)
1373 assert(ppThis);
1374 assert(capacity && *capacity >= 0);
1375 assert(offset >= 0 && offset <= (*ppThis)->length);
1376 assert(len >= 0);
1377 if (len == 0)
1378 return;
1380 stringbuffer_ensureCapacity(ppThis, capacity, (*ppThis)->length + len);
1382 sal_Int32 nOldLen = (*ppThis)->length;
1383 auto* pBuf = (*ppThis)->buffer;
1385 /* copy the tail */
1386 const sal_Int32 n = nOldLen - offset;
1387 if (n > 0)
1388 CopyBackward(pBuf + offset + len, pBuf + offset, n);
1390 /* insert the new characters */
1391 if (pStr != nullptr)
1392 Copy(pBuf + offset, pStr, len);
1394 (*ppThis)->length = nOldLen + len;
1395 pBuf[nOldLen + len] = 0;
1398 template <class IMPL_RTL_STRINGDATA>
1399 void stringbuffer_remove(IMPL_RTL_STRINGDATA** ppThis, sal_Int32 start, sal_Int32 len)
1401 assert(ppThis);
1402 assert(start >= 0 && start <= (*ppThis)->length);
1403 assert(len >= 0);
1405 if (len > (*ppThis)->length - start)
1406 len = (*ppThis)->length - start;
1408 //remove nothing
1409 if (!len)
1410 return;
1412 auto* pBuf = (*ppThis)->buffer;
1413 const sal_Int32 nTailLen = (*ppThis)->length - (start + len);
1415 if (nTailLen)
1417 /* move the tail */
1418 Copy(pBuf + start, pBuf + start + len, nTailLen);
1421 (*ppThis)->length -= len;
1422 pBuf[(*ppThis)->length] = 0;
1425 template <class S, typename CharTypeFrom, typename CharTypeTo>
1426 void newReplaceAllFromIndex(S** s, S* s1, CharTypeFrom const* from, sal_Int32 fromLength,
1427 CharTypeTo const* to, sal_Int32 toLength, sal_Int32 fromIndex)
1429 assert(s != nullptr);
1430 assert(s1 != nullptr);
1431 assert(fromLength >= 0);
1432 assert(from != nullptr || fromLength == 0);
1433 assert(toLength >= 0);
1434 assert(to != nullptr || toLength == 0);
1435 assert(fromIndex >= 0 && fromIndex <= s1->length);
1436 sal_Int32 i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex,
1437 from, fromLength);
1438 if (i >= 0)
1440 if (s1->length - fromLength > SAL_MAX_INT32 - toLength)
1441 std::abort();
1442 i += fromIndex;
1443 sal_Int32 nCapacity = s1->length + (toLength - fromLength);
1444 if (fromLength < toLength)
1446 // Pre-allocate up to 16 replacements more
1447 const sal_Int32 nMaxMoreFinds = (s1->length - i - fromLength) / fromLength;
1448 const sal_Int32 nIncrease = toLength - fromLength;
1449 const sal_Int32 nMoreReplacements = std::min(
1450 { nMaxMoreFinds, (SAL_MAX_INT32 - nCapacity) / nIncrease, sal_Int32(16) });
1451 nCapacity += nMoreReplacements * nIncrease;
1453 const auto pOld = *s;
1454 *s = Alloc<S>(nCapacity);
1455 (*s)->length = 0;
1456 fromIndex = 0;
1459 stringbuffer_insert(s, &nCapacity, (*s)->length, s1->buffer + fromIndex, i);
1460 stringbuffer_insert(s, &nCapacity, (*s)->length, to, toLength);
1461 fromIndex += i + fromLength;
1462 i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex,
1463 from, fromLength);
1464 } while (i >= 0);
1465 // the rest
1466 stringbuffer_insert(s, &nCapacity, (*s)->length,
1467 s1->buffer + fromIndex, s1->length - fromIndex);
1468 if (pOld)
1469 release(pOld); // Must be last in case *s == s1
1471 else
1472 assign(s, s1);
1474 RTL_LOG_STRING_NEW(*s);
1477 template <typename IMPL_RTL_STRINGDATA>
1478 void newReplace(IMPL_RTL_STRINGDATA** ppThis, IMPL_RTL_STRINGDATA* pStr,
1479 STRCODE<IMPL_RTL_STRINGDATA> cOld, STRCODE<IMPL_RTL_STRINGDATA> cNew)
1481 return newReplaceAllFromIndex(ppThis, pStr, &cOld, 1, &cNew, 1, 0);
1484 template <class S, typename CharTypeFrom, typename CharTypeTo>
1485 void newReplaceFirst(S** s, S* s1, CharTypeFrom const* from, sal_Int32 fromLength,
1486 CharTypeTo const* to, sal_Int32 toLength, sal_Int32& fromIndex)
1488 assert(s != nullptr);
1489 assert(s1 != nullptr);
1490 assert(fromLength >= 0);
1491 assert(from != nullptr || fromLength == 0);
1492 assert(toLength >= 0);
1493 assert(to != nullptr || toLength == 0);
1494 assert(fromIndex >= 0 && fromIndex <= s1->length);
1495 sal_Int32 i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex,
1496 from, fromLength);
1497 if (i >= 0)
1499 if (s1->length - fromLength > SAL_MAX_INT32 - toLength)
1500 std::abort();
1501 i += fromIndex;
1502 newReplaceStrAt(s, s1, i, fromLength, to, toLength);
1504 else
1505 assign(s, s1);
1507 fromIndex = i;
1510 // doubleToString implementation
1512 static inline constexpr sal_uInt64 eX[] = { 10ull,
1513 100ull,
1514 1000ull,
1515 10000ull,
1516 100000ull,
1517 1000000ull,
1518 10000000ull,
1519 100000000ull,
1520 1000000000ull,
1521 10000000000ull,
1522 100000000000ull,
1523 1000000000000ull,
1524 10000000000000ull,
1525 100000000000000ull,
1526 1000000000000000ull,
1527 10000000000000000ull,
1528 100000000000000000ull,
1529 1000000000000000000ull,
1530 10000000000000000000ull };
1532 template <typename S>
1533 void doubleToString(S** pResult, sal_Int32* pResultCapacity, sal_Int32 nResultOffset, double fValue,
1534 rtl_math_StringFormat eFormat, sal_Int32 nDecPlaces, STRCODE<S> cDecSeparator,
1535 sal_Int32 const* pGroups, STRCODE<S> cGroupSeparator,
1536 bool bEraseTrailingDecZeros)
1538 auto decimalDigits = [](sal_uInt64 n) {
1539 return std::distance(std::begin(eX), std::upper_bound(std::begin(eX), std::end(eX), n)) + 1;
1542 auto roundToPow10 = [](sal_uInt64 n, int e) {
1543 assert(e > 0 && o3tl::make_unsigned(e) <= std::size(eX));
1544 const sal_uInt64 d = eX[e - 1];
1545 return (n + d / 2) / d * d;
1548 auto append = [](S** s, sal_Int32* pCapacity, sal_Int32& rOffset, auto sv)
1550 stringbuffer_insert(s, pCapacity, rOffset, sv.data(), sv.size());
1551 rOffset += sv.size();
1554 if (std::isnan(fValue))
1556 // #i112652# XMLSchema-2
1557 constexpr std::string_view nan{ "NaN" };
1558 if (!pResultCapacity)
1559 return newFromStr_WithLength(pResult, nan.data(), nan.size());
1560 else
1561 return append(pResult, pResultCapacity, nResultOffset, nan);
1564 // sign adjustment, instead of testing for fValue<0.0 this will also fetch -0.0
1565 bool bSign = std::signbit(fValue);
1567 if (std::isinf(fValue))
1569 // #i112652# XMLSchema-2
1570 std::string_view inf = bSign ? std::string_view("-INF") : std::string_view("INF");
1571 if (!pResultCapacity)
1572 return newFromStr_WithLength(pResult, inf.data(), inf.size());
1573 else
1574 return append(pResult, pResultCapacity, nResultOffset, inf);
1577 if (bSign)
1578 fValue = -fValue;
1580 decltype(jkj::dragonbox::to_decimal(fValue, jkj::dragonbox::policy::sign::ignore,
1581 jkj::dragonbox::policy::trailing_zero::ignore)) aParts{};
1582 if (fValue) // to_decimal is documented to only handle non-zero finite numbers
1583 aParts = jkj::dragonbox::to_decimal(fValue, jkj::dragonbox::policy::sign::ignore,
1584 jkj::dragonbox::policy::trailing_zero::ignore);
1586 int nOrigDigits = decimalDigits(aParts.significand);
1587 int nExp = nOrigDigits + aParts.exponent - 1;
1588 int nRoundDigits = 15;
1590 // Unfortunately the old rounding below writes 1.79769313486232e+308 for
1591 // DBL_MAX and 4 subsequent nextafter(...,0).
1592 static const double fB1 = std::nextafter(std::numeric_limits<double>::max(), 0);
1593 static const double fB2 = std::nextafter(fB1, 0);
1594 static const double fB3 = std::nextafter(fB2, 0);
1595 static const double fB4 = std::nextafter(fB3, 0);
1596 if ((fValue >= fB4) && eFormat != rtl_math_StringFormat_F)
1598 // 1.7976931348623157e+308 instead of rounded 1.79769313486232e+308
1599 // that can't be converted back as out of range. For rounded values if
1600 // they exceed range they should not be written to exchange strings or
1601 // file formats.
1603 eFormat = rtl_math_StringFormat_E;
1604 nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, 0, 16);
1605 nRoundDigits = 17;
1608 // Use integer representation for integer values that fit into the
1609 // mantissa (1.((2^53)-1)) with a precision of 1 for highest accuracy.
1610 if ((eFormat == rtl_math_StringFormat_Automatic || eFormat == rtl_math_StringFormat_F)
1611 && aParts.exponent >= 0 && fValue < 0x1p53)
1613 eFormat = rtl_math_StringFormat_F;
1614 if (nDecPlaces == rtl_math_DecimalPlaces_Max)
1615 nDecPlaces = 0;
1616 else
1617 nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, -15, 15);
1619 if (bEraseTrailingDecZeros && nDecPlaces > 0)
1620 nDecPlaces = 0;
1622 nRoundDigits = nOrigDigits; // no rounding
1625 switch (eFormat)
1627 case rtl_math_StringFormat_Automatic:
1628 // E or F depending on exponent magnitude
1629 if (nExp <= -15 || nExp >= 15)
1631 if (nDecPlaces == rtl_math_DecimalPlaces_Max)
1632 nDecPlaces = 14;
1633 eFormat = rtl_math_StringFormat_E;
1635 else
1637 if (nDecPlaces == rtl_math_DecimalPlaces_Max)
1638 nDecPlaces = (nExp < 14) ? 15 - nExp - 1 : 15;
1639 eFormat = rtl_math_StringFormat_F;
1641 break;
1643 case rtl_math_StringFormat_G:
1644 case rtl_math_StringFormat_G1:
1645 case rtl_math_StringFormat_G2:
1646 // G-Point, similar to sprintf %G
1647 if (nDecPlaces == rtl_math_DecimalPlaces_DefaultSignificance)
1648 nDecPlaces = 6;
1650 if (nExp < -4 || nExp >= nDecPlaces)
1652 nDecPlaces = std::max<sal_Int32>(1, nDecPlaces - 1);
1654 if (eFormat == rtl_math_StringFormat_G)
1655 eFormat = rtl_math_StringFormat_E;
1656 else if (eFormat == rtl_math_StringFormat_G2)
1657 eFormat = rtl_math_StringFormat_E2;
1658 else if (eFormat == rtl_math_StringFormat_G1)
1659 eFormat = rtl_math_StringFormat_E1;
1661 else
1663 nDecPlaces = std::max<sal_Int32>(0, nDecPlaces - nExp - 1);
1664 eFormat = rtl_math_StringFormat_F;
1666 break;
1668 default:
1669 break;
1672 // Too large values for nDecPlaces make no sense; it might also be
1673 // rtl_math_DecimalPlaces_Max was passed with rtl_math_StringFormat_F or
1674 // others, but we don't want to allocate/deallocate 2GB just to fill it
1675 // with trailing '0' characters..
1676 nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, -20, 20);
1678 sal_Int32 nDigits = nDecPlaces + 1;
1680 if (eFormat == rtl_math_StringFormat_F)
1681 nDigits += nExp;
1683 // Round the number
1684 nRoundDigits = std::min<int>(nDigits, nRoundDigits);
1685 if (nDigits >= 0 && nOrigDigits > nRoundDigits)
1687 aParts.significand = roundToPow10(aParts.significand, nOrigDigits - nRoundDigits);
1688 assert(aParts.significand <= eX[nOrigDigits - 1]);
1689 if (aParts.significand == eX[nOrigDigits - 1]) // up-rounding to the next decade
1691 nOrigDigits++;
1692 nExp++;
1694 if (eFormat == rtl_math_StringFormat_F)
1695 nDigits++;
1699 sal_Int32 nBuf
1700 = (nDigits <= 0 ? std::max<sal_Int32>(nDecPlaces, std::abs(nExp)) : nDigits + nDecPlaces)
1701 + 10 + (pGroups ? std::abs(nDigits) * 2 : 0);
1702 // max(nDigits) = max(nDecPlaces) + 1 + max(nExp) + 1 = 20 + 1 + 308 + 1 = 330
1703 // max(nBuf) = max(nDigits) + max(nDecPlaces) + 10 + max(nDigits) * 2 = 330 * 3 + 20 + 10 = 1020
1704 assert(nBuf <= 1024);
1705 STRCODE<S>* const pBuf = static_cast<STRCODE<S>*>(alloca(nBuf * sizeof(STRCODE<S>)));
1706 STRCODE<S>* p = pBuf;
1707 if (bSign)
1708 *p++ = '-';
1710 bool bHasDec = false;
1712 int nDecPos;
1713 // Check for F format and number < 1
1714 if (eFormat == rtl_math_StringFormat_F)
1716 if (nExp < 0)
1718 *p++ = '0';
1719 if (nDecPlaces > 0)
1721 *p++ = cDecSeparator;
1722 bHasDec = true;
1725 sal_Int32 i = (nDigits <= 0 ? nDecPlaces : -nExp - 1);
1727 while ((i--) > 0)
1728 *p++ = '0';
1730 nDecPos = 0;
1732 else
1733 nDecPos = nExp + 1;
1735 else
1736 nDecPos = 1;
1738 int nGrouping = 0, nGroupSelector = 0, nGroupExceed = 0;
1739 if (nDecPos > 1 && pGroups && pGroups[0] && cGroupSeparator)
1741 while (nGrouping + pGroups[nGroupSelector] < nDecPos)
1743 nGrouping += pGroups[nGroupSelector];
1744 if (pGroups[nGroupSelector + 1])
1746 if (nGrouping + pGroups[nGroupSelector + 1] >= nDecPos)
1747 break; // while
1749 ++nGroupSelector;
1751 else if (!nGroupExceed)
1752 nGroupExceed = nGrouping;
1756 // print the number
1757 if (nDigits > 0)
1759 for (int nCurExp = nOrigDigits - 1;;)
1761 int nDigit;
1762 if (aParts.significand > 0 && nCurExp > 0)
1764 --nCurExp;
1765 nDigit = aParts.significand / eX[nCurExp];
1766 aParts.significand %= eX[nCurExp];
1768 else
1770 nDigit = aParts.significand;
1771 aParts.significand = 0;
1773 assert(nDigit >= 0 && nDigit < 10);
1774 *p++ = nDigit + '0';
1776 if (!--nDigits)
1777 break; // for
1779 if (nDecPos)
1781 if (!--nDecPos)
1783 *p++ = cDecSeparator;
1784 bHasDec = true;
1786 else if (nDecPos == nGrouping)
1788 *p++ = cGroupSeparator;
1789 nGrouping -= pGroups[nGroupSelector];
1791 if (nGroupSelector && nGrouping < nGroupExceed)
1792 --nGroupSelector;
1798 if (!bHasDec && eFormat == rtl_math_StringFormat_F)
1799 { // nDecPlaces < 0 did round the value
1800 while (--nDecPos > 0)
1801 { // fill before decimal point
1802 if (nDecPos == nGrouping)
1804 *p++ = cGroupSeparator;
1805 nGrouping -= pGroups[nGroupSelector];
1807 if (nGroupSelector && nGrouping < nGroupExceed)
1808 --nGroupSelector;
1811 *p++ = '0';
1815 if (bEraseTrailingDecZeros && bHasDec)
1817 while (*(p - 1) == '0')
1818 p--;
1820 if (*(p - 1) == cDecSeparator)
1821 p--;
1824 // Print the exponent ('E', followed by '+' or '-', followed by exactly
1825 // three digits for rtl_math_StringFormat_E). The code in
1826 // rtl_[u]str_valueOf{Float|Double} relies on this format.
1827 if (eFormat == rtl_math_StringFormat_E || eFormat == rtl_math_StringFormat_E2
1828 || eFormat == rtl_math_StringFormat_E1)
1830 if (p == pBuf)
1831 *p++ = '1';
1832 // maybe no nDigits if nDecPlaces < 0
1834 *p++ = 'E';
1835 if (nExp < 0)
1837 nExp = -nExp;
1838 *p++ = '-';
1840 else
1841 *p++ = '+';
1843 if (eFormat == rtl_math_StringFormat_E || nExp >= 100)
1844 *p++ = nExp / 100 + '0';
1846 nExp %= 100;
1848 if (eFormat == rtl_math_StringFormat_E || eFormat == rtl_math_StringFormat_E2 || nExp >= 10)
1849 *p++ = nExp / 10 + '0';
1851 *p++ = nExp % 10 + '0';
1854 if (!pResultCapacity)
1855 newFromStr_WithLength(pResult, pBuf, p - pBuf);
1856 else
1857 append(pResult, pResultCapacity, nResultOffset, std::basic_string_view(pBuf, p - pBuf));
1860 template <sal_Int32 maxLen, typename C, typename T> sal_Int32 SAL_CALL valueOfFP(C* pStr, T f)
1862 assert(pStr);
1863 STRINGDATA<C>* pResult = nullptr;
1864 doubleToString(&pResult, nullptr, 0, f, rtl_math_StringFormat_G,
1865 maxLen - std::size("-x.E-xxx") + 1, '.', nullptr, 0, true);
1866 const sal_Int32 nLen = pResult->length;
1867 assert(nLen < maxLen);
1868 Copy(pStr, pResult->buffer, nLen + 1);
1869 release(pResult);
1870 return nLen;
1875 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */