Bump version to 24.04.3.4
[LibreOffice.git] / sal / rtl / strtmpl.hxx
blob8c63f1081f9ccda9755b5d5ab111ed693158063b
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 <cstddef>
26 #include <cstdlib>
27 #include <cstring>
28 #include <cwchar>
29 #include <limits>
30 #include <new>
31 #include <string_view>
32 #include <type_traits>
33 #include <utility>
35 #include "strimp.hxx"
37 #include <o3tl/safeint.hxx>
38 #include <o3tl/string_view.hxx>
39 #include <osl/diagnose.h>
40 #include <sal/log.hxx>
41 #include <rtl/character.hxx>
42 #include <rtl/math.h>
43 #include <rtl/string.h>
44 #include <rtl/ustring.h>
46 #include <dragonbox/dragonbox.h>
48 void internRelease(rtl_uString*);
50 namespace rtl::str
52 template <typename C> auto UChar(C c) { return std::make_unsigned_t<C>(c); }
54 // Wrappers around null-terminated/known-length strings, that allow to generalize algorithms
55 // without overhead (e.g., without need to get length of null-terminated strings).
57 template <typename C> struct null_terminated
59 C* p;
60 null_terminated(C* pStr)
61 : p(pStr)
63 assert(pStr);
65 auto begin() const { return p; }
66 struct EndDetector
68 friend bool operator==(EndDetector, C* iter) { return *iter == 0; }
69 friend bool operator==(C* iter, EndDetector) { return *iter == 0; }
70 friend bool operator!=(EndDetector, C* iter) { return *iter != 0; }
71 friend bool operator!=(C* iter, EndDetector) { return *iter != 0; }
73 static auto end() { return EndDetector{}; }
76 template <typename C> struct with_length
78 C* p;
79 sal_Int32 len;
80 with_length(C* pStr, sal_Int32 nLength)
81 : p(pStr)
82 , len(nLength)
84 assert(len >= 0);
86 auto begin() const { return p; }
87 auto end() const { return p + len; }
90 template <bool (&fApplicable)(sal_uInt32), sal_uInt32 (&fReplace)(sal_uInt32)> struct CaseReplace
92 static auto Applicable() { return [](auto c) { return fApplicable(UChar(c)); }; }
93 template <typename C> static C Replace(C c) { return fReplace(UChar(c)); }
95 constexpr CaseReplace<rtl::isAsciiUpperCase, rtl::toAsciiLowerCase> toAsciiLower;
96 constexpr CaseReplace<rtl::isAsciiLowerCase, rtl::toAsciiUpperCase> toAsciiUpper;
98 template <typename C> struct FromTo
100 C from;
101 C to;
102 FromTo(C cFrom, C cTo) : from(cFrom), to(cTo) {}
103 auto Applicable() const { return [this](C c) { return c == from; }; }
104 C Replace(C c) const { return c == from ? to : c; }
107 template <typename C> void Copy(C* _pDest, const C* _pSrc, sal_Int32 _nCount)
109 // take advantage of builtin optimisations
110 std::copy(_pSrc, _pSrc + _nCount, _pDest);
113 template <typename C> void CopyBackward(C* _pDest, const C* _pSrc, sal_Int32 _nCount)
115 // take advantage of builtin optimisations
116 std::copy_backward(_pSrc, _pSrc + _nCount, _pDest + _nCount);
119 inline void Copy(sal_Unicode* _pDest, const char* _pSrc, sal_Int32 _nCount)
121 std::transform(_pSrc, _pSrc + _nCount, _pDest,
122 [](char c)
124 assert(rtl::isAscii(static_cast<unsigned char>(c)));
125 SAL_WARN_IF(c == '\0', "rtl.string", "Found embedded \\0 ASCII character");
126 return static_cast<unsigned char>(c);
130 inline sal_Int16 implGetDigit(sal_Unicode ch, sal_Int16 nRadix)
132 sal_Int16 n = -1;
133 if ((ch >= '0') && (ch <= '9'))
134 n = ch - '0';
135 else if ((ch >= 'a') && (ch <= 'z'))
136 n = ch - 'a' + 10;
137 else if ((ch >= 'A') && (ch <= 'Z'))
138 n = ch - 'A' + 10;
139 return (n < nRadix) ? n : -1;
142 /* ======================================================================= */
143 /* C-String functions which could be used without the String-Class */
144 /* ======================================================================= */
146 template <typename T> sal_Int32 getLength( const T* pStr )
148 assert(pStr);
149 if constexpr (std::is_class_v<T>)
151 return pStr->length;
153 else
155 // take advantage of builtin optimisations
156 return std::char_traits<T>::length(pStr);
160 /* ----------------------------------------------------------------------- */
162 template <typename C> void warnIfCharAndNotAscii(C c)
164 if constexpr (sizeof(c) == sizeof(char))
165 SAL_WARN_IF(!rtl::isAscii(static_cast<unsigned char>(c)), "rtl.string",
166 "Found non-ASCII char");
169 template <typename C1, typename C2> void warnIfOneIsCharAndNotAscii(C1 c1, C2 c2)
171 if constexpr (sizeof(c1) != sizeof(c2))
173 warnIfCharAndNotAscii(c1);
174 warnIfCharAndNotAscii(c2);
178 struct CompareNormal
180 template <typename C1, typename C2> static sal_Int32 compare(C1 c1, C2 c2)
182 warnIfOneIsCharAndNotAscii(c1, c2);
183 return static_cast<sal_Int32>(UChar(c1))
184 - static_cast<sal_Int32>(UChar(c2));
188 struct CompareIgnoreAsciiCase
190 template <typename C1, typename C2> static sal_Int32 compare(C1 c1, C2 c2)
192 warnIfOneIsCharAndNotAscii(c1, c2);
193 return rtl::compareIgnoreAsciiCase(UChar(c1), UChar(c2));
197 /* ----------------------------------------------------------------------- */
199 struct NoShortening
201 constexpr bool operator>=(int) { return true; } // for assert
202 constexpr bool operator==(int) { return false; } // for loop break check
203 constexpr void operator--() {} // for decrement in loop
204 } constexpr noShortening;
206 template <class S1, class S2, class Compare, typename Shorten_t>
207 sal_Int32 compare(S1 s1, S2 s2, Compare, Shorten_t shortenedLength)
209 static_assert(std::is_same_v<Shorten_t, NoShortening> || std::is_same_v<Shorten_t, sal_Int32>);
210 assert(shortenedLength >= 0);
211 auto pStr1 = s1.begin();
212 const auto end1 = s1.end();
213 auto pStr2 = s2.begin();
214 const auto end2 = s2.end();
215 for (;;)
217 if (shortenedLength == 0)
218 return 0;
219 if (pStr2 == end2)
220 return pStr1 == end1 ? 0 : 1;
221 if (pStr1 == end1)
222 return -1;
223 if (const sal_Int32 nRet = Compare::compare(*pStr1, *pStr2))
224 return nRet;
225 --shortenedLength;
226 ++pStr1;
227 ++pStr2;
231 // take advantage of builtin optimisations
232 template <typename C> requires (sizeof(C) == sizeof(wchar_t))
233 sal_Int32 compare(null_terminated<C> s1, null_terminated<C> s2, CompareNormal, NoShortening)
235 return wcscmp(reinterpret_cast<wchar_t const*>(s1.p), reinterpret_cast<wchar_t const*>(s2.p));
237 template <typename C> requires (sizeof(C) == sizeof(char))
238 sal_Int32 compare(null_terminated<C> s1, null_terminated<C> s2, CompareNormal, NoShortening)
240 return strcmp(reinterpret_cast<char const*>(s1.p), reinterpret_cast<char const*>(s2.p));
242 template <typename C>
243 sal_Int32 compare(with_length<C> s1, with_length<C> s2, CompareNormal, NoShortening)
245 std::basic_string_view sv1(s1.p, s1.len);
246 return sv1.compare(std::basic_string_view(s2.p, s2.len));
248 template <typename C1, typename C2, class Compare>
249 sal_Int32 compare(with_length<C1> s1, with_length<C2> s2, Compare cf, sal_Int32 nShortenedLength)
251 assert(nShortenedLength >= 0);
252 s1.len = std::min(s1.len, nShortenedLength);
253 s2.len = std::min(s2.len, nShortenedLength);
254 return compare(s1, s2, cf, noShortening);
257 /* ----------------------------------------------------------------------- */
259 template <typename C1, typename C2, class Compare>
260 sal_Int32 reverseCompare_WithLengths(const C1* pStr1, sal_Int32 nStr1Len,
261 const C2* pStr2, sal_Int32 nStr2Len, Compare)
263 assert(pStr1 || nStr1Len == 0);
264 assert(nStr1Len >= 0);
265 assert(pStr2 || nStr2Len == 0);
266 assert(nStr2Len >= 0);
267 const C1* pStr1Run = pStr1+nStr1Len;
268 const C2* pStr2Run = pStr2+nStr2Len;
269 while ((pStr1 < pStr1Run) && (pStr2 < pStr2Run))
271 pStr1Run--;
272 pStr2Run--;
273 if (const sal_Int32 nRet = Compare::compare(*pStr1Run, *pStr2Run))
274 return nRet;
277 return nStr1Len - nStr2Len;
280 /* ----------------------------------------------------------------------- */
282 template <typename C> sal_Int32 hashCode_WithLength(const C* pStr, sal_Int32 nLen)
284 assert(nLen >= 0);
285 sal_uInt32 h = static_cast<sal_uInt32>(nLen);
286 while ( nLen > 0 )
288 h = (h*37U) + UChar( *pStr );
289 pStr++;
290 nLen--;
292 return static_cast<sal_Int32>(h);
295 /* ----------------------------------------------------------------------- */
297 template <typename C> sal_Int32 hashCode(const C* pStr)
299 return hashCode_WithLength( pStr, getLength( pStr ) );
302 /* ----------------------------------------------------------------------- */
304 template <typename C> sal_Int32 indexOfChar(const C* pStr, C c)
306 assert(pStr);
307 if (!c)
308 return -1; // Unifies behavior of strchr/wcschr and unoptimized algorithm wrt '\0'
310 if constexpr (sizeof(C) == sizeof(char))
312 // take advantage of builtin optimisations
313 const C* p = strchr(pStr, c);
314 return p ? p - pStr : -1;
316 else if constexpr (sizeof(C) == sizeof(wchar_t))
318 // take advantage of builtin optimisations
319 wchar_t const * p = wcschr(reinterpret_cast<wchar_t const *>(pStr), static_cast<wchar_t>(c));
320 return p ? p - reinterpret_cast<wchar_t const *>(pStr) : -1;
322 else
324 const C* pTempStr = pStr;
325 while ( *pTempStr )
327 if ( *pTempStr == c )
328 return pTempStr-pStr;
330 pTempStr++;
333 return -1;
337 /* ----------------------------------------------------------------------- */
339 template <typename C> sal_Int32 indexOfChar_WithLength(const C* pStr, sal_Int32 nLen, C c)
341 // assert(nLen >= 0);
342 if (nLen <= 0)
343 return -1;
344 // take advantage of builtin optimisations
345 std::basic_string_view v(pStr, nLen);
346 auto idx = v.find(c);
347 return idx == v.npos ? -1 : idx;
350 /* ----------------------------------------------------------------------- */
352 template <typename C> sal_Int32 lastIndexOfChar_WithLength(const C* pStr, sal_Int32 nLen, C c)
354 assert(nLen >= 0);
355 // take advantage of builtin optimisations
356 std::basic_string_view v(pStr, nLen);
357 auto idx = v.rfind(c);
358 return idx == v.npos ? -1 : idx;
361 /* ----------------------------------------------------------------------- */
363 template <typename C> sal_Int32 lastIndexOfChar(const C* pStr, C c)
365 assert(pStr);
366 if (!c)
367 return -1; // Unifies behavior of strrchr/wcsrchr and lastIndexOfChar_WithLength wrt '\0'
369 if constexpr (sizeof(C) == sizeof(char))
371 // take advantage of builtin optimisations
372 const C* p = strrchr(pStr, c);
373 return p ? p - pStr : -1;
375 else if constexpr (sizeof(C) == sizeof(wchar_t))
377 // take advantage of builtin optimisations
378 wchar_t const * p = wcsrchr(reinterpret_cast<wchar_t const *>(pStr), static_cast<wchar_t>(c));
379 return p ? p - reinterpret_cast<wchar_t const *>(pStr) : -1;
381 else
383 return lastIndexOfChar_WithLength( pStr, getLength( pStr ), c );
387 /* ----------------------------------------------------------------------- */
389 template <typename C>
390 sal_Int32 indexOfStr_WithLength(const C* pStr, sal_Int32 nStrLen,
391 const C* pSubStr, sal_Int32 nSubLen)
393 assert(nStrLen >= 0);
394 assert(nSubLen >= 0);
395 /* an empty SubString is always not findable */
396 if ( nSubLen == 0 )
397 return -1;
398 // take advantage of builtin optimisations
399 std::basic_string_view v(pStr, nStrLen);
400 auto idx = nSubLen == 1 ? v.find(*pSubStr) : v.find(pSubStr, 0, nSubLen);
401 return idx == v.npos ? -1 : idx;
404 inline sal_Int32 indexOfStr_WithLength(const sal_Unicode* pStr, sal_Int32 nStrLen,
405 const char* pSubStr, sal_Int32 nSubLen)
407 assert(nStrLen >= 0);
408 assert(nSubLen >= 0);
409 if (nSubLen > 0 && nSubLen <= nStrLen)
411 sal_Unicode const* end = pStr + nStrLen;
412 sal_Unicode const* cursor = pStr;
414 while (cursor < end)
416 cursor = std::char_traits<sal_Unicode>::find(cursor, end - cursor, *pSubStr);
417 if (!cursor || (end - cursor < nSubLen))
419 /* no enough left to actually have a match */
420 break;
422 /* now it is worth trying a full match */
423 if (nSubLen == 1 || rtl_ustr_asciil_reverseEquals_WithLength(cursor, pSubStr, nSubLen))
425 return cursor - pStr;
427 cursor += 1;
430 return -1;
433 /* ----------------------------------------------------------------------- */
435 template <typename C> sal_Int32 indexOfStr(const C* pStr, const C* pSubStr)
437 assert(pStr);
438 assert(pSubStr);
439 /* an empty SubString is always not findable */
440 if (*pSubStr == 0)
441 return -1;
442 if constexpr (sizeof(C) == sizeof(char))
444 // take advantage of builtin optimisations
445 const C* p = strstr(pStr, pSubStr);
446 return p ? p - pStr : -1;
448 else if constexpr (sizeof(C) == sizeof(wchar_t))
450 // take advantage of builtin optimisations
451 wchar_t const * p = wcsstr(reinterpret_cast<wchar_t const *>(pStr), reinterpret_cast<wchar_t const *>(pSubStr));
452 return p ? p - reinterpret_cast<wchar_t const *>(pStr) : -1;
454 else
456 return indexOfStr_WithLength( pStr, getLength( pStr ),
457 pSubStr, getLength( pSubStr ) );
461 /* ----------------------------------------------------------------------- */
463 template <typename C>
464 sal_Int32 lastIndexOfStr_WithLength(const C* pStr, sal_Int32 nStrLen,
465 const C* pSubStr, sal_Int32 nSubLen)
467 assert(nStrLen >= 0);
468 assert(nSubLen >= 0);
469 /* an empty SubString is always not findable */
470 if ( nSubLen == 0 )
471 return -1;
472 // take advantage of builtin optimisations
473 std::basic_string_view v(pStr, nStrLen);
474 std::basic_string_view needle(pSubStr, nSubLen);
475 auto idx = v.rfind(needle);
476 return idx == v.npos ? -1 : idx;
479 /* ----------------------------------------------------------------------- */
481 template <typename C> sal_Int32 lastIndexOfStr(const C* pStr, const C* pSubStr)
483 return lastIndexOfStr_WithLength(pStr, getLength(pStr), pSubStr, getLength(pSubStr));
486 /* ----------------------------------------------------------------------- */
488 template <class S, class Replacer> void replaceChars(S str, Replacer replacer)
490 for (auto& rChar : str)
491 rChar = replacer.Replace(rChar);
494 /* ----------------------------------------------------------------------- */
496 template <typename C> sal_Int32 trim_WithLength(C* pStr, sal_Int32 nLen)
498 const auto view = o3tl::trim(std::basic_string_view(pStr, nLen));
500 if (static_cast<sal_Int32>(view.size()) != nLen)
502 nLen = static_cast<sal_Int32>(view.size());
503 if (view.data() != pStr)
504 Copy(pStr, view.data(), nLen);
505 *(pStr+nLen) = 0;
508 return nLen;
511 /* ----------------------------------------------------------------------- */
513 template <typename C> sal_Int32 trim(C* pStr) { return trim_WithLength(pStr, getLength(pStr)); }
515 /* ----------------------------------------------------------------------- */
517 template <typename C> sal_Int32 valueOfBoolean(C* pStr, sal_Bool b)
519 assert(pStr);
520 if ( b )
522 *pStr = 't';
523 pStr++;
524 *pStr = 'r';
525 pStr++;
526 *pStr = 'u';
527 pStr++;
528 *pStr = 'e';
529 pStr++;
530 *pStr = 0;
531 return 4;
533 else
535 *pStr = 'f';
536 pStr++;
537 *pStr = 'a';
538 pStr++;
539 *pStr = 'l';
540 pStr++;
541 *pStr = 's';
542 pStr++;
543 *pStr = 'e';
544 pStr++;
545 *pStr = 0;
546 return 5;
550 /* ----------------------------------------------------------------------- */
552 template <typename C> sal_Int32 valueOfChar(C* pStr, C c)
554 assert(pStr);
555 *pStr++ = c;
556 *pStr = 0;
557 return 1;
560 /* ----------------------------------------------------------------------- */
562 template <sal_Int32 maxLen, typename C, typename T>
563 sal_Int32 valueOfInt(C* pStr, T n, sal_Int16 nRadix)
565 assert(pStr);
566 assert( nRadix >= RTL_STR_MIN_RADIX && nRadix <= RTL_STR_MAX_RADIX );
567 const auto* const pStart = pStr;
568 char aBuf[maxLen];
569 char* pBuf = aBuf;
570 using uT = std::make_unsigned_t<T>;
571 uT nValue;
573 /* Radix must be valid */
574 if ( (nRadix < RTL_STR_MIN_RADIX) || (nRadix > RTL_STR_MAX_RADIX) )
575 nRadix = 10;
577 if constexpr (std::is_signed_v<T>)
579 /* is value negative */
580 if ( n < 0 )
582 *pStr = '-';
583 pStr++;
584 nValue = n == std::numeric_limits<T>::min() ? static_cast<uT>(n) : -n;
586 else
587 nValue = n;
589 else
590 nValue = n;
592 /* create a recursive buffer with all values, except the last one */
595 char nDigit = static_cast<char>(nValue % nRadix);
596 nValue /= nRadix;
597 if ( nDigit > 9 )
598 *pBuf = (nDigit-10) + 'a';
599 else
600 *pBuf = (nDigit + '0' );
601 pBuf++;
603 while ( nValue > 0 );
605 /* copy the values in the right direction into the destination buffer */
606 pStr = std::reverse_copy(aBuf, pBuf, pStr);
607 *pStr = 0;
609 return pStr - pStart;
612 /* ----------------------------------------------------------------------- */
614 template <typename C> sal_Bool toBoolean(const C* pStr)
616 assert(pStr);
617 if ( *pStr == '1' )
618 return true;
620 if ( (*pStr == 'T') || (*pStr == 't') )
622 pStr++;
623 if ( (*pStr == 'R') || (*pStr == 'r') )
625 pStr++;
626 if ( (*pStr == 'U') || (*pStr == 'u') )
628 pStr++;
629 if ( (*pStr == 'E') || (*pStr == 'e') )
630 return true;
635 return false;
638 /* ----------------------------------------------------------------------- */
640 template <typename T, class Iter> inline bool HandleSignChar(Iter& iter)
642 if constexpr (std::numeric_limits<T>::is_signed)
644 if (*iter == '-')
646 ++iter;
647 return true;
650 if (*iter == '+')
651 ++iter;
652 return false;
655 template <typename T> std::pair<T, sal_Int16> DivMod(sal_Int16 nRadix, [[maybe_unused]] bool bNeg)
657 if constexpr (std::numeric_limits<T>::is_signed)
658 if (bNeg)
659 return { -(std::numeric_limits<T>::min() / nRadix),
660 -(std::numeric_limits<T>::min() % nRadix) };
661 return { std::numeric_limits<T>::max() / nRadix, std::numeric_limits<T>::max() % nRadix };
664 template <typename T, class S> T toInt(S str, sal_Int16 nRadix)
666 assert( nRadix >= RTL_STR_MIN_RADIX && nRadix <= RTL_STR_MAX_RADIX );
668 if ( (nRadix < RTL_STR_MIN_RADIX) || (nRadix > RTL_STR_MAX_RADIX) )
669 nRadix = 10;
671 auto pStr = str.begin();
672 const auto end = str.end();
674 /* Skip whitespaces */
675 while (pStr != end && o3tl::internal::implIsWhitespace(UChar(*pStr)))
676 pStr++;
677 if (pStr == end)
678 return 0;
680 const bool bNeg = HandleSignChar<T>(pStr);
681 const auto& [nDiv, nMod] = DivMod<T>(nRadix, bNeg);
682 assert(nDiv > 0);
684 std::make_unsigned_t<T> n = 0;
685 while (pStr != end)
687 sal_Int16 nDigit = implGetDigit(UChar(*pStr), nRadix);
688 if ( nDigit < 0 )
689 break;
690 if (static_cast<std::make_unsigned_t<T>>(nMod < nDigit ? nDiv - 1 : nDiv) < n)
691 return 0;
693 n *= nRadix;
694 n += nDigit;
696 pStr++;
699 if constexpr (std::numeric_limits<T>::is_signed)
700 if (bNeg)
701 return n == static_cast<std::make_unsigned_t<T>>(std::numeric_limits<T>::min())
702 ? std::numeric_limits<T>::min()
703 : -static_cast<T>(n);
704 return static_cast<T>(n);
707 /* ======================================================================= */
708 /* Internal String-Class help functions */
709 /* ======================================================================= */
711 template <class rtl_tString> using Char_T = std::remove_extent_t<decltype(rtl_tString::buffer)>;
713 template <typename rtl_tString> rtl_tString* Alloc(sal_Int32 nLen)
715 constexpr auto fix = offsetof(rtl_tString, buffer) + sizeof rtl_tString::buffer;
716 rtl_tString * pData
717 = (o3tl::make_unsigned(nLen)
718 <= ((std::numeric_limits<std::size_t>::max() - fix)
719 / sizeof (Char_T<rtl_tString>)))
720 ? static_cast<rtl_tString *>(rtl_allocateString(
721 fix + nLen * sizeof (Char_T<rtl_tString>)))
722 : nullptr;
723 if (pData != nullptr) {
724 pData->refCount = 1;
725 pData->length = nLen;
726 pData->buffer[nLen] = 0;
728 return pData;
731 /* ======================================================================= */
732 /* String-Class functions */
733 /* ======================================================================= */
735 template <typename rtl_tString> void acquire(rtl_tString* pThis)
737 if (!SAL_STRING_IS_STATIC (pThis))
738 osl_atomic_increment( &((pThis)->refCount) );
741 /* ----------------------------------------------------------------------- */
743 template <typename rtl_tString> void release(rtl_tString* pThis)
745 if (SAL_UNLIKELY(SAL_STRING_IS_STATIC (pThis)))
746 return;
748 /* OString doesn't have an 'intern' */
749 if constexpr (sizeof(Char_T<rtl_tString>) == sizeof(sal_Unicode))
751 if (SAL_STRING_IS_INTERN (pThis))
753 internRelease (pThis);
754 return;
758 if ( !osl_atomic_decrement( &(pThis->refCount) ) )
760 RTL_LOG_STRING_DELETE( pThis );
761 rtl_freeString( pThis );
765 /* ----------------------------------------------------------------------- */
767 /* static data to be referenced by all empty strings
768 * the refCount is predefined to 1 and must never become 0 !
770 template <typename rtl_tString> struct EmptyStringImpl
772 static rtl_tString data;
775 template <>
776 inline rtl_uString EmptyStringImpl<rtl_uString>::data = {
777 sal_Int32(SAL_STRING_INTERN_FLAG | SAL_STRING_STATIC_FLAG | 1), /* sal_Int32 refCount; */
778 0, /* sal_Int32 length; */
779 { 0 } /* sal_Unicode buffer[1]; */
782 template <>
783 inline rtl_String EmptyStringImpl<rtl_String>::data = {
784 SAL_STRING_STATIC_FLAG | 1, /* sal_Int32 refCount; */
785 0, /* sal_Int32 length; */
786 { 0 } /* char buffer[1]; */
789 template <typename rtl_tString> void new_(rtl_tString** ppThis)
791 assert(ppThis);
792 if ( *ppThis)
793 release( *ppThis );
795 *ppThis = &EmptyStringImpl<rtl_tString>::data;
798 /* ----------------------------------------------------------------------- */
800 template <typename rtl_tString> void new_WithLength(rtl_tString** ppThis, sal_Int32 nLen)
802 assert(ppThis);
803 assert(nLen >= 0);
804 if ( nLen <= 0 )
805 new_( ppThis );
806 else
808 if ( *ppThis)
809 release( *ppThis );
811 *ppThis = Alloc<rtl_tString>( nLen );
812 assert(*ppThis != nullptr);
813 (*ppThis)->length = 0;
814 (*ppThis)->buffer[0] = 0;
818 /* ----------------------------------------------------------------------- */
820 template <typename rtl_tString, typename C>
821 void newFromStr_WithLength(rtl_tString** ppThis, const C* pCharStr, sal_Int32 nLen,
822 sal_Int32 allocExtra = 0)
824 assert(ppThis);
825 assert(nLen >= 0);
826 assert(pCharStr || nLen == 0);
827 assert(allocExtra >= 0);
829 if (nLen + allocExtra == 0)
830 return new_(ppThis);
832 rtl_tString* pOrg = *ppThis;
833 *ppThis = Alloc<rtl_tString>(nLen + allocExtra);
834 assert(*ppThis != nullptr);
835 if (nLen > 0)
836 Copy((*ppThis)->buffer, pCharStr, nLen);
837 if (allocExtra > 0)
839 (*ppThis)->length = nLen;
840 (*ppThis)->buffer[nLen] = 0;
843 RTL_LOG_STRING_NEW(*ppThis);
845 /* must be done last, if pCharStr belongs to *ppThis */
846 if (pOrg)
847 release(pOrg);
850 template <typename rtl_tString> void newFromString(rtl_tString** ppThis, const rtl_tString* pStr)
852 assert(pStr);
854 newFromStr_WithLength(ppThis, pStr->buffer, pStr->length);
857 /* ----------------------------------------------------------------------- */
859 template <typename rtl_tString>
860 void newFromStr(rtl_tString** ppThis, const Char_T<rtl_tString>* pCharStr)
862 newFromStr_WithLength(ppThis, pCharStr, getLength(pCharStr));
865 /* ----------------------------------------------------------------------- */
867 template <typename rtl_tString> void assign(rtl_tString** ppThis, rtl_tString* pStr)
869 assert(ppThis);
870 /* must be done at first, if pStr == *ppThis */
871 acquire( pStr );
873 if ( *ppThis )
874 release( *ppThis );
876 *ppThis = pStr;
879 /* ----------------------------------------------------------------------- */
881 template <typename rtl_tString>
882 void newFromSubString(rtl_tString** ppThis, const rtl_tString* pFrom, sal_Int32 beginIndex,
883 sal_Int32 count)
885 assert(ppThis);
886 if ( beginIndex == 0 && count == pFrom->length )
887 return assign(ppThis, const_cast<rtl_tString*>(pFrom));
888 if ( count < 0 || beginIndex < 0 || beginIndex + count > pFrom->length )
890 assert(false); // fail fast at least in debug builds
891 return newFromStr_WithLength(ppThis, "!!br0ken!!", 10);
894 newFromStr_WithLength( ppThis, pFrom->buffer + beginIndex, count );
897 /* ----------------------------------------------------------------------- */
899 template <typename rtl_tString> auto* getStr(rtl_tString* pThis)
901 assert(pThis);
902 return pThis->buffer;
905 /* ----------------------------------------------------------------------- */
907 enum ThrowPolicy { NoThrow, Throw };
909 template <ThrowPolicy throwPolicy, typename rtl_tString, typename C1, typename C2>
910 void newConcat(rtl_tString** ppThis, const C1* pLeft, sal_Int32 nLeftLength,
911 const C2* pRight, sal_Int32 nRightLength)
913 assert(ppThis);
914 assert(nLeftLength >= 0);
915 assert(pLeft || nLeftLength == 0);
916 assert(nRightLength >= 0);
917 assert(pRight || nRightLength == 0);
918 rtl_tString* pOrg = *ppThis;
920 if (nLeftLength > std::numeric_limits<sal_Int32>::max() - nRightLength)
922 if constexpr (throwPolicy == NoThrow)
923 *ppThis = nullptr;
924 else
926 #if !defined(__COVERITY__)
927 throw std::length_error("newConcat");
928 #else
929 //coverity doesn't report std::bad_alloc as an unhandled exception when
930 //potentially thrown from destructors but does report std::length_error
931 throw std::bad_alloc();
932 #endif
935 else
937 auto* pTempStr = Alloc<rtl_tString>(nLeftLength + nRightLength);
938 OSL_ASSERT(pTempStr != nullptr);
939 *ppThis = pTempStr;
940 if (*ppThis != nullptr) {
941 if (nLeftLength)
942 Copy( pTempStr->buffer, pLeft, nLeftLength );
943 if (nRightLength)
944 Copy( pTempStr->buffer+nLeftLength, pRight, nRightLength );
946 RTL_LOG_STRING_NEW( *ppThis );
950 /* must be done last, if left or right == *ppThis */
951 if ( pOrg )
952 release( pOrg );
955 template <typename rtl_tString, typename C>
956 void newConcat(rtl_tString** ppThis, rtl_tString* pLeft, const C* pRight, sal_Int32 nRightLength)
958 assert(pLeft != nullptr);
959 if (nRightLength == 0)
960 assign(ppThis, pLeft);
961 else
962 newConcat<Throw>(ppThis, pLeft->buffer, pLeft->length, pRight, nRightLength);
965 template <typename rtl_tString>
966 void newConcat(rtl_tString** ppThis, rtl_tString* pLeft, rtl_tString* pRight)
968 /* Test for 0-Pointer - if not, change newReplaceStrAt! */
969 if ( !pRight || !pRight->length )
971 assert(pLeft != nullptr);
972 assign(ppThis, pLeft);
974 else if ( !pLeft || !pLeft->length )
975 assign(ppThis, pRight);
976 else
977 newConcat<NoThrow>(ppThis, pLeft->buffer, pLeft->length, pRight->buffer, pRight->length);
980 /* ----------------------------------------------------------------------- */
982 template <typename rtl_tString> void ensureCapacity(rtl_tString** ppThis, sal_Int32 size)
984 assert(ppThis);
985 rtl_tString* const pOrg = *ppThis;
986 if ( pOrg->refCount == 1 && pOrg->length >= size )
987 return;
988 assert( pOrg->length <= size ); // do not truncate
989 auto* pTempStr = Alloc<rtl_tString>( size );
990 Copy( pTempStr->buffer, pOrg->buffer, pOrg->length );
991 // right now the length is still the same as of the original
992 pTempStr->length = pOrg->length;
993 pTempStr->buffer[ pOrg->length ] = '\0';
994 *ppThis = pTempStr;
995 RTL_LOG_STRING_NEW( *ppThis );
997 release( pOrg );
1000 /* ----------------------------------------------------------------------- */
1002 template <typename rtl_tString, typename C>
1003 void newReplaceStrAt(rtl_tString** ppThis, rtl_tString* pStr, sal_Int32 nIndex, sal_Int32 nCount,
1004 const C* pNewSubStr, sal_Int32 nNewSubStrLen)
1006 assert(ppThis);
1007 assert(nIndex >= 0 && nIndex <= pStr->length);
1008 assert(nCount >= 0);
1009 assert(nCount <= pStr->length - nIndex);
1010 assert(pNewSubStr != nullptr || nNewSubStrLen == 0);
1011 assert(nNewSubStrLen >= 0);
1012 /* Append? */
1013 if ( nIndex >= pStr->length )
1014 return newConcat(ppThis, pStr, pNewSubStr, nNewSubStrLen);
1016 /* not more than the String length could be deleted */
1017 if ( nCount >= pStr->length-nIndex )
1019 /* Assign of NewSubStr? */
1020 if (nIndex == 0)
1021 return newFromStr_WithLength( ppThis, pNewSubStr, nNewSubStrLen );
1023 nCount = pStr->length - nIndex;
1026 /* Assign of Str? */
1027 if ( !nCount && !nNewSubStrLen )
1028 return assign(ppThis, pStr);
1030 rtl_tString* pOrg = *ppThis;
1032 /* Alloc New Buffer */
1033 *ppThis = Alloc<rtl_tString>(pStr->length - nCount + nNewSubStrLen);
1034 assert(*ppThis != nullptr);
1035 auto* pBuffer = (*ppThis)->buffer;
1036 if ( nIndex )
1038 Copy( pBuffer, pStr->buffer, nIndex );
1039 pBuffer += nIndex;
1041 if ( nNewSubStrLen )
1043 Copy( pBuffer, pNewSubStr, nNewSubStrLen );
1044 pBuffer += nNewSubStrLen;
1046 Copy( pBuffer, pStr->buffer+nIndex+nCount, pStr->length-nIndex-nCount );
1048 RTL_LOG_STRING_NEW( *ppThis );
1049 /* must be done last, if pStr or pNewSubStr == *ppThis */
1050 if ( pOrg )
1051 release( pOrg );
1054 /* ----------------------------------------------------------------------- */
1056 template <typename rtl_tString>
1057 void newReplaceStrAt(rtl_tString** ppThis, rtl_tString* pStr, sal_Int32 nIndex, sal_Int32 nCount,
1058 rtl_tString* pNewSubStr)
1060 assert(ppThis);
1061 assert(nIndex >= 0 && nIndex <= pStr->length);
1062 assert(nCount >= 0);
1063 assert(nCount <= pStr->length - nIndex);
1064 /* Append? */
1065 if (nIndex >= pStr->length)
1067 /* newConcat test, if pNewSubStr is 0 */
1068 newConcat(ppThis, pStr, pNewSubStr);
1069 return;
1072 /* not more than the String length could be deleted */
1073 if (nCount >= pStr->length-nIndex)
1075 /* Assign of NewSubStr? */
1076 if (nIndex == 0)
1078 if (!pNewSubStr)
1079 return new_(ppThis);
1080 else
1081 return assign(ppThis, pNewSubStr);
1083 nCount = pStr->length - nIndex;
1086 /* Assign of Str? */
1087 if (!nCount && (!pNewSubStr || !pNewSubStr->length))
1089 assign(ppThis, pStr);
1090 return;
1093 const auto* pNewSubStrBuf = pNewSubStr ? pNewSubStr->buffer : nullptr;
1094 const sal_Int32 nNewSubStrLength = pNewSubStr ? pNewSubStr->length : 0;
1095 newReplaceStrAt(ppThis, pStr, nIndex, nCount, pNewSubStrBuf, nNewSubStrLength);
1098 /* ----------------------------------------------------------------------- */
1100 template <typename rtl_tString, class Replacer>
1101 void newReplaceChars(rtl_tString** ppThis, rtl_tString* pStr, Replacer replacer)
1103 assert(ppThis);
1104 assert(pStr);
1106 const auto pEnd = pStr->buffer + pStr->length;
1107 auto pCharStr = std::find_if(pStr->buffer, pEnd, replacer.Applicable());
1108 if (pCharStr != pEnd)
1110 rtl_tString* pOrg = *ppThis;
1111 *ppThis = Alloc<rtl_tString>(pStr->length);
1112 assert(*ppThis != nullptr);
1113 auto* pNewCharStr = (*ppThis)->buffer;
1114 /* Copy String */
1115 const sal_Int32 nCount = pCharStr - pStr->buffer;
1116 Copy(pNewCharStr, pStr->buffer, nCount);
1117 pNewCharStr += nCount;
1118 /* replace/copy rest of the string */
1121 *pNewCharStr = replacer.Replace(*pCharStr);
1122 pNewCharStr++;
1123 pCharStr++;
1124 } while (pCharStr != pEnd);
1126 RTL_LOG_STRING_NEW(*ppThis);
1127 /* must be done last, if pStr == *ppThis */
1128 if (pOrg)
1129 release(pOrg);
1131 else
1132 assign(ppThis, pStr);
1135 /* ----------------------------------------------------------------------- */
1137 template <typename rtl_tString> void newTrim(rtl_tString** ppThis, rtl_tString* pStr)
1139 assert(pStr);
1140 const auto view = o3tl::trim(std::basic_string_view(pStr->buffer, pStr->length));
1142 if (static_cast<sal_Int32>(view.size()) == pStr->length)
1143 assign(ppThis, pStr);
1144 else
1145 newFromStr_WithLength(ppThis, view.data(), view.size());
1148 /* ----------------------------------------------------------------------- */
1150 template <typename rtl_tString>
1151 sal_Int32 getToken(rtl_tString** ppThis, rtl_tString* pStr, sal_Int32 nToken,
1152 Char_T<rtl_tString> cTok, sal_Int32 nIndex)
1154 assert(ppThis);
1155 assert(pStr);
1156 assert(nIndex <= pStr->length);
1158 // Set ppThis to an empty string and return -1 if either nToken or nIndex is
1159 // negative:
1160 if (nIndex >= 0 && nToken >= 0)
1162 const auto* pOrgCharStr = pStr->buffer;
1163 const auto* pCharStr = pOrgCharStr + nIndex;
1164 sal_Int32 nLen = pStr->length - nIndex;
1165 sal_Int32 nTokCount = 0;
1166 const auto* pCharStrStart = pCharStr;
1167 while (nLen > 0)
1169 if (*pCharStr == cTok)
1171 nTokCount++;
1173 if (nTokCount > nToken)
1174 break;
1175 if (nTokCount == nToken)
1176 pCharStrStart = pCharStr + 1;
1179 pCharStr++;
1180 nLen--;
1182 if (nTokCount >= nToken)
1184 newFromStr_WithLength(ppThis, pCharStrStart, pCharStr - pCharStrStart);
1185 if (nLen > 0)
1186 return pCharStr - pOrgCharStr + 1;
1187 else
1188 return -1;
1192 new_(ppThis);
1193 return -1;
1196 /* ======================================================================= */
1197 /* String buffer help functions */
1198 /* ======================================================================= */
1200 template <class rtl_tString>
1201 void stringbuffer_newFromStr_WithLength(rtl_tString** ppThis,
1202 const Char_T<rtl_tString>* pStr, sal_Int32 count)
1204 assert(ppThis);
1205 assert(count >= 0);
1206 if (!pStr)
1207 count = 0; // Because old code didn't care about count when !pStr
1209 newFromStr_WithLength(ppThis, pStr, count, 16);
1212 template <class rtl_tString>
1213 sal_Int32 stringbuffer_newFromStringBuffer(rtl_tString** ppThis, sal_Int32 capacity,
1214 rtl_tString* pStr)
1216 assert(capacity >= 0);
1217 assert(pStr);
1219 if (capacity < pStr->length)
1220 capacity = pStr->length;
1222 newFromStr_WithLength(ppThis, pStr->buffer, pStr->length, capacity - pStr->length);
1223 return capacity;
1226 template <class rtl_tString>
1227 void stringbuffer_ensureCapacity(rtl_tString** ppThis, sal_Int32* capacity,
1228 sal_Int32 minimumCapacity)
1230 assert(ppThis);
1231 assert(capacity && *capacity >= 0);
1232 // assert(minimumCapacity >= 0); // It was commented out in rtl_stringbuffer_ensureCapacity
1233 if (minimumCapacity <= *capacity)
1234 return;
1236 const auto nLength = (*ppThis)->length;
1237 *capacity = (nLength + 1) * 2;
1238 if (minimumCapacity > *capacity)
1239 *capacity = minimumCapacity;
1241 newFromStr_WithLength(ppThis, (*ppThis)->buffer, nLength, *capacity - nLength);
1244 template <class rtl_tString, typename C>
1245 void stringbuffer_insert(rtl_tString** ppThis, sal_Int32* capacity, sal_Int32 offset,
1246 const C* pStr, sal_Int32 len)
1248 assert(ppThis);
1249 assert(capacity && *capacity >= 0);
1250 assert(offset >= 0 && offset <= (*ppThis)->length);
1251 assert(len >= 0);
1252 if (len == 0)
1253 return;
1254 if (len > std::numeric_limits<sal_Int32>::max() - (*ppThis)->length) {
1255 throw std::bad_alloc();
1258 stringbuffer_ensureCapacity(ppThis, capacity, (*ppThis)->length + len);
1260 sal_Int32 nOldLen = (*ppThis)->length;
1261 auto* pBuf = (*ppThis)->buffer;
1263 /* copy the tail */
1264 const sal_Int32 n = nOldLen - offset;
1265 if (n > 0)
1266 CopyBackward(pBuf + offset + len, pBuf + offset, n);
1268 /* insert the new characters */
1269 if (pStr != nullptr)
1270 Copy(pBuf + offset, pStr, len);
1272 (*ppThis)->length = nOldLen + len;
1273 pBuf[nOldLen + len] = 0;
1276 template <class rtl_tString>
1277 void stringbuffer_remove(rtl_tString** ppThis, sal_Int32 start, sal_Int32 len)
1279 assert(ppThis);
1280 assert(start >= 0 && start <= (*ppThis)->length);
1281 assert(len >= 0);
1283 if (len > (*ppThis)->length - start)
1284 len = (*ppThis)->length - start;
1286 //remove nothing
1287 if (!len)
1288 return;
1290 auto* pBuf = (*ppThis)->buffer;
1291 const sal_Int32 nTailLen = (*ppThis)->length - (start + len);
1293 if (nTailLen)
1295 /* move the tail */
1296 Copy(pBuf + start, pBuf + start + len, nTailLen);
1299 (*ppThis)->length -= len;
1300 pBuf[(*ppThis)->length] = 0;
1303 template <class S, typename CharTypeFrom, typename CharTypeTo>
1304 void newReplaceAllFromIndex(S** s, S* s1, CharTypeFrom const* from, sal_Int32 fromLength,
1305 CharTypeTo const* to, sal_Int32 toLength, sal_Int32 fromIndex)
1307 assert(s != nullptr);
1308 assert(s1 != nullptr);
1309 assert(fromLength >= 0);
1310 assert(from != nullptr || fromLength == 0);
1311 assert(toLength >= 0);
1312 assert(to != nullptr || toLength == 0);
1313 assert(fromIndex >= 0 && fromIndex <= s1->length);
1314 sal_Int32 i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex,
1315 from, fromLength);
1316 if (i >= 0)
1318 if (s1->length - fromLength > SAL_MAX_INT32 - toLength)
1319 std::abort();
1320 i += fromIndex;
1321 sal_Int32 nCapacity = s1->length + (toLength - fromLength);
1322 if (fromLength < toLength)
1324 // Pre-allocate up to 16 replacements more
1325 const sal_Int32 nMaxMoreFinds = (s1->length - i - fromLength) / fromLength;
1326 const sal_Int32 nIncrease = toLength - fromLength;
1327 const sal_Int32 nMoreReplacements = std::min(
1328 { nMaxMoreFinds, (SAL_MAX_INT32 - nCapacity) / nIncrease, sal_Int32(16) });
1329 nCapacity += nMoreReplacements * nIncrease;
1331 const auto pOld = *s;
1332 *s = Alloc<S>(nCapacity);
1333 (*s)->length = 0;
1334 fromIndex = 0;
1337 stringbuffer_insert(s, &nCapacity, (*s)->length, s1->buffer + fromIndex, i);
1338 stringbuffer_insert(s, &nCapacity, (*s)->length, to, toLength);
1339 fromIndex += i + fromLength;
1340 i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex,
1341 from, fromLength);
1342 } while (i >= 0);
1343 // the rest
1344 stringbuffer_insert(s, &nCapacity, (*s)->length,
1345 s1->buffer + fromIndex, s1->length - fromIndex);
1346 if (pOld)
1347 release(pOld); // Must be last in case *s == s1
1349 else
1350 assign(s, s1);
1352 RTL_LOG_STRING_NEW(*s);
1355 template <class rtl_tString, typename C1, typename C2>
1356 void newReplaceFirst(rtl_tString** s, rtl_tString* s1, C1 const* from, sal_Int32 fromLength,
1357 C2 const* to, sal_Int32 toLength, sal_Int32& fromIndex)
1359 assert(s != nullptr);
1360 assert(s1 != nullptr);
1361 assert(fromLength >= 0);
1362 assert(from != nullptr || fromLength == 0);
1363 assert(toLength >= 0);
1364 assert(to != nullptr || toLength == 0);
1365 assert(fromIndex >= 0 && fromIndex <= s1->length);
1366 sal_Int32 i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex,
1367 from, fromLength);
1368 if (i >= 0)
1370 if (s1->length - fromLength > SAL_MAX_INT32 - toLength)
1371 std::abort();
1372 i += fromIndex;
1373 newReplaceStrAt(s, s1, i, fromLength, to, toLength);
1375 else
1376 assign(s, s1);
1378 fromIndex = i;
1381 // doubleToString implementation
1383 static inline constexpr sal_uInt64 eX[] = { 10ull,
1384 100ull,
1385 1000ull,
1386 10000ull,
1387 100000ull,
1388 1000000ull,
1389 10000000ull,
1390 100000000ull,
1391 1000000000ull,
1392 10000000000ull,
1393 100000000000ull,
1394 1000000000000ull,
1395 10000000000000ull,
1396 100000000000000ull,
1397 1000000000000000ull,
1398 10000000000000000ull,
1399 100000000000000000ull,
1400 1000000000000000000ull,
1401 10000000000000000000ull };
1403 template <typename rtl_tString>
1404 void doubleToString(rtl_tString** pResult, sal_Int32* pResultCapacity, sal_Int32 nResultOffset,
1405 double fValue, rtl_math_StringFormat eFormat, sal_Int32 nDecPlaces,
1406 Char_T<rtl_tString> cDecSeparator, sal_Int32 const* pGroups,
1407 Char_T<rtl_tString> cGroupSeparator, bool bEraseTrailingDecZeros)
1409 auto decimalDigits = [](sal_uInt64 n) {
1410 return std::distance(std::begin(eX), std::upper_bound(std::begin(eX), std::end(eX), n)) + 1;
1413 auto roundToPow10 = [](sal_uInt64 n, int e) {
1414 assert(e > 0 && o3tl::make_unsigned(e) <= std::size(eX));
1415 const sal_uInt64 d = eX[e - 1];
1416 return (n + d / 2) / d * d;
1419 auto append = [](rtl_tString** s, sal_Int32* pCapacity, sal_Int32 rOffset, auto sv)
1421 if (!pCapacity)
1422 newFromStr_WithLength(s, sv.data(), sv.size());
1423 else
1424 stringbuffer_insert(s, pCapacity, rOffset, sv.data(), sv.size());
1427 if (std::isnan(fValue))
1429 // #i112652# XMLSchema-2
1430 constexpr std::string_view nan{ "NaN" };
1431 return append(pResult, pResultCapacity, nResultOffset, nan);
1434 // sign adjustment, instead of testing for fValue<0.0 this will also fetch -0.0
1435 bool bSign = std::signbit(fValue);
1437 if (std::isinf(fValue))
1439 // #i112652# XMLSchema-2
1440 std::string_view inf = bSign ? std::string_view("-INF") : std::string_view("INF");
1441 return append(pResult, pResultCapacity, nResultOffset, inf);
1444 if (bSign)
1445 fValue = -fValue;
1447 decltype(jkj::dragonbox::to_decimal(fValue, jkj::dragonbox::policy::sign::ignore,
1448 jkj::dragonbox::policy::trailing_zero::ignore)) aParts{};
1449 if (fValue) // to_decimal is documented to only handle non-zero finite numbers
1450 aParts = jkj::dragonbox::to_decimal(fValue, jkj::dragonbox::policy::sign::ignore,
1451 jkj::dragonbox::policy::trailing_zero::ignore);
1453 int nOrigDigits = decimalDigits(aParts.significand);
1454 int nExp = nOrigDigits + aParts.exponent - 1;
1455 int nRoundDigits = 15;
1457 // Unfortunately the old rounding below writes 1.79769313486232e+308 for
1458 // DBL_MAX and 4 subsequent nextafter(...,0).
1459 static const double fB1 = std::nextafter(std::numeric_limits<double>::max(), 0);
1460 static const double fB2 = std::nextafter(fB1, 0);
1461 static const double fB3 = std::nextafter(fB2, 0);
1462 static const double fB4 = std::nextafter(fB3, 0);
1463 if ((fValue >= fB4) && eFormat != rtl_math_StringFormat_F)
1465 // 1.7976931348623157e+308 instead of rounded 1.79769313486232e+308
1466 // that can't be converted back as out of range. For rounded values if
1467 // they exceed range they should not be written to exchange strings or
1468 // file formats.
1470 eFormat = rtl_math_StringFormat_E;
1471 nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, 0, 16);
1472 nRoundDigits = 17;
1475 // Use integer representation for integer values that fit into the
1476 // mantissa (1.((2^53)-1)) with a precision of 1 for highest accuracy.
1477 if ((eFormat == rtl_math_StringFormat_Automatic || eFormat == rtl_math_StringFormat_F)
1478 && aParts.exponent >= 0 && fValue < 0x1p53)
1480 eFormat = rtl_math_StringFormat_F;
1481 if (nDecPlaces == rtl_math_DecimalPlaces_Max)
1482 nDecPlaces = 0;
1483 else
1484 nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, -15, 15);
1486 if (bEraseTrailingDecZeros && nDecPlaces > 0)
1487 nDecPlaces = 0;
1489 nRoundDigits = nOrigDigits; // no rounding
1492 switch (eFormat)
1494 case rtl_math_StringFormat_Automatic:
1495 // E or F depending on exponent magnitude
1496 if (nExp <= -15 || nExp >= 15)
1498 if (nDecPlaces == rtl_math_DecimalPlaces_Max)
1499 nDecPlaces = 14;
1500 eFormat = rtl_math_StringFormat_E;
1502 else
1504 if (nDecPlaces == rtl_math_DecimalPlaces_Max)
1505 nDecPlaces = (nExp < 14) ? 15 - nExp - 1 : 15;
1506 eFormat = rtl_math_StringFormat_F;
1508 break;
1510 case rtl_math_StringFormat_G:
1511 case rtl_math_StringFormat_G1:
1512 case rtl_math_StringFormat_G2:
1513 // G-Point, similar to sprintf %G
1514 if (nDecPlaces == rtl_math_DecimalPlaces_DefaultSignificance)
1515 nDecPlaces = 6;
1517 if (nExp < -4 || nExp >= nDecPlaces)
1519 nDecPlaces = std::max<sal_Int32>(1, nDecPlaces - 1);
1521 if (eFormat == rtl_math_StringFormat_G)
1522 eFormat = rtl_math_StringFormat_E;
1523 else if (eFormat == rtl_math_StringFormat_G2)
1524 eFormat = rtl_math_StringFormat_E2;
1525 else if (eFormat == rtl_math_StringFormat_G1)
1526 eFormat = rtl_math_StringFormat_E1;
1528 else
1530 if (nOrigDigits <= nDecPlaces && aParts.exponent >= 0 && fValue < 0x1p53)
1532 // Use integer representation with highest accuracy.
1533 nRoundDigits = nOrigDigits; // no rounding
1535 nDecPlaces = std::max<sal_Int32>(0, nDecPlaces - nExp - 1);
1536 eFormat = rtl_math_StringFormat_F;
1538 break;
1540 default:
1541 break;
1544 // Too large values for nDecPlaces make no sense; it might also be
1545 // rtl_math_DecimalPlaces_Max was passed with rtl_math_StringFormat_F or
1546 // others, but we don't want to allocate/deallocate 2GB just to fill it
1547 // with trailing '0' characters..
1548 nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, -309, 309);
1550 sal_Int32 nDigits = nDecPlaces + 1;
1552 if (eFormat == rtl_math_StringFormat_F)
1553 nDigits += nExp;
1555 // Round the number
1556 nRoundDigits = std::min<int>(nDigits, nRoundDigits);
1557 if (nDigits >= 0 && nOrigDigits > nRoundDigits)
1559 aParts.significand = roundToPow10(aParts.significand, nOrigDigits - nRoundDigits);
1560 assert(aParts.significand <= eX[nOrigDigits - 1]);
1561 if (aParts.significand == eX[nOrigDigits - 1]) // up-rounding to the next decade
1563 nOrigDigits++;
1564 nExp++;
1566 if (eFormat == rtl_math_StringFormat_F)
1567 nDigits++;
1571 sal_Int32 nBuf
1572 = (nDigits <= 0 ? std::max<sal_Int32>(nDecPlaces, std::abs(nExp)) : nDigits + nDecPlaces)
1573 + 10 + (pGroups ? std::abs(nDigits) * 2 : 0);
1574 // max(nDigits) = max(nDecPlaces) + 1 + max(nExp) + 1 = 309 + 1 + 308 + 1 = 619
1575 // max(nBuf) = max(nDigits) + max(nDecPlaces) + 10 + max(nDigits) * 2 = 619 * 3 + 309 + 10 = 2176
1576 assert(nBuf <= 2176);
1577 auto* const pBuf = static_cast<Char_T<rtl_tString>*>(alloca(nBuf * sizeof(Char_T<rtl_tString>)));
1578 auto* p = pBuf;
1579 if (bSign)
1580 *p++ = '-';
1582 bool bHasDec = false;
1584 int nDecPos;
1585 // Check for F format and number < 1
1586 if (eFormat == rtl_math_StringFormat_F)
1588 if (nExp < 0)
1590 *p++ = '0';
1591 if (nDecPlaces > 0)
1593 *p++ = cDecSeparator;
1594 bHasDec = true;
1597 sal_Int32 i = (nDigits <= 0 ? nDecPlaces : -nExp - 1);
1599 while ((i--) > 0)
1600 *p++ = '0';
1602 nDecPos = 0;
1604 else
1605 nDecPos = nExp + 1;
1607 else
1608 nDecPos = 1;
1610 int nGrouping = 0, nGroupSelector = 0, nGroupExceed = 0;
1611 if (nDecPos > 1 && pGroups && pGroups[0] && cGroupSeparator)
1613 while (nGrouping + pGroups[nGroupSelector] < nDecPos)
1615 nGrouping += pGroups[nGroupSelector];
1616 if (pGroups[nGroupSelector + 1])
1618 if (nGrouping + pGroups[nGroupSelector + 1] >= nDecPos)
1619 break; // while
1621 ++nGroupSelector;
1623 else if (!nGroupExceed)
1624 nGroupExceed = nGrouping;
1628 // print the number
1629 if (nDigits > 0)
1631 for (int nCurExp = nOrigDigits - 1;;)
1633 int nDigit;
1634 if (aParts.significand > 0 && nCurExp > 0)
1636 --nCurExp;
1637 nDigit = aParts.significand / eX[nCurExp];
1638 aParts.significand %= eX[nCurExp];
1640 else
1642 nDigit = aParts.significand;
1643 aParts.significand = 0;
1645 assert(nDigit >= 0 && nDigit < 10);
1646 *p++ = nDigit + '0';
1648 if (!--nDigits)
1649 break; // for
1651 if (nDecPos)
1653 if (!--nDecPos)
1655 *p++ = cDecSeparator;
1656 bHasDec = true;
1658 else if (nDecPos == nGrouping)
1660 *p++ = cGroupSeparator;
1661 nGrouping -= pGroups[nGroupSelector];
1663 if (nGroupSelector && nGrouping < nGroupExceed)
1664 --nGroupSelector;
1670 if (!bHasDec && eFormat == rtl_math_StringFormat_F)
1671 { // nDecPlaces < 0 did round the value
1672 while (--nDecPos > 0)
1673 { // fill before decimal point
1674 if (nDecPos == nGrouping)
1676 *p++ = cGroupSeparator;
1677 nGrouping -= pGroups[nGroupSelector];
1679 if (nGroupSelector && nGrouping < nGroupExceed)
1680 --nGroupSelector;
1683 *p++ = '0';
1687 if (bEraseTrailingDecZeros && bHasDec)
1689 while (*(p - 1) == '0')
1690 p--;
1692 if (*(p - 1) == cDecSeparator)
1693 p--;
1696 // Print the exponent ('E', followed by '+' or '-', followed by exactly
1697 // three digits for rtl_math_StringFormat_E). The code in
1698 // rtl_[u]str_valueOf{Float|Double} relies on this format.
1699 if (eFormat == rtl_math_StringFormat_E || eFormat == rtl_math_StringFormat_E2
1700 || eFormat == rtl_math_StringFormat_E1)
1702 if (p == pBuf)
1703 *p++ = '1';
1704 // maybe no nDigits if nDecPlaces < 0
1706 *p++ = 'E';
1707 if (nExp < 0)
1709 nExp = -nExp;
1710 *p++ = '-';
1712 else
1713 *p++ = '+';
1715 if (eFormat == rtl_math_StringFormat_E || nExp >= 100)
1716 *p++ = nExp / 100 + '0';
1718 nExp %= 100;
1720 if (eFormat == rtl_math_StringFormat_E || eFormat == rtl_math_StringFormat_E2 || nExp >= 10)
1721 *p++ = nExp / 10 + '0';
1723 *p++ = nExp % 10 + '0';
1726 append(pResult, pResultCapacity, nResultOffset, std::basic_string_view(pBuf, p - pBuf));
1729 template <sal_Int32 maxLen, typename C, typename T> sal_Int32 SAL_CALL valueOfFP(C* pStr, T f)
1731 assert(pStr);
1732 rtl_String* pResult = nullptr;
1733 doubleToString(&pResult, nullptr, 0, f, rtl_math_StringFormat_G,
1734 maxLen - std::size("-x.E-xxx") + 1, '.', nullptr, 0, true);
1735 const sal_Int32 nLen = pResult->length;
1736 assert(nLen < maxLen);
1737 Copy(pStr, pResult->buffer, nLen + 1);
1738 release(pResult);
1739 return nLen;
1744 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */