1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
31 #include <string_view>
32 #include <type_traits>
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>
43 #include <rtl/string.h>
44 #include <rtl/ustring.h>
46 #include <dragonbox/dragonbox.h>
48 void internRelease(rtl_uString
*);
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
60 null_terminated(C
* pStr
)
65 auto begin() const { return p
; }
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
80 with_length(C
* pStr
, sal_Int32 nLength
)
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
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
,
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
)
133 if ((ch
>= '0') && (ch
<= '9'))
135 else if ((ch
>= 'a') && (ch
<= 'z'))
137 else if ((ch
>= 'A') && (ch
<= 'Z'))
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
)
149 if constexpr (std::is_class_v
<T
>)
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
);
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 /* ----------------------------------------------------------------------- */
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();
217 if (shortenedLength
== 0)
220 return pStr1
== end1
? 0 : 1;
223 if (const sal_Int32 nRet
= Compare::compare(*pStr1
, *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
))
273 if (const sal_Int32 nRet
= Compare::compare(*pStr1Run
, *pStr2Run
))
277 return nStr1Len
- nStr2Len
;
280 /* ----------------------------------------------------------------------- */
282 template <typename C
> sal_Int32
hashCode_WithLength(const C
* pStr
, sal_Int32 nLen
)
285 sal_uInt32 h
= static_cast<sal_uInt32
>(nLen
);
288 h
= (h
*37U) + UChar( *pStr
);
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
)
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;
324 const C
* pTempStr
= pStr
;
327 if ( *pTempStr
== c
)
328 return pTempStr
-pStr
;
337 /* ----------------------------------------------------------------------- */
339 template <typename C
> sal_Int32
indexOfChar_WithLength(const C
* pStr
, sal_Int32 nLen
, C c
)
341 // assert(nLen >= 0);
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
)
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
)
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;
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 */
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
;
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 */
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
;
433 /* ----------------------------------------------------------------------- */
435 template <typename C
> sal_Int32
indexOfStr(const C
* pStr
, const C
* pSubStr
)
439 /* an empty SubString is always not findable */
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;
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 */
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
);
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
)
550 /* ----------------------------------------------------------------------- */
552 template <typename C
> sal_Int32
valueOfChar(C
* pStr
, C c
)
560 /* ----------------------------------------------------------------------- */
562 template <sal_Int32 maxLen
, typename C
, typename T
>
563 sal_Int32
valueOfInt(C
* pStr
, T n
, sal_Int16 nRadix
)
566 assert( nRadix
>= RTL_STR_MIN_RADIX
&& nRadix
<= RTL_STR_MAX_RADIX
);
567 const auto* const pStart
= pStr
;
570 using uT
= std::make_unsigned_t
<T
>;
573 /* Radix must be valid */
574 if ( (nRadix
< RTL_STR_MIN_RADIX
) || (nRadix
> RTL_STR_MAX_RADIX
) )
577 if constexpr (std::is_signed_v
<T
>)
579 /* is value negative */
584 nValue
= n
== std::numeric_limits
<T
>::min() ? static_cast<uT
>(n
) : -n
;
592 /* create a recursive buffer with all values, except the last one */
595 char nDigit
= static_cast<char>(nValue
% nRadix
);
598 *pBuf
= (nDigit
-10) + 'a';
600 *pBuf
= (nDigit
+ '0' );
603 while ( nValue
> 0 );
605 /* copy the values in the right direction into the destination buffer */
606 pStr
= std::reverse_copy(aBuf
, pBuf
, pStr
);
609 return pStr
- pStart
;
612 /* ----------------------------------------------------------------------- */
614 template <typename C
> sal_Bool
toBoolean(const C
* pStr
)
620 if ( (*pStr
== 'T') || (*pStr
== 't') )
623 if ( (*pStr
== 'R') || (*pStr
== 'r') )
626 if ( (*pStr
== 'U') || (*pStr
== 'u') )
629 if ( (*pStr
== 'E') || (*pStr
== 'e') )
638 /* ----------------------------------------------------------------------- */
640 template <typename T
, class Iter
> inline bool HandleSignChar(Iter
& iter
)
642 if constexpr (std::numeric_limits
<T
>::is_signed
)
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
)
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
) )
671 auto pStr
= str
.begin();
672 const auto end
= str
.end();
674 /* Skip whitespaces */
675 while (pStr
!= end
&& o3tl::internal::implIsWhitespace(UChar(*pStr
)))
680 const bool bNeg
= HandleSignChar
<T
>(pStr
);
681 const auto& [nDiv
, nMod
] = DivMod
<T
>(nRadix
, bNeg
);
684 std::make_unsigned_t
<T
> n
= 0;
687 sal_Int16 nDigit
= implGetDigit(UChar(*pStr
), nRadix
);
690 if (static_cast<std::make_unsigned_t
<T
>>(nMod
< nDigit
? nDiv
- 1 : nDiv
) < n
)
699 if constexpr (std::numeric_limits
<T
>::is_signed
)
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
;
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
>)))
723 if (pData
!= nullptr) {
725 pData
->length
= nLen
;
726 pData
->buffer
[nLen
] = 0;
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
)))
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
);
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
;
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]; */
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
)
795 *ppThis
= &EmptyStringImpl
<rtl_tString
>::data
;
798 /* ----------------------------------------------------------------------- */
800 template <typename rtl_tString
> void new_WithLength(rtl_tString
** ppThis
, sal_Int32 nLen
)
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)
826 assert(pCharStr
|| nLen
== 0);
827 assert(allocExtra
>= 0);
829 if (nLen
+ allocExtra
== 0)
832 rtl_tString
* pOrg
= *ppThis
;
833 *ppThis
= Alloc
<rtl_tString
>(nLen
+ allocExtra
);
834 assert(*ppThis
!= nullptr);
836 Copy((*ppThis
)->buffer
, pCharStr
, nLen
);
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 */
850 template <typename rtl_tString
> void newFromString(rtl_tString
** ppThis
, const rtl_tString
* 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
)
870 /* must be done at first, if pStr == *ppThis */
879 /* ----------------------------------------------------------------------- */
881 template <typename rtl_tString
>
882 void newFromSubString(rtl_tString
** ppThis
, const rtl_tString
* pFrom
, sal_Int32 beginIndex
,
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
)
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
)
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
)
926 #if !defined(__COVERITY__)
927 throw std::length_error("newConcat");
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();
937 auto* pTempStr
= Alloc
<rtl_tString
>(nLeftLength
+ nRightLength
);
938 OSL_ASSERT(pTempStr
!= nullptr);
940 if (*ppThis
!= nullptr) {
942 Copy( pTempStr
->buffer
, pLeft
, nLeftLength
);
944 Copy( pTempStr
->buffer
+nLeftLength
, pRight
, nRightLength
);
946 RTL_LOG_STRING_NEW( *ppThis
);
950 /* must be done last, if left or right == *ppThis */
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
);
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
);
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
)
985 rtl_tString
* const pOrg
= *ppThis
;
986 if ( pOrg
->refCount
== 1 && pOrg
->length
>= size
)
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';
995 RTL_LOG_STRING_NEW( *ppThis
);
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
)
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);
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? */
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
;
1038 Copy( pBuffer
, pStr
->buffer
, 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 */
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
)
1061 assert(nIndex
>= 0 && nIndex
<= pStr
->length
);
1062 assert(nCount
>= 0);
1063 assert(nCount
<= pStr
->length
- nIndex
);
1065 if (nIndex
>= pStr
->length
)
1067 /* newConcat test, if pNewSubStr is 0 */
1068 newConcat(ppThis
, pStr
, pNewSubStr
);
1072 /* not more than the String length could be deleted */
1073 if (nCount
>= pStr
->length
-nIndex
)
1075 /* Assign of NewSubStr? */
1079 return new_(ppThis
);
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
)
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
;
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
);
1117 } while (pCharStr
!= pEnd
);
1119 RTL_LOG_STRING_NEW(*ppThis
);
1120 /* must be done last, if pStr == *ppThis */
1125 assign(ppThis
, pStr
);
1128 /* ----------------------------------------------------------------------- */
1130 template <typename rtl_tString
> void newTrim(rtl_tString
** ppThis
, rtl_tString
* 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
);
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
)
1149 assert(nIndex
<= pStr
->length
);
1151 // Set ppThis to an empty string and return -1 if either nToken or nIndex is
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
;
1162 if (*pCharStr
== cTok
)
1166 if (nTokCount
> nToken
)
1168 if (nTokCount
== nToken
)
1169 pCharStrStart
= pCharStr
+ 1;
1175 if (nTokCount
>= nToken
)
1177 newFromStr_WithLength(ppThis
, pCharStrStart
, pCharStr
- pCharStrStart
);
1179 return pCharStr
- pOrgCharStr
+ 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
)
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
,
1209 assert(capacity
>= 0);
1212 if (capacity
< pStr
->length
)
1213 capacity
= pStr
->length
;
1215 newFromStr_WithLength(ppThis
, pStr
->buffer
, pStr
->length
, capacity
- pStr
->length
);
1219 template <class rtl_tString
>
1220 void stringbuffer_ensureCapacity(rtl_tString
** ppThis
, sal_Int32
* capacity
,
1221 sal_Int32 minimumCapacity
)
1224 assert(capacity
&& *capacity
>= 0);
1225 // assert(minimumCapacity >= 0); // It was commented out in rtl_stringbuffer_ensureCapacity
1226 if (minimumCapacity
<= *capacity
)
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
)
1242 assert(capacity
&& *capacity
>= 0);
1243 assert(offset
>= 0 && offset
<= (*ppThis
)->length
);
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
;
1257 const sal_Int32 n
= nOldLen
- offset
;
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
)
1273 assert(start
>= 0 && start
<= (*ppThis
)->length
);
1276 if (len
> (*ppThis
)->length
- start
)
1277 len
= (*ppThis
)->length
- start
;
1283 auto* pBuf
= (*ppThis
)->buffer
;
1284 const sal_Int32 nTailLen
= (*ppThis
)->length
- (start
+ len
);
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
,
1311 if (s1
->length
- fromLength
> SAL_MAX_INT32
- toLength
)
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
);
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
,
1337 stringbuffer_insert(s
, &nCapacity
, (*s
)->length
,
1338 s1
->buffer
+ fromIndex
, s1
->length
- fromIndex
);
1340 release(pOld
); // Must be last in case *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
,
1363 if (s1
->length
- fromLength
> SAL_MAX_INT32
- toLength
)
1366 newReplaceStrAt(s
, s1
, i
, fromLength
, to
, toLength
);
1374 // doubleToString implementation
1376 static inline constexpr sal_uInt64 eX
[] = { 10ull,
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
)
1415 newFromStr_WithLength(s
, sv
.data(), sv
.size());
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
);
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
1463 eFormat
= rtl_math_StringFormat_E
;
1464 nDecPlaces
= std::clamp
<sal_Int32
>(nDecPlaces
, 0, 16);
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
< 0x1p
53)
1473 eFormat
= rtl_math_StringFormat_F
;
1474 if (nDecPlaces
== rtl_math_DecimalPlaces_Max
)
1477 nDecPlaces
= std::clamp
<sal_Int32
>(nDecPlaces
, -15, 15);
1479 if (bEraseTrailingDecZeros
&& nDecPlaces
> 0)
1482 nRoundDigits
= nOrigDigits
; // no rounding
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
)
1493 eFormat
= rtl_math_StringFormat_E
;
1497 if (nDecPlaces
== rtl_math_DecimalPlaces_Max
)
1498 nDecPlaces
= (nExp
< 14) ? 15 - nExp
- 1 : 15;
1499 eFormat
= rtl_math_StringFormat_F
;
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
)
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
;
1523 if (nOrigDigits
<= nDecPlaces
&& aParts
.exponent
>= 0 && fValue
< 0x1p
53)
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
;
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
)
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
1559 if (eFormat
== rtl_math_StringFormat_F
)
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
>)));
1575 bool bHasDec
= false;
1578 // Check for F format and number < 1
1579 if (eFormat
== rtl_math_StringFormat_F
)
1586 *p
++ = cDecSeparator
;
1590 sal_Int32 i
= (nDigits
<= 0 ? nDecPlaces
: -nExp
- 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
)
1616 else if (!nGroupExceed
)
1617 nGroupExceed
= nGrouping
;
1624 for (int nCurExp
= nOrigDigits
- 1;;)
1627 if (aParts
.significand
> 0 && nCurExp
> 0)
1630 nDigit
= aParts
.significand
/ eX
[nCurExp
];
1631 aParts
.significand
%= eX
[nCurExp
];
1635 nDigit
= aParts
.significand
;
1636 aParts
.significand
= 0;
1638 assert(nDigit
>= 0 && nDigit
< 10);
1639 *p
++ = nDigit
+ '0';
1648 *p
++ = cDecSeparator
;
1651 else if (nDecPos
== nGrouping
)
1653 *p
++ = cGroupSeparator
;
1654 nGrouping
-= pGroups
[nGroupSelector
];
1656 if (nGroupSelector
&& nGrouping
< nGroupExceed
)
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
)
1680 if (bEraseTrailingDecZeros
&& bHasDec
)
1682 while (*(p
- 1) == '0')
1685 if (*(p
- 1) == cDecSeparator
)
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
)
1697 // maybe no nDigits if nDecPlaces < 0
1708 if (eFormat
== rtl_math_StringFormat_E
|| nExp
>= 100)
1709 *p
++ = nExp
/ 100 + '0';
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
)
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);
1737 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */