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 .
30 #include <string_view>
31 #include <type_traits>
36 #include <o3tl/safeint.hxx>
37 #include <osl/diagnose.h>
38 #include <sal/log.hxx>
39 #include <rtl/character.hxx>
41 #include <rtl/string.h>
42 #include <rtl/ustring.h>
44 #include <dragonbox/dragonbox.h>
46 void internRelease(rtl_uString
*);
50 template <typename C
> auto IMPL_RTL_USTRCODE(C c
) { return std::make_unsigned_t
<C
>(c
); }
52 // Wrappers around null-terminated/known-length strings, that allow to generalize algorithms
53 // without overhead (e.g., without need to get length of null-terminated strings).
55 template <typename C
> struct null_terminated
58 null_terminated(C
* pStr
)
63 auto begin() const { return p
; }
66 friend bool operator==(EndDetector
, C
* iter
) { return *iter
== 0; }
67 friend bool operator==(C
* iter
, EndDetector
) { return *iter
== 0; }
68 friend bool operator!=(EndDetector
, C
* iter
) { return *iter
!= 0; }
69 friend bool operator!=(C
* iter
, EndDetector
) { return *iter
!= 0; }
71 static auto end() { return EndDetector
{}; }
74 template <typename C
> struct with_length
78 with_length(C
* pStr
, sal_Int32 nLength
)
84 auto begin() const { return p
; }
85 auto end() const { return p
+ len
; }
90 template <typename C
> static bool Applicable(C c
)
92 return rtl::isAsciiUpperCase(IMPL_RTL_USTRCODE(c
));
94 template <typename C
> static C
Replace(C c
)
96 return rtl::toAsciiLowerCase(IMPL_RTL_USTRCODE(c
));
98 } constexpr toAsciiLower
;
102 template <typename C
> static bool Applicable(C c
)
104 return rtl::isAsciiLowerCase(IMPL_RTL_USTRCODE(c
));
106 template <typename C
> static C
Replace(C c
)
108 return rtl::toAsciiUpperCase(IMPL_RTL_USTRCODE(c
));
110 } constexpr toAsciiUpper
;
112 template <typename C
> struct FromTo
116 FromTo(C cFrom
, C cTo
) : from(cFrom
), to(cTo
) {}
117 C
Replace(C c
) const { return c
== from
? to
: c
; }
120 template <typename C
> void Copy(C
* _pDest
, const C
* _pSrc
, sal_Int32 _nCount
)
122 // take advantage of builtin optimisations
123 std::copy(_pSrc
, _pSrc
+ _nCount
, _pDest
);
126 template <typename C
> void CopyBackward(C
* _pDest
, const C
* _pSrc
, sal_Int32 _nCount
)
128 // take advantage of builtin optimisations
129 std::copy_backward(_pSrc
, _pSrc
+ _nCount
, _pDest
+ _nCount
);
132 inline void Copy(sal_Unicode
* _pDest
, const char* _pSrc
, sal_Int32 _nCount
)
134 std::transform(_pSrc
, _pSrc
+ _nCount
, _pDest
,
137 assert(rtl::isAscii(static_cast<unsigned char>(c
)));
138 SAL_WARN_IF(c
== '\0', "rtl.string", "Found embedded \\0 ASCII character");
139 return static_cast<unsigned char>(c
);
143 /* ======================================================================= */
144 /* C-String functions which could be used without the String-Class */
145 /* ======================================================================= */
147 template <typename T
> sal_Int32
getLength( const T
* pStr
)
150 if constexpr (std::is_class_v
<T
>)
156 // take advantage of builtin optimisations
157 return std::char_traits
<T
>::length(pStr
);
161 /* ----------------------------------------------------------------------- */
163 template <typename C
> void warnIfCharAndNotAscii(C c
)
165 if constexpr (sizeof(c
) == sizeof(char))
166 SAL_WARN_IF(!rtl::isAscii(static_cast<unsigned char>(c
)), "rtl.string",
167 "Found non-ASCII char");
170 template <typename C1
, typename C2
> void warnIfOneIsCharAndNotAscii(C1 c1
, C2 c2
)
172 if constexpr (sizeof(c1
) != sizeof(c2
))
174 warnIfCharAndNotAscii(c1
);
175 warnIfCharAndNotAscii(c2
);
181 template <typename C1
, typename C2
> static sal_Int32
compare(C1 c1
, C2 c2
)
183 warnIfOneIsCharAndNotAscii(c1
, c2
);
184 return static_cast<sal_Int32
>(IMPL_RTL_USTRCODE(c1
))
185 - static_cast<sal_Int32
>(IMPL_RTL_USTRCODE(c2
));
189 struct CompareIgnoreAsciiCase
191 template <typename C1
, typename C2
> static sal_Int32
compare(C1 c1
, C2 c2
)
193 warnIfOneIsCharAndNotAscii(c1
, c2
);
194 return rtl::compareIgnoreAsciiCase(IMPL_RTL_USTRCODE(c1
), IMPL_RTL_USTRCODE(c2
));
198 /* ----------------------------------------------------------------------- */
202 constexpr bool operator>=(int) { return true; } // for assert
203 constexpr bool operator==(int) { return false; } // for loop break check
204 constexpr void operator--() {} // for decrement in loop
205 } constexpr noShortening
;
207 template <class S1
, class S2
, class Compare
, typename Shorten_t
>
208 sal_Int32
compare(S1 s1
, S2 s2
, Compare
, Shorten_t shortenedLength
)
210 static_assert(std::is_same_v
<Shorten_t
, NoShortening
> || std::is_same_v
<Shorten_t
, sal_Int32
>);
211 assert(shortenedLength
>= 0);
212 auto pStr1
= s1
.begin();
213 const auto end1
= s1
.end();
214 auto pStr2
= s2
.begin();
215 const auto end2
= s2
.end();
218 if (shortenedLength
== 0)
221 return pStr1
== end1
? 0 : 1;
224 if (const sal_Int32 nRet
= Compare::compare(*pStr1
, *pStr2
))
232 // take advantage of builtin optimisations
233 template <typename C
, std::enable_if_t
<sizeof(C
) == sizeof(wchar_t), int> = 0>
234 sal_Int32
compare(null_terminated
<C
> s1
, null_terminated
<C
> s2
, CompareNormal
, NoShortening
)
236 return wcscmp(reinterpret_cast<wchar_t const*>(s1
.p
), reinterpret_cast<wchar_t const*>(s2
.p
));
238 template <typename C
, std::enable_if_t
<sizeof(C
) == sizeof(char), int> = 0>
239 sal_Int32
compare(null_terminated
<C
> s1
, null_terminated
<C
> s2
, CompareNormal
, NoShortening
)
241 return strcmp(reinterpret_cast<char const*>(s1
.p
), reinterpret_cast<char const*>(s2
.p
));
243 template <typename C
>
244 sal_Int32
compare(with_length
<C
> s1
, with_length
<C
> s2
, CompareNormal
, NoShortening
)
246 std::basic_string_view
sv1(s1
.p
, s1
.len
);
247 return sv1
.compare(std::basic_string_view(s2
.p
, s2
.len
));
249 template <typename C1
, typename C2
, class Compare
>
250 sal_Int32
compare(with_length
<C1
> s1
, with_length
<C2
> s2
, Compare cf
, sal_Int32 nShortenedLength
)
252 assert(nShortenedLength
>= 0);
253 s1
.len
= std::min(s1
.len
, nShortenedLength
);
254 s2
.len
= std::min(s2
.len
, nShortenedLength
);
255 return compare(s1
, s2
, cf
, noShortening
);
258 /* ----------------------------------------------------------------------- */
260 template <typename C1
, typename C2
, class Compare
>
261 sal_Int32
reverseCompare_WithLengths(const C1
* pStr1
, sal_Int32 nStr1Len
,
262 const C2
* pStr2
, sal_Int32 nStr2Len
, Compare
)
264 assert(pStr1
|| nStr1Len
== 0);
265 assert(nStr1Len
>= 0);
266 assert(pStr2
|| nStr2Len
== 0);
267 assert(nStr2Len
>= 0);
268 const C1
* pStr1Run
= pStr1
+nStr1Len
;
269 const C2
* pStr2Run
= pStr2
+nStr2Len
;
270 while ((pStr1
< pStr1Run
) && (pStr2
< pStr2Run
))
274 if (const sal_Int32 nRet
= Compare::compare(*pStr1Run
, *pStr2Run
))
278 return nStr1Len
- nStr2Len
;
281 /* ----------------------------------------------------------------------- */
283 template <typename IMPL_RTL_STRCODE
>
284 sal_Int32
hashCode_WithLength(const IMPL_RTL_STRCODE
*, sal_Int32
);
286 template <typename IMPL_RTL_STRCODE
> sal_Int32
hashCode( const IMPL_RTL_STRCODE
* pStr
)
288 return hashCode_WithLength( pStr
, getLength( pStr
) );
291 /* ----------------------------------------------------------------------- */
293 template <typename IMPL_RTL_STRCODE
>
294 sal_Int32
hashCode_WithLength ( const IMPL_RTL_STRCODE
* pStr
,
298 sal_uInt32 h
= static_cast<sal_uInt32
>(nLen
);
301 h
= (h
*37U) + IMPL_RTL_USTRCODE( *pStr
);
305 return static_cast<sal_Int32
>(h
);
308 /* ----------------------------------------------------------------------- */
310 template <typename IMPL_RTL_STRCODE
>
311 sal_Int32
indexOfChar ( const IMPL_RTL_STRCODE
* pStr
,
316 return -1; // Unifies behavior of strchr/wcschr and unoptimized algorithm wrt '\0'
318 if constexpr (sizeof(IMPL_RTL_STRCODE
) == sizeof(char))
320 // take advantage of builtin optimisations
321 const IMPL_RTL_STRCODE
* p
= strchr(pStr
, c
);
322 return p
? p
- pStr
: -1;
324 else if constexpr (sizeof(IMPL_RTL_STRCODE
) == sizeof(wchar_t))
326 // take advantage of builtin optimisations
327 wchar_t const * p
= wcschr(reinterpret_cast<wchar_t const *>(pStr
), static_cast<wchar_t>(c
));
328 return p
? p
- reinterpret_cast<wchar_t const *>(pStr
) : -1;
332 const IMPL_RTL_STRCODE
* pTempStr
= pStr
;
335 if ( *pTempStr
== c
)
336 return pTempStr
-pStr
;
345 /* ----------------------------------------------------------------------- */
347 template <typename IMPL_RTL_STRCODE
>
348 sal_Int32
indexOfChar_WithLength ( const IMPL_RTL_STRCODE
* pStr
,
352 // assert(nLen >= 0);
355 // take advantage of builtin optimisations
356 using my_string_view
= std::basic_string_view
<IMPL_RTL_STRCODE
>;
357 my_string_view
v(pStr
, nLen
);
358 typename
my_string_view::size_type idx
= v
.find(c
);
359 return idx
== my_string_view::npos
? -1 : idx
;
362 /* ----------------------------------------------------------------------- */
364 template <typename IMPL_RTL_STRCODE
>
365 sal_Int32
lastIndexOfChar_WithLength(const IMPL_RTL_STRCODE
*, sal_Int32
, IMPL_RTL_STRCODE
);
367 template <typename IMPL_RTL_STRCODE
>
368 sal_Int32
lastIndexOfChar ( const IMPL_RTL_STRCODE
* pStr
,
373 return -1; // Unifies behavior of strrchr/wcsrchr and lastIndexOfChar_WithLength wrt '\0'
375 if constexpr (sizeof(IMPL_RTL_STRCODE
) == sizeof(char))
377 // take advantage of builtin optimisations
378 const IMPL_RTL_STRCODE
* p
= strrchr(pStr
, c
);
379 return p
? p
- pStr
: -1;
381 else if constexpr (sizeof(IMPL_RTL_STRCODE
) == sizeof(wchar_t))
383 // take advantage of builtin optimisations
384 wchar_t const * p
= wcsrchr(reinterpret_cast<wchar_t const *>(pStr
), static_cast<wchar_t>(c
));
385 return p
? p
- reinterpret_cast<wchar_t const *>(pStr
) : -1;
389 return lastIndexOfChar_WithLength( pStr
, getLength( pStr
), c
);
393 /* ----------------------------------------------------------------------- */
395 template <typename IMPL_RTL_STRCODE
>
396 sal_Int32
lastIndexOfChar_WithLength ( const IMPL_RTL_STRCODE
* pStr
,
401 // take advantage of builtin optimisations
402 using my_string_view
= std::basic_string_view
<IMPL_RTL_STRCODE
>;
403 my_string_view
v(pStr
, nLen
);
404 typename
my_string_view::size_type idx
= v
.rfind(c
);
405 return idx
== my_string_view::npos
? -1 : idx
;
408 /* ----------------------------------------------------------------------- */
410 template <typename IMPL_RTL_STRCODE
>
411 sal_Int32
indexOfStr_WithLength(const IMPL_RTL_STRCODE
*, sal_Int32
, const IMPL_RTL_STRCODE
*,
414 template <typename IMPL_RTL_STRCODE
>
415 sal_Int32
indexOfStr ( const IMPL_RTL_STRCODE
* pStr
,
416 const IMPL_RTL_STRCODE
* pSubStr
)
420 /* an empty SubString is always not findable */
423 if constexpr (sizeof(IMPL_RTL_STRCODE
) == sizeof(char))
425 // take advantage of builtin optimisations
426 const IMPL_RTL_STRCODE
* p
= strstr(pStr
, pSubStr
);
427 return p
? p
- pStr
: -1;
429 else if constexpr (sizeof(IMPL_RTL_STRCODE
) == sizeof(wchar_t))
431 // take advantage of builtin optimisations
432 wchar_t const * p
= wcsstr(reinterpret_cast<wchar_t const *>(pStr
), reinterpret_cast<wchar_t const *>(pSubStr
));
433 return p
? p
- reinterpret_cast<wchar_t const *>(pStr
) : -1;
437 return indexOfStr_WithLength( pStr
, getLength( pStr
),
438 pSubStr
, getLength( pSubStr
) );
442 /* ----------------------------------------------------------------------- */
444 template <typename IMPL_RTL_STRCODE
>
445 sal_Int32
indexOfStr_WithLength ( const IMPL_RTL_STRCODE
* pStr
,
447 const IMPL_RTL_STRCODE
* pSubStr
,
450 assert(nStrLen
>= 0);
451 assert(nSubLen
>= 0);
452 /* an empty SubString is always not findable */
455 // take advantage of builtin optimisations
456 using my_string_view
= std::basic_string_view
<IMPL_RTL_STRCODE
>;
457 my_string_view
v(pStr
, nStrLen
);
458 auto idx
= nSubLen
== 1 ? v
.find(*pSubStr
) : v
.find(pSubStr
, 0, nSubLen
);
459 return idx
== my_string_view::npos
? -1 : idx
;
462 inline sal_Int32
indexOfStr_WithLength(const sal_Unicode
* pStr
, sal_Int32 nStrLen
,
463 const char* pSubStr
, sal_Int32 nSubLen
)
465 assert(nStrLen
>= 0);
466 assert(nSubLen
>= 0);
467 if (nSubLen
> 0 && nSubLen
<= nStrLen
)
469 sal_Unicode
const* end
= pStr
+ nStrLen
;
470 sal_Unicode
const* cursor
= pStr
;
474 cursor
= std::char_traits
<sal_Unicode
>::find(cursor
, end
- cursor
, *pSubStr
);
475 if (!cursor
|| (end
- cursor
< nSubLen
))
477 /* no enough left to actually have a match */
480 /* now it is worth trying a full match */
481 if (nSubLen
== 1 || rtl_ustr_asciil_reverseEquals_WithLength(cursor
, pSubStr
, nSubLen
))
483 return cursor
- pStr
;
491 /* ----------------------------------------------------------------------- */
493 template <typename IMPL_RTL_STRCODE
>
494 sal_Int32
lastIndexOfStr_WithLength(const IMPL_RTL_STRCODE
*, sal_Int32
, const IMPL_RTL_STRCODE
*,
497 template <typename IMPL_RTL_STRCODE
>
498 sal_Int32
lastIndexOfStr ( const IMPL_RTL_STRCODE
* pStr
,
499 const IMPL_RTL_STRCODE
* pSubStr
)
501 return lastIndexOfStr_WithLength( pStr
, getLength( pStr
),
502 pSubStr
, getLength( pSubStr
) );
505 /* ----------------------------------------------------------------------- */
507 template <typename IMPL_RTL_STRCODE
>
508 sal_Int32
lastIndexOfStr_WithLength ( const IMPL_RTL_STRCODE
* pStr
,
510 const IMPL_RTL_STRCODE
* pSubStr
,
513 assert(nStrLen
>= 0);
514 assert(nSubLen
>= 0);
515 /* an empty SubString is always not findable */
518 // take advantage of builtin optimisations
519 using my_string_view
= std::basic_string_view
<IMPL_RTL_STRCODE
>;
520 my_string_view
v(pStr
, nStrLen
);
521 my_string_view
needle(pSubStr
, nSubLen
);
522 typename
my_string_view::size_type idx
= v
.rfind(needle
);
523 return idx
== my_string_view::npos
? -1 : idx
;
526 /* ----------------------------------------------------------------------- */
528 template <class S
, class Replacer
> void replaceChars(S str
, Replacer replacer
)
530 for (auto& rChar
: str
)
531 rChar
= replacer
.Replace(rChar
);
534 /* ----------------------------------------------------------------------- */
536 template <typename IMPL_RTL_STRCODE
> sal_Int32
trim_WithLength(IMPL_RTL_STRCODE
*, sal_Int32
);
538 template <typename IMPL_RTL_STRCODE
> sal_Int32
trim( IMPL_RTL_STRCODE
* pStr
)
540 return trim_WithLength( pStr
, getLength( pStr
) );
543 /* ----------------------------------------------------------------------- */
545 template <typename IMPL_RTL_STRCODE
>
546 std::basic_string_view
<IMPL_RTL_STRCODE
> trimView( IMPL_RTL_STRCODE
* pStr
, sal_Int32 nLen
)
549 sal_Int32 nPreSpaces
= 0;
550 sal_Int32 nPostSpaces
= 0;
551 sal_Int32 nIndex
= nLen
-1;
553 while ( (nPreSpaces
< nLen
) && rtl_ImplIsWhitespace( IMPL_RTL_USTRCODE(*(pStr
+nPreSpaces
)) ) )
556 while ( (nIndex
> nPreSpaces
) && rtl_ImplIsWhitespace( IMPL_RTL_USTRCODE(*(pStr
+nIndex
)) ) )
562 return { pStr
+ nPreSpaces
, static_cast<size_t>(nLen
- nPostSpaces
- nPreSpaces
) };
565 /* ----------------------------------------------------------------------- */
567 template <typename IMPL_RTL_STRCODE
>
568 sal_Int32
trim_WithLength( IMPL_RTL_STRCODE
* pStr
, sal_Int32 nLen
)
570 const auto view
= trimView(pStr
, nLen
);
572 if (static_cast<sal_Int32
>(view
.size()) != nLen
)
574 nLen
= static_cast<sal_Int32
>(view
.size());
575 if (view
.data() != pStr
)
576 Copy(pStr
, view
.data(), nLen
);
583 /* ----------------------------------------------------------------------- */
585 template <typename IMPL_RTL_STRCODE
> sal_Int32
valueOfBoolean( IMPL_RTL_STRCODE
* pStr
, sal_Bool b
)
618 /* ----------------------------------------------------------------------- */
620 template <typename IMPL_RTL_STRCODE
>
621 sal_Int32
valueOfChar ( IMPL_RTL_STRCODE
* pStr
,
630 /* ----------------------------------------------------------------------- */
632 template <sal_Int32 maxLen
, typename IMPL_RTL_STRCODE
, typename T
>
633 sal_Int32
valueOfInt ( IMPL_RTL_STRCODE
* pStr
,
638 assert( nRadix
>= RTL_STR_MIN_RADIX
&& nRadix
<= RTL_STR_MAX_RADIX
);
642 using uT
= std::make_unsigned_t
<T
>;
645 /* Radix must be valid */
646 if ( (nRadix
< RTL_STR_MIN_RADIX
) || (nRadix
> RTL_STR_MAX_RADIX
) )
649 if constexpr (std::is_signed_v
<T
>)
651 /* is value negative */
657 nValue
= n
== std::numeric_limits
<T
>::min() ? static_cast<uT
>(n
) : -n
;
665 /* create a recursive buffer with all values, except the last one */
668 char nDigit
= static_cast<char>(nValue
% nRadix
);
671 *pBuf
= (nDigit
-10) + 'a';
673 *pBuf
= (nDigit
+ '0' );
676 while ( nValue
> 0 );
678 /* copy the values in the right direction into the destination buffer */
686 while ( pBuf
!= aBuf
);
692 /* ----------------------------------------------------------------------- */
694 template <typename IMPL_RTL_STRCODE
> sal_Bool
toBoolean( const IMPL_RTL_STRCODE
* pStr
)
700 if ( (*pStr
== 'T') || (*pStr
== 't') )
703 if ( (*pStr
== 'R') || (*pStr
== 'r') )
706 if ( (*pStr
== 'U') || (*pStr
== 'u') )
709 if ( (*pStr
== 'E') || (*pStr
== 'e') )
718 /* ----------------------------------------------------------------------- */
720 template <typename T
, class Iter
> inline bool HandleSignChar(Iter
& iter
)
722 if constexpr (std::numeric_limits
<T
>::is_signed
)
735 template <typename T
> std::pair
<T
, sal_Int16
> DivMod(sal_Int16 nRadix
, [[maybe_unused
]] bool bNeg
)
737 if constexpr (std::numeric_limits
<T
>::is_signed
)
739 return { -(std::numeric_limits
<T
>::min() / nRadix
),
740 -(std::numeric_limits
<T
>::min() % nRadix
) };
741 return { std::numeric_limits
<T
>::max() / nRadix
, std::numeric_limits
<T
>::max() % nRadix
};
744 template <typename T
, class S
> T
toInt(S str
, sal_Int16 nRadix
)
746 assert( nRadix
>= RTL_STR_MIN_RADIX
&& nRadix
<= RTL_STR_MAX_RADIX
);
748 if ( (nRadix
< RTL_STR_MIN_RADIX
) || (nRadix
> RTL_STR_MAX_RADIX
) )
751 auto pStr
= str
.begin();
752 const auto end
= str
.end();
754 /* Skip whitespaces */
755 while (pStr
!= end
&& rtl_ImplIsWhitespace(IMPL_RTL_USTRCODE(*pStr
)))
760 const bool bNeg
= HandleSignChar
<T
>(pStr
);
761 const auto& [nDiv
, nMod
] = DivMod
<T
>(nRadix
, bNeg
);
764 std::make_unsigned_t
<T
> n
= 0;
767 sal_Int16 nDigit
= rtl_ImplGetDigit(IMPL_RTL_USTRCODE(*pStr
), nRadix
);
770 if (static_cast<std::make_unsigned_t
<T
>>(nMod
< nDigit
? nDiv
- 1 : nDiv
) < n
)
779 if constexpr (std::numeric_limits
<T
>::is_signed
)
781 return n
== static_cast<std::make_unsigned_t
<T
>>(std::numeric_limits
<T
>::min())
782 ? std::numeric_limits
<T
>::min()
783 : -static_cast<T
>(n
);
784 return static_cast<T
>(n
);
787 /* ======================================================================= */
788 /* Internal String-Class help functions */
789 /* ======================================================================= */
791 template <class STRINGDATA
> using STRCODE
= std::remove_extent_t
<decltype(STRINGDATA::buffer
)>;
792 template <typename C
> struct STRINGDATA_
;
793 template <> struct STRINGDATA_
<char>
795 using T
= rtl_String
;
797 template <> struct STRINGDATA_
<sal_Unicode
>
799 using T
= rtl_uString
;
801 template <typename C
> using STRINGDATA
= typename STRINGDATA_
<C
>::T
;
803 template <typename IMPL_RTL_STRINGDATA
> IMPL_RTL_STRINGDATA
* Alloc( sal_Int32 nLen
)
805 IMPL_RTL_STRINGDATA
* pData
806 = (sal::static_int_cast
< sal_uInt32
>(nLen
)
807 <= ((SAL_MAX_UINT32
- sizeof (IMPL_RTL_STRINGDATA
))
808 / sizeof (STRCODE
<IMPL_RTL_STRINGDATA
>)))
809 ? static_cast<IMPL_RTL_STRINGDATA
*>(rtl_allocateString(
810 sizeof (IMPL_RTL_STRINGDATA
) + nLen
* sizeof (STRCODE
<IMPL_RTL_STRINGDATA
>)))
812 if (pData
!= nullptr) {
814 pData
->length
= nLen
;
815 pData
->buffer
[nLen
] = 0;
820 /* ======================================================================= */
821 /* String-Class functions */
822 /* ======================================================================= */
824 template <typename IMPL_RTL_STRINGDATA
> void acquire(IMPL_RTL_STRINGDATA
* pThis
)
826 if (!SAL_STRING_IS_STATIC (pThis
))
827 osl_atomic_increment( &((pThis
)->refCount
) );
830 /* ----------------------------------------------------------------------- */
832 template <typename IMPL_RTL_STRINGDATA
> void release( IMPL_RTL_STRINGDATA
* pThis
)
834 if (SAL_UNLIKELY(SAL_STRING_IS_STATIC (pThis
)))
837 /* OString doesn't have an 'intern' */
838 if constexpr (sizeof(STRCODE
<IMPL_RTL_STRINGDATA
>) == sizeof(sal_Unicode
))
840 if (SAL_STRING_IS_INTERN (pThis
))
842 internRelease (pThis
);
847 if ( !osl_atomic_decrement( &(pThis
->refCount
) ) )
849 RTL_LOG_STRING_DELETE( pThis
);
850 rtl_freeString( pThis
);
854 /* ----------------------------------------------------------------------- */
856 /* static data to be referenced by all empty strings
857 * the refCount is predefined to 1 and must never become 0 !
859 template <typename IMPL_RTL_STRINGDATA
> struct EmptyStringImpl
861 static IMPL_RTL_STRINGDATA data
;
865 inline rtl_uString EmptyStringImpl
<rtl_uString
>::data
= {
866 sal_Int32(SAL_STRING_INTERN_FLAG
| SAL_STRING_STATIC_FLAG
| 1), /* sal_Int32 refCount; */
867 0, /* sal_Int32 length; */
868 { 0 } /* sal_Unicode buffer[1]; */
872 inline rtl_String EmptyStringImpl
<rtl_String
>::data
= {
873 SAL_STRING_STATIC_FLAG
| 1, /* sal_Int32 refCount; */
874 0, /* sal_Int32 length; */
875 { 0 } /* char buffer[1]; */
878 template <typename IMPL_RTL_STRINGDATA
> void new_( IMPL_RTL_STRINGDATA
** ppThis
)
884 *ppThis
= &EmptyStringImpl
<IMPL_RTL_STRINGDATA
>::data
;
887 /* ----------------------------------------------------------------------- */
889 template <typename IMPL_RTL_STRINGDATA
>
890 void new_WithLength( IMPL_RTL_STRINGDATA
** ppThis
, sal_Int32 nLen
)
901 *ppThis
= Alloc
<IMPL_RTL_STRINGDATA
>( nLen
);
902 assert(*ppThis
!= nullptr);
903 (*ppThis
)->length
= 0;
904 (*ppThis
)->buffer
[0] = 0;
908 /* ----------------------------------------------------------------------- */
910 template <typename IMPL_RTL_STRINGDATA
, typename C
>
911 void newFromStr_WithLength(IMPL_RTL_STRINGDATA
** ppThis
, const C
* pCharStr
, sal_Int32 nLen
,
912 sal_Int32 allocExtra
= 0)
916 assert(pCharStr
|| nLen
== 0);
917 assert(allocExtra
>= 0);
919 if (nLen
+ allocExtra
== 0)
922 IMPL_RTL_STRINGDATA
* pOrg
= *ppThis
;
923 *ppThis
= Alloc
<IMPL_RTL_STRINGDATA
>(nLen
+ allocExtra
);
924 assert(*ppThis
!= nullptr);
926 Copy((*ppThis
)->buffer
, pCharStr
, nLen
);
929 (*ppThis
)->length
= nLen
;
930 (*ppThis
)->buffer
[nLen
] = 0;
933 RTL_LOG_STRING_NEW(*ppThis
);
935 /* must be done last, if pCharStr belongs to *ppThis */
940 template <typename IMPL_RTL_STRINGDATA
>
941 void newFromString ( IMPL_RTL_STRINGDATA
** ppThis
,
942 const IMPL_RTL_STRINGDATA
* pStr
)
946 newFromStr_WithLength(ppThis
, pStr
->buffer
, pStr
->length
);
949 /* ----------------------------------------------------------------------- */
951 template <typename IMPL_RTL_STRINGDATA
>
952 void newFromStr ( IMPL_RTL_STRINGDATA
** ppThis
,
953 const STRCODE
<IMPL_RTL_STRINGDATA
>* pCharStr
)
955 #if OSL_DEBUG_LEVEL > 0
956 //TODO: For now, only abort in non-production debug builds; once all places that rely on the
957 // undocumented newFromStr behavior of treating a null pCharStr like an empty string have been
958 // found and fixed, drop support for that behavior and turn this into a general assert:
959 if (pCharStr
== nullptr) {
964 newFromStr_WithLength(ppThis
, pCharStr
, pCharStr
? getLength(pCharStr
) : 0);
967 /* ----------------------------------------------------------------------- */
969 template <typename IMPL_RTL_STRINGDATA
> void assign(IMPL_RTL_STRINGDATA
**, IMPL_RTL_STRINGDATA
*);
971 template <typename IMPL_RTL_STRINGDATA
>
972 void newFromSubString ( IMPL_RTL_STRINGDATA
** ppThis
,
973 const IMPL_RTL_STRINGDATA
* pFrom
,
974 sal_Int32 beginIndex
,
978 if ( beginIndex
== 0 && count
== pFrom
->length
)
979 return assign(ppThis
, const_cast<IMPL_RTL_STRINGDATA
*>(pFrom
));
980 if ( count
< 0 || beginIndex
< 0 || beginIndex
+ count
> pFrom
->length
)
982 assert(false); // fail fast at least in debug builds
983 return newFromStr_WithLength(ppThis
, "!!br0ken!!", 10);
986 newFromStr_WithLength( ppThis
, pFrom
->buffer
+ beginIndex
, count
);
989 /* ----------------------------------------------------------------------- */
991 template <typename IMPL_RTL_STRINGDATA
>
992 void assign ( IMPL_RTL_STRINGDATA
** ppThis
,
993 IMPL_RTL_STRINGDATA
* pStr
)
996 /* must be done at first, if pStr == *ppThis */
1005 /* ----------------------------------------------------------------------- */
1007 template <typename IMPL_RTL_STRINGDATA
> auto* getStr( IMPL_RTL_STRINGDATA
* pThis
)
1010 return pThis
->buffer
;
1013 /* ----------------------------------------------------------------------- */
1015 enum ThrowPolicy
{ NoThrow
, Throw
};
1017 template <ThrowPolicy throwPolicy
, typename IMPL_RTL_STRINGDATA
, typename C1
, typename C2
>
1018 void newConcat(IMPL_RTL_STRINGDATA
** ppThis
, const C1
* pLeft
, sal_Int32 nLeftLength
,
1019 const C2
* pRight
, sal_Int32 nRightLength
)
1022 assert(nLeftLength
>= 0);
1023 assert(pLeft
|| nLeftLength
== 0);
1024 assert(nRightLength
>= 0);
1025 assert(pRight
|| nRightLength
== 0);
1026 IMPL_RTL_STRINGDATA
* pOrg
= *ppThis
;
1028 if (nLeftLength
> std::numeric_limits
<sal_Int32
>::max() - nRightLength
)
1030 if constexpr (throwPolicy
== NoThrow
)
1034 #if !defined(__COVERITY__)
1035 throw std::length_error("newConcat");
1037 //coverity doesn't report std::bad_alloc as an unhandled exception when
1038 //potentially thrown from destructors but does report std::length_error
1039 throw std::bad_alloc();
1045 auto* pTempStr
= Alloc
<IMPL_RTL_STRINGDATA
>(nLeftLength
+ nRightLength
);
1046 OSL_ASSERT(pTempStr
!= nullptr);
1048 if (*ppThis
!= nullptr) {
1050 Copy( pTempStr
->buffer
, pLeft
, nLeftLength
);
1052 Copy( pTempStr
->buffer
+nLeftLength
, pRight
, nRightLength
);
1054 RTL_LOG_STRING_NEW( *ppThis
);
1058 /* must be done last, if left or right == *ppThis */
1063 template<typename IMPL_RTL_STRINGDATA
, typename IMPL_RTL_STRCODE
>
1064 void newConcat(IMPL_RTL_STRINGDATA
** ppThis
, IMPL_RTL_STRINGDATA
* pLeft
,
1065 const IMPL_RTL_STRCODE
* pRight
, sal_Int32 nRightLength
)
1067 assert(pLeft
!= nullptr);
1068 if (nRightLength
== 0)
1069 assign(ppThis
, pLeft
);
1071 newConcat
<Throw
>(ppThis
, pLeft
->buffer
, pLeft
->length
, pRight
, nRightLength
);
1074 template <typename IMPL_RTL_STRINGDATA
>
1075 void newConcat ( IMPL_RTL_STRINGDATA
** ppThis
,
1076 IMPL_RTL_STRINGDATA
* pLeft
,
1077 IMPL_RTL_STRINGDATA
* pRight
)
1079 /* Test for 0-Pointer - if not, change newReplaceStrAt! */
1080 if ( !pRight
|| !pRight
->length
)
1082 assert(pLeft
!= nullptr);
1083 assign(ppThis
, pLeft
);
1085 else if ( !pLeft
|| !pLeft
->length
)
1086 assign(ppThis
, pRight
);
1088 newConcat
<NoThrow
>(ppThis
, pLeft
->buffer
, pLeft
->length
, pRight
->buffer
, pRight
->length
);
1091 /* ----------------------------------------------------------------------- */
1093 template <typename IMPL_RTL_STRINGDATA
>
1094 void ensureCapacity ( IMPL_RTL_STRINGDATA
** ppThis
,
1098 IMPL_RTL_STRINGDATA
* const pOrg
= *ppThis
;
1099 if ( pOrg
->refCount
== 1 && pOrg
->length
>= size
)
1101 assert( pOrg
->length
<= size
); // do not truncate
1102 auto* pTempStr
= Alloc
<IMPL_RTL_STRINGDATA
>( size
);
1103 Copy( pTempStr
->buffer
, pOrg
->buffer
, pOrg
->length
);
1104 // right now the length is still the same as of the original
1105 pTempStr
->length
= pOrg
->length
;
1106 pTempStr
->buffer
[ pOrg
->length
] = '\0';
1108 RTL_LOG_STRING_NEW( *ppThis
);
1113 /* ----------------------------------------------------------------------- */
1115 template <typename IMPL_RTL_STRINGDATA
, typename IMPL_RTL_STRCODE
>
1116 void newReplaceStrAt ( IMPL_RTL_STRINGDATA
** ppThis
,
1117 IMPL_RTL_STRINGDATA
* pStr
,
1120 const IMPL_RTL_STRCODE
* pNewSubStr
,
1121 sal_Int32 nNewSubStrLen
)
1124 assert(nIndex
>= 0 && nIndex
<= pStr
->length
);
1125 assert(nCount
>= 0);
1126 assert(nCount
<= pStr
->length
- nIndex
);
1127 assert(pNewSubStr
!= nullptr || nNewSubStrLen
== 0);
1128 assert(nNewSubStrLen
>= 0);
1130 if ( nIndex
>= pStr
->length
)
1131 return newConcat(ppThis
, pStr
, pNewSubStr
, nNewSubStrLen
);
1133 /* not more than the String length could be deleted */
1134 if ( nCount
>= pStr
->length
-nIndex
)
1136 /* Assign of NewSubStr? */
1138 return newFromStr_WithLength( ppThis
, pNewSubStr
, nNewSubStrLen
);
1140 nCount
= pStr
->length
- nIndex
;
1143 /* Assign of Str? */
1144 if ( !nCount
&& !nNewSubStrLen
)
1145 return assign(ppThis
, pStr
);
1147 IMPL_RTL_STRINGDATA
* pOrg
= *ppThis
;
1149 /* Alloc New Buffer */
1150 *ppThis
= Alloc
<IMPL_RTL_STRINGDATA
>(pStr
->length
- nCount
+ nNewSubStrLen
);
1151 assert(*ppThis
!= nullptr);
1152 auto* pBuffer
= (*ppThis
)->buffer
;
1155 Copy( pBuffer
, pStr
->buffer
, nIndex
);
1158 if ( nNewSubStrLen
)
1160 Copy( pBuffer
, pNewSubStr
, nNewSubStrLen
);
1161 pBuffer
+= nNewSubStrLen
;
1163 Copy( pBuffer
, pStr
->buffer
+nIndex
+nCount
, pStr
->length
-nIndex
-nCount
);
1165 RTL_LOG_STRING_NEW( *ppThis
);
1166 /* must be done last, if pStr or pNewSubStr == *ppThis */
1171 /* ----------------------------------------------------------------------- */
1173 template <typename IMPL_RTL_STRINGDATA
>
1174 void newReplaceStrAt ( IMPL_RTL_STRINGDATA
** ppThis
,
1175 IMPL_RTL_STRINGDATA
* pStr
,
1178 IMPL_RTL_STRINGDATA
* pNewSubStr
)
1181 assert(nIndex
>= 0 && nIndex
<= pStr
->length
);
1182 assert(nCount
>= 0);
1183 assert(nCount
<= pStr
->length
- nIndex
);
1185 if (nIndex
>= pStr
->length
)
1187 /* newConcat test, if pNewSubStr is 0 */
1188 newConcat(ppThis
, pStr
, pNewSubStr
);
1192 /* not more than the String length could be deleted */
1193 if (nCount
>= pStr
->length
-nIndex
)
1195 /* Assign of NewSubStr? */
1199 return new_(ppThis
);
1201 return assign(ppThis
, pNewSubStr
);
1203 nCount
= pStr
->length
- nIndex
;
1206 /* Assign of Str? */
1207 if (!nCount
&& (!pNewSubStr
|| !pNewSubStr
->length
))
1209 assign(ppThis
, pStr
);
1213 const auto* pNewSubStrBuf
= pNewSubStr
? pNewSubStr
->buffer
: nullptr;
1214 const sal_Int32 nNewSubStrLength
= pNewSubStr
? pNewSubStr
->length
: 0;
1215 newReplaceStrAt(ppThis
, pStr
, nIndex
, nCount
, pNewSubStrBuf
, nNewSubStrLength
);
1218 /* ----------------------------------------------------------------------- */
1220 template <class Traits
, typename IMPL_RTL_STRINGDATA
>
1221 void newReplaceChars(IMPL_RTL_STRINGDATA
** ppThis
, IMPL_RTL_STRINGDATA
* pStr
)
1226 const auto pEnd
= pStr
->buffer
+ pStr
->length
;
1227 auto pCharStr
= std::find_if(pStr
->buffer
, pEnd
, [](auto c
) { return Traits::Applicable(c
); });
1228 if (pCharStr
!= pEnd
)
1230 IMPL_RTL_STRINGDATA
* pOrg
= *ppThis
;
1231 *ppThis
= Alloc
<IMPL_RTL_STRINGDATA
>(pStr
->length
);
1232 assert(*ppThis
!= nullptr);
1233 auto* pNewCharStr
= (*ppThis
)->buffer
;
1235 const sal_Int32 nCount
= pCharStr
- pStr
->buffer
;
1236 Copy(pNewCharStr
, pStr
->buffer
, nCount
);
1237 pNewCharStr
+= nCount
;
1238 /* replace/copy rest of the string */
1241 *pNewCharStr
= Traits::Replace(*pCharStr
);
1244 } while (pCharStr
!= pEnd
);
1246 RTL_LOG_STRING_NEW(*ppThis
);
1247 /* must be done last, if pStr == *ppThis */
1252 assign(ppThis
, pStr
);
1255 /* ----------------------------------------------------------------------- */
1257 template <typename IMPL_RTL_STRINGDATA
>
1258 void newTrim ( IMPL_RTL_STRINGDATA
** ppThis
,
1259 IMPL_RTL_STRINGDATA
* pStr
)
1262 const auto view
= trimView(pStr
->buffer
, pStr
->length
);
1264 if (static_cast<sal_Int32
>(view
.size()) == pStr
->length
)
1265 assign(ppThis
, pStr
);
1267 newFromStr_WithLength(ppThis
, view
.data(), view
.size());
1270 /* ----------------------------------------------------------------------- */
1272 template <typename IMPL_RTL_STRINGDATA
>
1273 sal_Int32
getToken ( IMPL_RTL_STRINGDATA
** ppThis
,
1274 IMPL_RTL_STRINGDATA
* pStr
,
1276 STRCODE
<IMPL_RTL_STRINGDATA
> cTok
,
1281 assert(nIndex
<= pStr
->length
);
1283 // Set ppThis to an empty string and return -1 if either nToken or nIndex is
1285 if (nIndex
>= 0 && nToken
>= 0)
1287 const auto* pOrgCharStr
= pStr
->buffer
;
1288 const auto* pCharStr
= pOrgCharStr
+ nIndex
;
1289 sal_Int32 nLen
= pStr
->length
- nIndex
;
1290 sal_Int32 nTokCount
= 0;
1291 const auto* pCharStrStart
= pCharStr
;
1294 if (*pCharStr
== cTok
)
1298 if (nTokCount
> nToken
)
1300 if (nTokCount
== nToken
)
1301 pCharStrStart
= pCharStr
+ 1;
1307 if (nTokCount
>= nToken
)
1309 newFromStr_WithLength(ppThis
, pCharStrStart
, pCharStr
- pCharStrStart
);
1311 return pCharStr
- pOrgCharStr
+ 1;
1321 /* ======================================================================= */
1322 /* String buffer help functions */
1323 /* ======================================================================= */
1325 template <class IMPL_RTL_STRINGDATA
>
1326 void stringbuffer_newFromStr_WithLength(IMPL_RTL_STRINGDATA
** ppThis
,
1327 const STRCODE
<IMPL_RTL_STRINGDATA
>* pStr
, sal_Int32 count
)
1332 count
= 0; // Because old code didn't care about count when !pStr
1334 newFromStr_WithLength(ppThis
, pStr
, count
, 16);
1337 template <class IMPL_RTL_STRINGDATA
>
1338 sal_Int32
stringbuffer_newFromStringBuffer(IMPL_RTL_STRINGDATA
** ppThis
, sal_Int32 capacity
,
1339 IMPL_RTL_STRINGDATA
* pStr
)
1341 assert(capacity
>= 0);
1344 if (capacity
< pStr
->length
)
1345 capacity
= pStr
->length
;
1347 newFromStr_WithLength(ppThis
, pStr
->buffer
, pStr
->length
, capacity
- pStr
->length
);
1351 template <class IMPL_RTL_STRINGDATA
>
1352 void stringbuffer_ensureCapacity(IMPL_RTL_STRINGDATA
** ppThis
, sal_Int32
* capacity
,
1353 sal_Int32 minimumCapacity
)
1356 assert(capacity
&& *capacity
>= 0);
1357 // assert(minimumCapacity >= 0); // It was commented out in rtl_stringbuffer_ensureCapacity
1358 if (minimumCapacity
<= *capacity
)
1361 const auto nLength
= (*ppThis
)->length
;
1362 *capacity
= (nLength
+ 1) * 2;
1363 if (minimumCapacity
> *capacity
)
1364 *capacity
= minimumCapacity
;
1366 newFromStr_WithLength(ppThis
, (*ppThis
)->buffer
, nLength
, *capacity
- nLength
);
1369 template <class IMPL_RTL_STRINGDATA
, typename C
>
1370 void stringbuffer_insert(IMPL_RTL_STRINGDATA
** ppThis
, sal_Int32
* capacity
, sal_Int32 offset
,
1371 const C
* pStr
, sal_Int32 len
)
1374 assert(capacity
&& *capacity
>= 0);
1375 assert(offset
>= 0 && offset
<= (*ppThis
)->length
);
1380 stringbuffer_ensureCapacity(ppThis
, capacity
, (*ppThis
)->length
+ len
);
1382 sal_Int32 nOldLen
= (*ppThis
)->length
;
1383 auto* pBuf
= (*ppThis
)->buffer
;
1386 const sal_Int32 n
= nOldLen
- offset
;
1388 CopyBackward(pBuf
+ offset
+ len
, pBuf
+ offset
, n
);
1390 /* insert the new characters */
1391 if (pStr
!= nullptr)
1392 Copy(pBuf
+ offset
, pStr
, len
);
1394 (*ppThis
)->length
= nOldLen
+ len
;
1395 pBuf
[nOldLen
+ len
] = 0;
1398 template <class IMPL_RTL_STRINGDATA
>
1399 void stringbuffer_remove(IMPL_RTL_STRINGDATA
** ppThis
, sal_Int32 start
, sal_Int32 len
)
1402 assert(start
>= 0 && start
<= (*ppThis
)->length
);
1405 if (len
> (*ppThis
)->length
- start
)
1406 len
= (*ppThis
)->length
- start
;
1412 auto* pBuf
= (*ppThis
)->buffer
;
1413 const sal_Int32 nTailLen
= (*ppThis
)->length
- (start
+ len
);
1418 Copy(pBuf
+ start
, pBuf
+ start
+ len
, nTailLen
);
1421 (*ppThis
)->length
-= len
;
1422 pBuf
[(*ppThis
)->length
] = 0;
1425 template <class S
, typename CharTypeFrom
, typename CharTypeTo
>
1426 void newReplaceAllFromIndex(S
** s
, S
* s1
, CharTypeFrom
const* from
, sal_Int32 fromLength
,
1427 CharTypeTo
const* to
, sal_Int32 toLength
, sal_Int32 fromIndex
)
1429 assert(s
!= nullptr);
1430 assert(s1
!= nullptr);
1431 assert(fromLength
>= 0);
1432 assert(from
!= nullptr || fromLength
== 0);
1433 assert(toLength
>= 0);
1434 assert(to
!= nullptr || toLength
== 0);
1435 assert(fromIndex
>= 0 && fromIndex
<= s1
->length
);
1436 sal_Int32 i
= indexOfStr_WithLength(s1
->buffer
+ fromIndex
, s1
->length
- fromIndex
,
1440 if (s1
->length
- fromLength
> SAL_MAX_INT32
- toLength
)
1443 sal_Int32 nCapacity
= s1
->length
+ (toLength
- fromLength
);
1444 if (fromLength
< toLength
)
1446 // Pre-allocate up to 16 replacements more
1447 const sal_Int32 nMaxMoreFinds
= (s1
->length
- i
- fromLength
) / fromLength
;
1448 const sal_Int32 nIncrease
= toLength
- fromLength
;
1449 const sal_Int32 nMoreReplacements
= std::min(
1450 { nMaxMoreFinds
, (SAL_MAX_INT32
- nCapacity
) / nIncrease
, sal_Int32(16) });
1451 nCapacity
+= nMoreReplacements
* nIncrease
;
1453 const auto pOld
= *s
;
1454 *s
= Alloc
<S
>(nCapacity
);
1459 stringbuffer_insert(s
, &nCapacity
, (*s
)->length
, s1
->buffer
+ fromIndex
, i
);
1460 stringbuffer_insert(s
, &nCapacity
, (*s
)->length
, to
, toLength
);
1461 fromIndex
+= i
+ fromLength
;
1462 i
= indexOfStr_WithLength(s1
->buffer
+ fromIndex
, s1
->length
- fromIndex
,
1466 stringbuffer_insert(s
, &nCapacity
, (*s
)->length
,
1467 s1
->buffer
+ fromIndex
, s1
->length
- fromIndex
);
1469 release(pOld
); // Must be last in case *s == s1
1474 RTL_LOG_STRING_NEW(*s
);
1477 template <typename IMPL_RTL_STRINGDATA
>
1478 void newReplace(IMPL_RTL_STRINGDATA
** ppThis
, IMPL_RTL_STRINGDATA
* pStr
,
1479 STRCODE
<IMPL_RTL_STRINGDATA
> cOld
, STRCODE
<IMPL_RTL_STRINGDATA
> cNew
)
1481 return newReplaceAllFromIndex(ppThis
, pStr
, &cOld
, 1, &cNew
, 1, 0);
1484 template <class S
, typename CharTypeFrom
, typename CharTypeTo
>
1485 void newReplaceFirst(S
** s
, S
* s1
, CharTypeFrom
const* from
, sal_Int32 fromLength
,
1486 CharTypeTo
const* to
, sal_Int32 toLength
, sal_Int32
& fromIndex
)
1488 assert(s
!= nullptr);
1489 assert(s1
!= nullptr);
1490 assert(fromLength
>= 0);
1491 assert(from
!= nullptr || fromLength
== 0);
1492 assert(toLength
>= 0);
1493 assert(to
!= nullptr || toLength
== 0);
1494 assert(fromIndex
>= 0 && fromIndex
<= s1
->length
);
1495 sal_Int32 i
= indexOfStr_WithLength(s1
->buffer
+ fromIndex
, s1
->length
- fromIndex
,
1499 if (s1
->length
- fromLength
> SAL_MAX_INT32
- toLength
)
1502 newReplaceStrAt(s
, s1
, i
, fromLength
, to
, toLength
);
1510 // doubleToString implementation
1512 static inline constexpr sal_uInt64 eX
[] = { 10ull,
1526 1000000000000000ull,
1527 10000000000000000ull,
1528 100000000000000000ull,
1529 1000000000000000000ull,
1530 10000000000000000000ull };
1532 template <typename S
>
1533 void doubleToString(S
** pResult
, sal_Int32
* pResultCapacity
, sal_Int32 nResultOffset
, double fValue
,
1534 rtl_math_StringFormat eFormat
, sal_Int32 nDecPlaces
, STRCODE
<S
> cDecSeparator
,
1535 sal_Int32
const* pGroups
, STRCODE
<S
> cGroupSeparator
,
1536 bool bEraseTrailingDecZeros
)
1538 auto decimalDigits
= [](sal_uInt64 n
) {
1539 return std::distance(std::begin(eX
), std::upper_bound(std::begin(eX
), std::end(eX
), n
)) + 1;
1542 auto roundToPow10
= [](sal_uInt64 n
, int e
) {
1543 assert(e
> 0 && o3tl::make_unsigned(e
) <= std::size(eX
));
1544 const sal_uInt64 d
= eX
[e
- 1];
1545 return (n
+ d
/ 2) / d
* d
;
1548 auto append
= [](S
** s
, sal_Int32
* pCapacity
, sal_Int32
& rOffset
, auto sv
)
1550 stringbuffer_insert(s
, pCapacity
, rOffset
, sv
.data(), sv
.size());
1551 rOffset
+= sv
.size();
1554 if (std::isnan(fValue
))
1556 // #i112652# XMLSchema-2
1557 constexpr std::string_view nan
{ "NaN" };
1558 if (!pResultCapacity
)
1559 return newFromStr_WithLength(pResult
, nan
.data(), nan
.size());
1561 return append(pResult
, pResultCapacity
, nResultOffset
, nan
);
1564 // sign adjustment, instead of testing for fValue<0.0 this will also fetch -0.0
1565 bool bSign
= std::signbit(fValue
);
1567 if (std::isinf(fValue
))
1569 // #i112652# XMLSchema-2
1570 std::string_view inf
= bSign
? std::string_view("-INF") : std::string_view("INF");
1571 if (!pResultCapacity
)
1572 return newFromStr_WithLength(pResult
, inf
.data(), inf
.size());
1574 return append(pResult
, pResultCapacity
, nResultOffset
, inf
);
1580 decltype(jkj::dragonbox::to_decimal(fValue
, jkj::dragonbox::policy::sign::ignore
,
1581 jkj::dragonbox::policy::trailing_zero::ignore
)) aParts
{};
1582 if (fValue
) // to_decimal is documented to only handle non-zero finite numbers
1583 aParts
= jkj::dragonbox::to_decimal(fValue
, jkj::dragonbox::policy::sign::ignore
,
1584 jkj::dragonbox::policy::trailing_zero::ignore
);
1586 int nOrigDigits
= decimalDigits(aParts
.significand
);
1587 int nExp
= nOrigDigits
+ aParts
.exponent
- 1;
1588 int nRoundDigits
= 15;
1590 // Unfortunately the old rounding below writes 1.79769313486232e+308 for
1591 // DBL_MAX and 4 subsequent nextafter(...,0).
1592 static const double fB1
= std::nextafter(std::numeric_limits
<double>::max(), 0);
1593 static const double fB2
= std::nextafter(fB1
, 0);
1594 static const double fB3
= std::nextafter(fB2
, 0);
1595 static const double fB4
= std::nextafter(fB3
, 0);
1596 if ((fValue
>= fB4
) && eFormat
!= rtl_math_StringFormat_F
)
1598 // 1.7976931348623157e+308 instead of rounded 1.79769313486232e+308
1599 // that can't be converted back as out of range. For rounded values if
1600 // they exceed range they should not be written to exchange strings or
1603 eFormat
= rtl_math_StringFormat_E
;
1604 nDecPlaces
= std::clamp
<sal_Int32
>(nDecPlaces
, 0, 16);
1608 // Use integer representation for integer values that fit into the
1609 // mantissa (1.((2^53)-1)) with a precision of 1 for highest accuracy.
1610 if ((eFormat
== rtl_math_StringFormat_Automatic
|| eFormat
== rtl_math_StringFormat_F
)
1611 && aParts
.exponent
>= 0 && fValue
< 0x1p
53)
1613 eFormat
= rtl_math_StringFormat_F
;
1614 if (nDecPlaces
== rtl_math_DecimalPlaces_Max
)
1617 nDecPlaces
= std::clamp
<sal_Int32
>(nDecPlaces
, -15, 15);
1619 if (bEraseTrailingDecZeros
&& nDecPlaces
> 0)
1622 nRoundDigits
= nOrigDigits
; // no rounding
1627 case rtl_math_StringFormat_Automatic
:
1628 // E or F depending on exponent magnitude
1629 if (nExp
<= -15 || nExp
>= 15)
1631 if (nDecPlaces
== rtl_math_DecimalPlaces_Max
)
1633 eFormat
= rtl_math_StringFormat_E
;
1637 if (nDecPlaces
== rtl_math_DecimalPlaces_Max
)
1638 nDecPlaces
= (nExp
< 14) ? 15 - nExp
- 1 : 15;
1639 eFormat
= rtl_math_StringFormat_F
;
1643 case rtl_math_StringFormat_G
:
1644 case rtl_math_StringFormat_G1
:
1645 case rtl_math_StringFormat_G2
:
1646 // G-Point, similar to sprintf %G
1647 if (nDecPlaces
== rtl_math_DecimalPlaces_DefaultSignificance
)
1650 if (nExp
< -4 || nExp
>= nDecPlaces
)
1652 nDecPlaces
= std::max
<sal_Int32
>(1, nDecPlaces
- 1);
1654 if (eFormat
== rtl_math_StringFormat_G
)
1655 eFormat
= rtl_math_StringFormat_E
;
1656 else if (eFormat
== rtl_math_StringFormat_G2
)
1657 eFormat
= rtl_math_StringFormat_E2
;
1658 else if (eFormat
== rtl_math_StringFormat_G1
)
1659 eFormat
= rtl_math_StringFormat_E1
;
1663 nDecPlaces
= std::max
<sal_Int32
>(0, nDecPlaces
- nExp
- 1);
1664 eFormat
= rtl_math_StringFormat_F
;
1672 // Too large values for nDecPlaces make no sense; it might also be
1673 // rtl_math_DecimalPlaces_Max was passed with rtl_math_StringFormat_F or
1674 // others, but we don't want to allocate/deallocate 2GB just to fill it
1675 // with trailing '0' characters..
1676 nDecPlaces
= std::clamp
<sal_Int32
>(nDecPlaces
, -20, 20);
1678 sal_Int32 nDigits
= nDecPlaces
+ 1;
1680 if (eFormat
== rtl_math_StringFormat_F
)
1684 nRoundDigits
= std::min
<int>(nDigits
, nRoundDigits
);
1685 if (nDigits
>= 0 && nOrigDigits
> nRoundDigits
)
1687 aParts
.significand
= roundToPow10(aParts
.significand
, nOrigDigits
- nRoundDigits
);
1688 assert(aParts
.significand
<= eX
[nOrigDigits
- 1]);
1689 if (aParts
.significand
== eX
[nOrigDigits
- 1]) // up-rounding to the next decade
1694 if (eFormat
== rtl_math_StringFormat_F
)
1700 = (nDigits
<= 0 ? std::max
<sal_Int32
>(nDecPlaces
, std::abs(nExp
)) : nDigits
+ nDecPlaces
)
1701 + 10 + (pGroups
? std::abs(nDigits
) * 2 : 0);
1702 // max(nDigits) = max(nDecPlaces) + 1 + max(nExp) + 1 = 20 + 1 + 308 + 1 = 330
1703 // max(nBuf) = max(nDigits) + max(nDecPlaces) + 10 + max(nDigits) * 2 = 330 * 3 + 20 + 10 = 1020
1704 assert(nBuf
<= 1024);
1705 STRCODE
<S
>* const pBuf
= static_cast<STRCODE
<S
>*>(alloca(nBuf
* sizeof(STRCODE
<S
>)));
1706 STRCODE
<S
>* p
= pBuf
;
1710 bool bHasDec
= false;
1713 // Check for F format and number < 1
1714 if (eFormat
== rtl_math_StringFormat_F
)
1721 *p
++ = cDecSeparator
;
1725 sal_Int32 i
= (nDigits
<= 0 ? nDecPlaces
: -nExp
- 1);
1738 int nGrouping
= 0, nGroupSelector
= 0, nGroupExceed
= 0;
1739 if (nDecPos
> 1 && pGroups
&& pGroups
[0] && cGroupSeparator
)
1741 while (nGrouping
+ pGroups
[nGroupSelector
] < nDecPos
)
1743 nGrouping
+= pGroups
[nGroupSelector
];
1744 if (pGroups
[nGroupSelector
+ 1])
1746 if (nGrouping
+ pGroups
[nGroupSelector
+ 1] >= nDecPos
)
1751 else if (!nGroupExceed
)
1752 nGroupExceed
= nGrouping
;
1759 for (int nCurExp
= nOrigDigits
- 1;;)
1762 if (aParts
.significand
> 0 && nCurExp
> 0)
1765 nDigit
= aParts
.significand
/ eX
[nCurExp
];
1766 aParts
.significand
%= eX
[nCurExp
];
1770 nDigit
= aParts
.significand
;
1771 aParts
.significand
= 0;
1773 assert(nDigit
>= 0 && nDigit
< 10);
1774 *p
++ = nDigit
+ '0';
1783 *p
++ = cDecSeparator
;
1786 else if (nDecPos
== nGrouping
)
1788 *p
++ = cGroupSeparator
;
1789 nGrouping
-= pGroups
[nGroupSelector
];
1791 if (nGroupSelector
&& nGrouping
< nGroupExceed
)
1798 if (!bHasDec
&& eFormat
== rtl_math_StringFormat_F
)
1799 { // nDecPlaces < 0 did round the value
1800 while (--nDecPos
> 0)
1801 { // fill before decimal point
1802 if (nDecPos
== nGrouping
)
1804 *p
++ = cGroupSeparator
;
1805 nGrouping
-= pGroups
[nGroupSelector
];
1807 if (nGroupSelector
&& nGrouping
< nGroupExceed
)
1815 if (bEraseTrailingDecZeros
&& bHasDec
)
1817 while (*(p
- 1) == '0')
1820 if (*(p
- 1) == cDecSeparator
)
1824 // Print the exponent ('E', followed by '+' or '-', followed by exactly
1825 // three digits for rtl_math_StringFormat_E). The code in
1826 // rtl_[u]str_valueOf{Float|Double} relies on this format.
1827 if (eFormat
== rtl_math_StringFormat_E
|| eFormat
== rtl_math_StringFormat_E2
1828 || eFormat
== rtl_math_StringFormat_E1
)
1832 // maybe no nDigits if nDecPlaces < 0
1843 if (eFormat
== rtl_math_StringFormat_E
|| nExp
>= 100)
1844 *p
++ = nExp
/ 100 + '0';
1848 if (eFormat
== rtl_math_StringFormat_E
|| eFormat
== rtl_math_StringFormat_E2
|| nExp
>= 10)
1849 *p
++ = nExp
/ 10 + '0';
1851 *p
++ = nExp
% 10 + '0';
1854 if (!pResultCapacity
)
1855 newFromStr_WithLength(pResult
, pBuf
, p
- pBuf
);
1857 append(pResult
, pResultCapacity
, nResultOffset
, std::basic_string_view(pBuf
, p
- pBuf
));
1860 template <sal_Int32 maxLen
, typename C
, typename T
> sal_Int32 SAL_CALL
valueOfFP(C
* pStr
, T f
)
1863 STRINGDATA
<C
>* pResult
= nullptr;
1864 doubleToString(&pResult
, nullptr, 0, f
, rtl_math_StringFormat_G
,
1865 maxLen
- std::size("-x.E-xxx") + 1, '.', nullptr, 0, true);
1866 const sal_Int32 nLen
= pResult
->length
;
1867 assert(nLen
< maxLen
);
1868 Copy(pStr
, pResult
->buffer
, nLen
+ 1);
1875 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */