svx: prefix members of SdrObjEditView
[LibreOffice.git] / sal / rtl / strtmpl.hxx
blob154e553ad2d4cc208db59feb5c26f2467204a399
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 const auto* pNewSubStrBuf = pNewSubStr ? pNewSubStr->buffer : nullptr;
1087 const sal_Int32 nNewSubStrLength = pNewSubStr ? pNewSubStr->length : 0;
1088 newReplaceStrAt(ppThis, pStr, nIndex, nCount, pNewSubStrBuf, nNewSubStrLength);
1091 /* ----------------------------------------------------------------------- */
1093 template <typename rtl_tString, class Replacer>
1094 void newReplaceChars(rtl_tString** ppThis, rtl_tString* pStr, Replacer replacer)
1096 assert(ppThis);
1097 assert(pStr);
1099 const auto pEnd = pStr->buffer + pStr->length;
1100 auto pCharStr = std::find_if(pStr->buffer, pEnd, replacer.Applicable());
1101 if (pCharStr != pEnd)
1103 rtl_tString* pOrg = *ppThis;
1104 *ppThis = Alloc<rtl_tString>(pStr->length);
1105 assert(*ppThis != nullptr);
1106 auto* pNewCharStr = (*ppThis)->buffer;
1107 /* Copy String */
1108 const sal_Int32 nCount = pCharStr - pStr->buffer;
1109 Copy(pNewCharStr, pStr->buffer, nCount);
1110 pNewCharStr += nCount;
1111 /* replace/copy rest of the string */
1114 *pNewCharStr = replacer.Replace(*pCharStr);
1115 pNewCharStr++;
1116 pCharStr++;
1117 } while (pCharStr != pEnd);
1119 RTL_LOG_STRING_NEW(*ppThis);
1120 /* must be done last, if pStr == *ppThis */
1121 if (pOrg)
1122 release(pOrg);
1124 else
1125 assign(ppThis, pStr);
1128 /* ----------------------------------------------------------------------- */
1130 template <typename rtl_tString> void newTrim(rtl_tString** ppThis, rtl_tString* pStr)
1132 assert(pStr);
1133 const auto view = o3tl::trim(std::basic_string_view(pStr->buffer, pStr->length));
1135 if (static_cast<sal_Int32>(view.size()) == pStr->length)
1136 assign(ppThis, pStr);
1137 else
1138 newFromStr_WithLength(ppThis, view.data(), view.size());
1141 /* ----------------------------------------------------------------------- */
1143 template <typename rtl_tString>
1144 sal_Int32 getToken(rtl_tString** ppThis, rtl_tString* pStr, sal_Int32 nToken,
1145 Char_T<rtl_tString> cTok, sal_Int32 nIndex)
1147 assert(ppThis);
1148 assert(pStr);
1149 assert(nIndex <= pStr->length);
1151 // Set ppThis to an empty string and return -1 if either nToken or nIndex is
1152 // negative:
1153 if (nIndex >= 0 && nToken >= 0)
1155 const auto* pOrgCharStr = pStr->buffer;
1156 const auto* pCharStr = pOrgCharStr + nIndex;
1157 sal_Int32 nLen = pStr->length - nIndex;
1158 sal_Int32 nTokCount = 0;
1159 const auto* pCharStrStart = pCharStr;
1160 while (nLen > 0)
1162 if (*pCharStr == cTok)
1164 nTokCount++;
1166 if (nTokCount > nToken)
1167 break;
1168 if (nTokCount == nToken)
1169 pCharStrStart = pCharStr + 1;
1172 pCharStr++;
1173 nLen--;
1175 if (nTokCount >= nToken)
1177 newFromStr_WithLength(ppThis, pCharStrStart, pCharStr - pCharStrStart);
1178 if (nLen > 0)
1179 return pCharStr - pOrgCharStr + 1;
1180 else
1181 return -1;
1185 new_(ppThis);
1186 return -1;
1189 /* ======================================================================= */
1190 /* String buffer help functions */
1191 /* ======================================================================= */
1193 template <class rtl_tString>
1194 void stringbuffer_newFromStr_WithLength(rtl_tString** ppThis,
1195 const Char_T<rtl_tString>* pStr, sal_Int32 count)
1197 assert(ppThis);
1198 assert(count >= 0);
1199 if (!pStr)
1200 count = 0; // Because old code didn't care about count when !pStr
1202 newFromStr_WithLength(ppThis, pStr, count, 16);
1205 template <class rtl_tString>
1206 sal_Int32 stringbuffer_newFromStringBuffer(rtl_tString** ppThis, sal_Int32 capacity,
1207 rtl_tString* pStr)
1209 assert(capacity >= 0);
1210 assert(pStr);
1212 if (capacity < pStr->length)
1213 capacity = pStr->length;
1215 newFromStr_WithLength(ppThis, pStr->buffer, pStr->length, capacity - pStr->length);
1216 return capacity;
1219 template <class rtl_tString>
1220 void stringbuffer_ensureCapacity(rtl_tString** ppThis, sal_Int32* capacity,
1221 sal_Int32 minimumCapacity)
1223 assert(ppThis);
1224 assert(capacity && *capacity >= 0);
1225 // assert(minimumCapacity >= 0); // It was commented out in rtl_stringbuffer_ensureCapacity
1226 if (minimumCapacity <= *capacity)
1227 return;
1229 const auto nLength = (*ppThis)->length;
1230 *capacity = (nLength + 1) * 2;
1231 if (minimumCapacity > *capacity)
1232 *capacity = minimumCapacity;
1234 newFromStr_WithLength(ppThis, (*ppThis)->buffer, nLength, *capacity - nLength);
1237 template <class rtl_tString, typename C>
1238 void stringbuffer_insert(rtl_tString** ppThis, sal_Int32* capacity, sal_Int32 offset,
1239 const C* pStr, sal_Int32 len)
1241 assert(ppThis);
1242 assert(capacity && *capacity >= 0);
1243 assert(offset >= 0 && offset <= (*ppThis)->length);
1244 assert(len >= 0);
1245 if (len == 0)
1246 return;
1247 if (len > std::numeric_limits<sal_Int32>::max() - (*ppThis)->length) {
1248 throw std::bad_alloc();
1251 stringbuffer_ensureCapacity(ppThis, capacity, (*ppThis)->length + len);
1253 sal_Int32 nOldLen = (*ppThis)->length;
1254 auto* pBuf = (*ppThis)->buffer;
1256 /* copy the tail */
1257 const sal_Int32 n = nOldLen - offset;
1258 if (n > 0)
1259 CopyBackward(pBuf + offset + len, pBuf + offset, n);
1261 /* insert the new characters */
1262 if (pStr != nullptr)
1263 Copy(pBuf + offset, pStr, len);
1265 (*ppThis)->length = nOldLen + len;
1266 pBuf[nOldLen + len] = 0;
1269 template <class rtl_tString>
1270 void stringbuffer_remove(rtl_tString** ppThis, sal_Int32 start, sal_Int32 len)
1272 assert(ppThis);
1273 assert(start >= 0 && start <= (*ppThis)->length);
1274 assert(len >= 0);
1276 if (len > (*ppThis)->length - start)
1277 len = (*ppThis)->length - start;
1279 //remove nothing
1280 if (!len)
1281 return;
1283 auto* pBuf = (*ppThis)->buffer;
1284 const sal_Int32 nTailLen = (*ppThis)->length - (start + len);
1286 if (nTailLen)
1288 /* move the tail */
1289 Copy(pBuf + start, pBuf + start + len, nTailLen);
1292 (*ppThis)->length -= len;
1293 pBuf[(*ppThis)->length] = 0;
1296 template <class S, typename CharTypeFrom, typename CharTypeTo>
1297 void newReplaceAllFromIndex(S** s, S* s1, CharTypeFrom const* from, sal_Int32 fromLength,
1298 CharTypeTo const* to, sal_Int32 toLength, sal_Int32 fromIndex)
1300 assert(s != nullptr);
1301 assert(s1 != nullptr);
1302 assert(fromLength >= 0);
1303 assert(from != nullptr || fromLength == 0);
1304 assert(toLength >= 0);
1305 assert(to != nullptr || toLength == 0);
1306 assert(fromIndex >= 0 && fromIndex <= s1->length);
1307 sal_Int32 i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex,
1308 from, fromLength);
1309 if (i >= 0)
1311 if (s1->length - fromLength > SAL_MAX_INT32 - toLength)
1312 std::abort();
1313 i += fromIndex;
1314 sal_Int32 nCapacity = s1->length + (toLength - fromLength);
1315 if (fromLength < toLength)
1317 // Pre-allocate up to 16 replacements more
1318 const sal_Int32 nMaxMoreFinds = (s1->length - i - fromLength) / fromLength;
1319 const sal_Int32 nIncrease = toLength - fromLength;
1320 const sal_Int32 nMoreReplacements = std::min(
1321 { nMaxMoreFinds, (SAL_MAX_INT32 - nCapacity) / nIncrease, sal_Int32(16) });
1322 nCapacity += nMoreReplacements * nIncrease;
1324 const auto pOld = *s;
1325 *s = Alloc<S>(nCapacity);
1326 (*s)->length = 0;
1327 fromIndex = 0;
1330 stringbuffer_insert(s, &nCapacity, (*s)->length, s1->buffer + fromIndex, i);
1331 stringbuffer_insert(s, &nCapacity, (*s)->length, to, toLength);
1332 fromIndex += i + fromLength;
1333 i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex,
1334 from, fromLength);
1335 } while (i >= 0);
1336 // the rest
1337 stringbuffer_insert(s, &nCapacity, (*s)->length,
1338 s1->buffer + fromIndex, s1->length - fromIndex);
1339 if (pOld)
1340 release(pOld); // Must be last in case *s == s1
1342 else
1343 assign(s, s1);
1345 RTL_LOG_STRING_NEW(*s);
1348 template <class rtl_tString, typename C1, typename C2>
1349 void newReplaceFirst(rtl_tString** s, rtl_tString* s1, C1 const* from, sal_Int32 fromLength,
1350 C2 const* to, sal_Int32 toLength, sal_Int32& fromIndex)
1352 assert(s != nullptr);
1353 assert(s1 != nullptr);
1354 assert(fromLength >= 0);
1355 assert(from != nullptr || fromLength == 0);
1356 assert(toLength >= 0);
1357 assert(to != nullptr || toLength == 0);
1358 assert(fromIndex >= 0 && fromIndex <= s1->length);
1359 sal_Int32 i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex,
1360 from, fromLength);
1361 if (i >= 0)
1363 if (s1->length - fromLength > SAL_MAX_INT32 - toLength)
1364 std::abort();
1365 i += fromIndex;
1366 newReplaceStrAt(s, s1, i, fromLength, to, toLength);
1368 else
1369 assign(s, s1);
1371 fromIndex = i;
1374 // doubleToString implementation
1376 static inline constexpr sal_uInt64 eX[] = { 10ull,
1377 100ull,
1378 1000ull,
1379 10000ull,
1380 100000ull,
1381 1000000ull,
1382 10000000ull,
1383 100000000ull,
1384 1000000000ull,
1385 10000000000ull,
1386 100000000000ull,
1387 1000000000000ull,
1388 10000000000000ull,
1389 100000000000000ull,
1390 1000000000000000ull,
1391 10000000000000000ull,
1392 100000000000000000ull,
1393 1000000000000000000ull,
1394 10000000000000000000ull };
1396 template <typename rtl_tString>
1397 void doubleToString(rtl_tString** pResult, sal_Int32* pResultCapacity, sal_Int32 nResultOffset,
1398 double fValue, rtl_math_StringFormat eFormat, sal_Int32 nDecPlaces,
1399 Char_T<rtl_tString> cDecSeparator, sal_Int32 const* pGroups,
1400 Char_T<rtl_tString> cGroupSeparator, bool bEraseTrailingDecZeros)
1402 auto decimalDigits = [](sal_uInt64 n) {
1403 return std::distance(std::begin(eX), std::upper_bound(std::begin(eX), std::end(eX), n)) + 1;
1406 auto roundToPow10 = [](sal_uInt64 n, int e) {
1407 assert(e > 0 && o3tl::make_unsigned(e) <= std::size(eX));
1408 const sal_uInt64 d = eX[e - 1];
1409 return (n + d / 2) / d * d;
1412 auto append = [](rtl_tString** s, sal_Int32* pCapacity, sal_Int32 rOffset, auto sv)
1414 if (!pCapacity)
1415 newFromStr_WithLength(s, sv.data(), sv.size());
1416 else
1417 stringbuffer_insert(s, pCapacity, rOffset, sv.data(), sv.size());
1420 if (std::isnan(fValue))
1422 // #i112652# XMLSchema-2
1423 constexpr std::string_view nan{ "NaN" };
1424 return append(pResult, pResultCapacity, nResultOffset, nan);
1427 // sign adjustment, instead of testing for fValue<0.0 this will also fetch -0.0
1428 bool bSign = std::signbit(fValue);
1430 if (std::isinf(fValue))
1432 // #i112652# XMLSchema-2
1433 std::string_view inf = bSign ? std::string_view("-INF") : std::string_view("INF");
1434 return append(pResult, pResultCapacity, nResultOffset, inf);
1437 if (bSign)
1438 fValue = -fValue;
1440 decltype(jkj::dragonbox::to_decimal(fValue, jkj::dragonbox::policy::sign::ignore,
1441 jkj::dragonbox::policy::trailing_zero::ignore)) aParts{};
1442 if (fValue) // to_decimal is documented to only handle non-zero finite numbers
1443 aParts = jkj::dragonbox::to_decimal(fValue, jkj::dragonbox::policy::sign::ignore,
1444 jkj::dragonbox::policy::trailing_zero::ignore);
1446 int nOrigDigits = decimalDigits(aParts.significand);
1447 int nExp = nOrigDigits + aParts.exponent - 1;
1448 int nRoundDigits = 15;
1450 // Unfortunately the old rounding below writes 1.79769313486232e+308 for
1451 // DBL_MAX and 4 subsequent nextafter(...,0).
1452 static const double fB1 = std::nextafter(std::numeric_limits<double>::max(), 0);
1453 static const double fB2 = std::nextafter(fB1, 0);
1454 static const double fB3 = std::nextafter(fB2, 0);
1455 static const double fB4 = std::nextafter(fB3, 0);
1456 if ((fValue >= fB4) && eFormat != rtl_math_StringFormat_F)
1458 // 1.7976931348623157e+308 instead of rounded 1.79769313486232e+308
1459 // that can't be converted back as out of range. For rounded values if
1460 // they exceed range they should not be written to exchange strings or
1461 // file formats.
1463 eFormat = rtl_math_StringFormat_E;
1464 nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, 0, 16);
1465 nRoundDigits = 17;
1468 // Use integer representation for integer values that fit into the
1469 // mantissa (1.((2^53)-1)) with a precision of 1 for highest accuracy.
1470 if ((eFormat == rtl_math_StringFormat_Automatic || eFormat == rtl_math_StringFormat_F)
1471 && aParts.exponent >= 0 && fValue < 0x1p53)
1473 eFormat = rtl_math_StringFormat_F;
1474 if (nDecPlaces == rtl_math_DecimalPlaces_Max)
1475 nDecPlaces = 0;
1476 else
1477 nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, -15, 15);
1479 if (bEraseTrailingDecZeros && nDecPlaces > 0)
1480 nDecPlaces = 0;
1482 nRoundDigits = nOrigDigits; // no rounding
1485 switch (eFormat)
1487 case rtl_math_StringFormat_Automatic:
1488 // E or F depending on exponent magnitude
1489 if (nExp <= -15 || nExp >= 15)
1491 if (nDecPlaces == rtl_math_DecimalPlaces_Max)
1492 nDecPlaces = 14;
1493 eFormat = rtl_math_StringFormat_E;
1495 else
1497 if (nDecPlaces == rtl_math_DecimalPlaces_Max)
1498 nDecPlaces = (nExp < 14) ? 15 - nExp - 1 : 15;
1499 eFormat = rtl_math_StringFormat_F;
1501 break;
1503 case rtl_math_StringFormat_G:
1504 case rtl_math_StringFormat_G1:
1505 case rtl_math_StringFormat_G2:
1506 // G-Point, similar to sprintf %G
1507 if (nDecPlaces == rtl_math_DecimalPlaces_DefaultSignificance)
1508 nDecPlaces = 6;
1510 if (nExp < -4 || nExp >= nDecPlaces)
1512 nDecPlaces = std::max<sal_Int32>(1, nDecPlaces - 1);
1514 if (eFormat == rtl_math_StringFormat_G)
1515 eFormat = rtl_math_StringFormat_E;
1516 else if (eFormat == rtl_math_StringFormat_G2)
1517 eFormat = rtl_math_StringFormat_E2;
1518 else if (eFormat == rtl_math_StringFormat_G1)
1519 eFormat = rtl_math_StringFormat_E1;
1521 else
1523 if (nOrigDigits <= nDecPlaces && aParts.exponent >= 0 && fValue < 0x1p53)
1525 // Use integer representation with highest accuracy.
1526 nRoundDigits = nOrigDigits; // no rounding
1528 nDecPlaces = std::max<sal_Int32>(0, nDecPlaces - nExp - 1);
1529 eFormat = rtl_math_StringFormat_F;
1531 break;
1533 default:
1534 break;
1537 // Too large values for nDecPlaces make no sense; it might also be
1538 // rtl_math_DecimalPlaces_Max was passed with rtl_math_StringFormat_F or
1539 // others, but we don't want to allocate/deallocate 2GB just to fill it
1540 // with trailing '0' characters..
1541 nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, -309, 309);
1543 sal_Int32 nDigits = nDecPlaces + 1;
1545 if (eFormat == rtl_math_StringFormat_F)
1546 nDigits += nExp;
1548 // Round the number
1549 nRoundDigits = std::min<int>(nDigits, nRoundDigits);
1550 if (nDigits >= 0 && nOrigDigits > nRoundDigits)
1552 aParts.significand = roundToPow10(aParts.significand, nOrigDigits - nRoundDigits);
1553 assert(aParts.significand <= eX[nOrigDigits - 1]);
1554 if (aParts.significand == eX[nOrigDigits - 1]) // up-rounding to the next decade
1556 nOrigDigits++;
1557 nExp++;
1559 if (eFormat == rtl_math_StringFormat_F)
1560 nDigits++;
1564 sal_Int32 nBuf
1565 = (nDigits <= 0 ? std::max<sal_Int32>(nDecPlaces, std::abs(nExp)) : nDigits + nDecPlaces)
1566 + 10 + (pGroups ? std::abs(nDigits) * 2 : 0);
1567 // max(nDigits) = max(nDecPlaces) + 1 + max(nExp) + 1 = 309 + 1 + 308 + 1 = 619
1568 // max(nBuf) = max(nDigits) + max(nDecPlaces) + 10 + max(nDigits) * 2 = 619 * 3 + 309 + 10 = 2176
1569 assert(nBuf <= 2176);
1570 auto* const pBuf = static_cast<Char_T<rtl_tString>*>(alloca(nBuf * sizeof(Char_T<rtl_tString>)));
1571 auto* p = pBuf;
1572 if (bSign)
1573 *p++ = '-';
1575 bool bHasDec = false;
1577 int nDecPos;
1578 // Check for F format and number < 1
1579 if (eFormat == rtl_math_StringFormat_F)
1581 if (nExp < 0)
1583 *p++ = '0';
1584 if (nDecPlaces > 0)
1586 *p++ = cDecSeparator;
1587 bHasDec = true;
1590 sal_Int32 i = (nDigits <= 0 ? nDecPlaces : -nExp - 1);
1592 while ((i--) > 0)
1593 *p++ = '0';
1595 nDecPos = 0;
1597 else
1598 nDecPos = nExp + 1;
1600 else
1601 nDecPos = 1;
1603 int nGrouping = 0, nGroupSelector = 0, nGroupExceed = 0;
1604 if (nDecPos > 1 && pGroups && pGroups[0] && cGroupSeparator)
1606 while (nGrouping + pGroups[nGroupSelector] < nDecPos)
1608 nGrouping += pGroups[nGroupSelector];
1609 if (pGroups[nGroupSelector + 1])
1611 if (nGrouping + pGroups[nGroupSelector + 1] >= nDecPos)
1612 break; // while
1614 ++nGroupSelector;
1616 else if (!nGroupExceed)
1617 nGroupExceed = nGrouping;
1621 // print the number
1622 if (nDigits > 0)
1624 for (int nCurExp = nOrigDigits - 1;;)
1626 int nDigit;
1627 if (aParts.significand > 0 && nCurExp > 0)
1629 --nCurExp;
1630 nDigit = aParts.significand / eX[nCurExp];
1631 aParts.significand %= eX[nCurExp];
1633 else
1635 nDigit = aParts.significand;
1636 aParts.significand = 0;
1638 assert(nDigit >= 0 && nDigit < 10);
1639 *p++ = nDigit + '0';
1641 if (!--nDigits)
1642 break; // for
1644 if (nDecPos)
1646 if (!--nDecPos)
1648 *p++ = cDecSeparator;
1649 bHasDec = true;
1651 else if (nDecPos == nGrouping)
1653 *p++ = cGroupSeparator;
1654 nGrouping -= pGroups[nGroupSelector];
1656 if (nGroupSelector && nGrouping < nGroupExceed)
1657 --nGroupSelector;
1663 if (!bHasDec && eFormat == rtl_math_StringFormat_F)
1664 { // nDecPlaces < 0 did round the value
1665 while (--nDecPos > 0)
1666 { // fill before decimal point
1667 if (nDecPos == nGrouping)
1669 *p++ = cGroupSeparator;
1670 nGrouping -= pGroups[nGroupSelector];
1672 if (nGroupSelector && nGrouping < nGroupExceed)
1673 --nGroupSelector;
1676 *p++ = '0';
1680 if (bEraseTrailingDecZeros && bHasDec)
1682 while (*(p - 1) == '0')
1683 p--;
1685 if (*(p - 1) == cDecSeparator)
1686 p--;
1689 // Print the exponent ('E', followed by '+' or '-', followed by exactly
1690 // three digits for rtl_math_StringFormat_E). The code in
1691 // rtl_[u]str_valueOf{Float|Double} relies on this format.
1692 if (eFormat == rtl_math_StringFormat_E || eFormat == rtl_math_StringFormat_E2
1693 || eFormat == rtl_math_StringFormat_E1)
1695 if (p == pBuf)
1696 *p++ = '1';
1697 // maybe no nDigits if nDecPlaces < 0
1699 *p++ = 'E';
1700 if (nExp < 0)
1702 nExp = -nExp;
1703 *p++ = '-';
1705 else
1706 *p++ = '+';
1708 if (eFormat == rtl_math_StringFormat_E || nExp >= 100)
1709 *p++ = nExp / 100 + '0';
1711 nExp %= 100;
1713 if (eFormat == rtl_math_StringFormat_E || eFormat == rtl_math_StringFormat_E2 || nExp >= 10)
1714 *p++ = nExp / 10 + '0';
1716 *p++ = nExp % 10 + '0';
1719 append(pResult, pResultCapacity, nResultOffset, std::basic_string_view(pBuf, p - pBuf));
1722 template <sal_Int32 maxLen, typename C, typename T> sal_Int32 SAL_CALL valueOfFP(C* pStr, T f)
1724 assert(pStr);
1725 rtl_String* pResult = nullptr;
1726 doubleToString(&pResult, nullptr, 0, f, rtl_math_StringFormat_G,
1727 maxLen - std::size("-x.E-xxx") + 1, '.', nullptr, 0, true);
1728 const sal_Int32 nLen = pResult->length;
1729 assert(nLen < maxLen);
1730 Copy(pStr, pResult->buffer, nLen + 1);
1731 release(pResult);
1732 return nLen;
1737 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */