summaryrefslogtreecommitdiffstats
path: root/include/o3tl
diff options
context:
space:
mode:
Diffstat (limited to 'include/o3tl')
-rw-r--r--include/o3tl/any.hxx325
-rw-r--r--include/o3tl/char16_t2wchar_t.hxx48
-rw-r--r--include/o3tl/concepts.hxx45
-rw-r--r--include/o3tl/cow_wrapper.hxx387
-rw-r--r--include/o3tl/cppunittraitshelper.hxx28
-rw-r--r--include/o3tl/deleter.hxx68
-rw-r--r--include/o3tl/enumarray.hxx157
-rw-r--r--include/o3tl/enumrange.hxx87
-rw-r--r--include/o3tl/float_int_conversion.hxx72
-rw-r--r--include/o3tl/functional.hxx63
-rw-r--r--include/o3tl/hash_combine.hxx55
-rw-r--r--include/o3tl/intcmp.hxx132
-rw-r--r--include/o3tl/lazy_update.hxx89
-rw-r--r--include/o3tl/lru_map.hxx297
-rw-r--r--include/o3tl/make_shared.hxx46
-rw-r--r--include/o3tl/numeric.hxx28
-rw-r--r--include/o3tl/runtimetooustring.hxx47
-rw-r--r--include/o3tl/safeCoInitUninit.hxx58
-rw-r--r--include/o3tl/safeint.hxx236
-rw-r--r--include/o3tl/sorted_vector.hxx424
-rw-r--r--include/o3tl/sprintf.hxx41
-rw-r--r--include/o3tl/string_view.hxx558
-rw-r--r--include/o3tl/strong_int.hxx156
-rw-r--r--include/o3tl/temporary.hxx27
-rw-r--r--include/o3tl/typed_flags_set.hxx325
-rw-r--r--include/o3tl/underlyingenumvalue.hxx29
-rw-r--r--include/o3tl/unit_conversion.hxx269
-rw-r--r--include/o3tl/unreachable.hxx48
-rw-r--r--include/o3tl/unsafe_downcast.hxx35
-rw-r--r--include/o3tl/vector_pool.hxx123
-rw-r--r--include/o3tl/vector_utils.hxx29
31 files changed, 4332 insertions, 0 deletions
diff --git a/include/o3tl/any.hxx b/include/o3tl/any.hxx
new file mode 100644
index 0000000000..0acccff643
--- /dev/null
+++ b/include/o3tl/any.hxx
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_O3TL_ANY_HXX
+#define INCLUDED_O3TL_ANY_HXX
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+
+#include <optional>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <cppu/unotype.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+// Some functionality related to css::uno::Any that would ideally be part of
+// <com/sun/star/uno/Any.hxx>, but (for now) cannot be for some reason.
+
+namespace o3tl {
+
+namespace detail {
+
+struct Void {};
+
+template<typename T> struct Optional { using type = T const *; };
+template<> struct Optional<void> { using type = std::optional<Void const>; };
+template<> struct Optional<bool> { using type = std::optional<bool const>; };
+template<> struct Optional<sal_Int8> {
+ using type = std::optional<sal_Int8 const>;
+};
+template<> struct Optional<sal_Int16> {
+ using type = std::optional<sal_Int16 const>;
+};
+template<> struct Optional<sal_uInt16> {
+ using type = std::optional<sal_uInt16 const>;
+};
+template<> struct Optional<sal_Int32> {
+ using type = std::optional<sal_Int32 const>;
+};
+template<> struct Optional<sal_uInt32> {
+ using type = std::optional<sal_uInt32 const>;
+};
+template<> struct Optional<sal_Int64> {
+ using type = std::optional<sal_Int64 const>;
+};
+template<> struct Optional<sal_uInt64> {
+ using type = std::optional<sal_uInt64 const>;
+};
+template<> struct Optional<float> {
+ using type = std::optional<float const>;
+};
+template<> struct Optional<double> {
+ using type = std::optional<double const>;
+};
+template<typename T> struct Optional<css::uno::Reference<T>> {
+ using type = std::optional<css::uno::Reference<T> const>;
+};
+template<> struct Optional<css::uno::Reference<css::uno::XInterface>> {
+ using type = css::uno::Reference<css::uno::XInterface> const *;
+};
+
+template<typename> struct IsDerivedReference: std::false_type {};
+template<typename T> struct IsDerivedReference<css::uno::Reference<T>>:
+ std::true_type
+{};
+template<> struct IsDerivedReference<css::uno::Reference<css::uno::XInterface>>:
+ std::false_type
+{};
+
+template<typename> struct IsUnoSequenceType: std::false_type {};
+template<typename T> struct IsUnoSequenceType<cppu::UnoSequenceType<T>>:
+ std::true_type
+{};
+
+template<typename T> inline std::optional<T const> tryGetConverted(
+ css::uno::Any const & any)
+{
+ T v;
+ return (any >>= v)
+ ? std::optional<T const>(std::move(v)) : std::optional<T const>();
+}
+
+}
+
+/** Try to access the value of a specific type stored in an Any.
+
+ In trying to obtain a value, the same set of conversions as supported by
+ ">>=" are considered.
+
+ The returned object is a proxy. Proxies can be either positive or negative.
+ Each proxy can be contextually converted to bool, yielding true iff the
+ proxy is positive. For a positive proxy P representing a value of requested
+ type T, for any T other than void, the expression *P yields that value of
+ type T. (Technically, the proxy is either a plain pointer or a
+ std::optional, depending on whether a plain pointer into the given Any can
+ be returned for the specified type.)
+
+ @attention A proxy returned from this function must not outlive the
+ corresponding Any passed into this function (as it may constitute a pointer
+ into the Any's internals). That is the reason why this function is
+ restricted to lvalue arguments (i.e., to non-temporary Any objects), to
+ avoid misuses like
+ @code
+ css::uno::Any f();
+
+ if (auto p = o3tl::tryAccess<css::beans::NamedValue>(f())) {
+ return p->Name;
+ }
+ @endcode
+
+ @note Ideally this would be a public member function of css::uno::Any (at
+ least conditional on LIBO_INTERNAL_ONLY, as it requires C++11). However, as
+ std::optional (which would be needed to implement the proxies) is only
+ available since C++14, we need to use std::optional for now. But To not
+ make every entity that includes <com/sun/star/uno/Any.hxx> depend on
+ boost_headers, keep this here for now.
+
+ @tparam T the C++ representation of a UNO type that can be contained in a
+ UNO ANY (i.e., any UNO type other than ANY itself). The legacy C++
+ representations sal_Bool, cppu::UnoVoidType, cppu::UnoUnsignedShortType,
+ cppu::UnoCharType, and cppu::UnoSequenceType are not supported. Must be a
+ complete type or void.
+
+ @param any an Any value.
+
+ @return a positive proxy for the value of the specified type obtained from
+ the given Any, or a negative proxy if no such value can be obtained.
+*/
+template<typename T> inline
+typename std::enable_if<
+ !(detail::IsDerivedReference<T>::value
+ || detail::IsUnoSequenceType<T>::value
+ || std::is_base_of<css::uno::XInterface, T>::value),
+ typename detail::Optional<T>::type>::type
+tryAccess(css::uno::Any const & any) {
+ // CHAR, STRING, TYPE, sequence types, enum types, struct types, exception
+ // types, and com.sun.star.uno.XInterface interface type:
+ return cppu::UnoType<T>::get().isAssignableFrom(any.getValueType())
+ ? static_cast<T const *>(any.getValue()) : nullptr;
+}
+
+template<> inline detail::Optional<void>::type tryAccess<void>(
+ css::uno::Any const & any)
+{
+ return any.hasValue()
+ ? std::optional<detail::Void const>()
+ : std::optional<detail::Void const>(detail::Void());
+}
+
+template<> inline detail::Optional<bool>::type tryAccess<bool>(
+ css::uno::Any const & any)
+{
+ return detail::tryGetConverted<bool>(any);
+}
+
+template<> inline detail::Optional<sal_Int8>::type tryAccess<sal_Int8>(
+ css::uno::Any const & any)
+{
+ return detail::tryGetConverted<sal_Int8>(any);
+}
+
+template<> inline detail::Optional<sal_Int16>::type tryAccess<sal_Int16>(
+ css::uno::Any const & any)
+{
+ return detail::tryGetConverted<sal_Int16>(any);
+}
+
+template<> inline detail::Optional<sal_uInt16>::type tryAccess<sal_uInt16>(
+ css::uno::Any const & any)
+{
+ return detail::tryGetConverted<sal_uInt16>(any);
+}
+
+template<> inline detail::Optional<sal_Int32>::type tryAccess<sal_Int32>(
+ css::uno::Any const & any)
+{
+ return detail::tryGetConverted<sal_Int32>(any);
+}
+
+template<> inline detail::Optional<sal_uInt32>::type tryAccess<sal_uInt32>(
+ css::uno::Any const & any)
+{
+ return detail::tryGetConverted<sal_uInt32>(any);
+}
+
+template<> inline detail::Optional<sal_Int64>::type tryAccess<sal_Int64>(
+ css::uno::Any const & any)
+{
+ return detail::tryGetConverted<sal_Int64>(any);
+}
+
+template<> inline detail::Optional<sal_uInt64>::type tryAccess<sal_uInt64>(
+ css::uno::Any const & any)
+{
+ return detail::tryGetConverted<sal_uInt64>(any);
+}
+
+template<> inline detail::Optional<float>::type tryAccess<float>(
+ css::uno::Any const & any)
+{
+ return detail::tryGetConverted<float>(any);
+}
+
+template<> inline detail::Optional<double>::type tryAccess<double>(
+ css::uno::Any const & any)
+{
+ return detail::tryGetConverted<double>(any);
+}
+
+template<> detail::Optional<css::uno::Any>::type tryAccess<css::uno::Any>(
+ css::uno::Any const &) = delete;
+
+template<> detail::Optional<sal_Bool>::type tryAccess<sal_Bool>(
+ css::uno::Any const &) = delete;
+
+/*
+
+// Already prevented by std::is_base_of<css::uno::XInterface, T> requiring T to
+// be complete:
+
+template<> detail::Optional<cppu::UnoVoidType>::type
+tryAccess<cppu::UnoVoidType>(css::uno::Any const &) = delete;
+
+template<> detail::Optional<cppu::UnoUnsignedShortType>::type
+tryAccess<cppu::UnoUnsignedShortType>(css::uno::Any const &) = delete;
+
+template<> detail::Optional<cppu::UnoCharType>::type
+tryAccess<cppu::UnoCharType>(css::uno::Any const &) = delete;
+
+*/
+
+template<typename T> inline
+typename std::enable_if<
+ detail::IsDerivedReference<T>::value,
+ typename detail::Optional<T>::type>::type
+tryAccess(css::uno::Any const & any) {
+ return detail::tryGetConverted<T>(any);
+}
+
+template<typename T> typename detail::Optional<T>::type tryAccess(
+ css::uno::Any const volatile &&) = delete;
+
+/** Access the value of a specific type stored in an Any, throwing an exception
+ on failure.
+
+ @attention A proxy returned from this function must not outlive the
+ corresponding Any passed into this function (as it may constitute a pointer
+ into the Any's internals). However, unlike with tryAccess, the benefit of
+ allowing this function to operate on temporaries appears to outweigh its
+ dangers.
+
+ @note Ideally this would be a public member function of css::uno::Any. See
+ tryAccess for details.
+
+ @tparam T the C++ representation of a UNO type that can be contained in a
+ UNO ANY. See tryAccess for details.
+
+ @param any an Any value.
+
+ @return a positive proxy for the value of the specified type obtained from
+ the given Any. See tryAccess for details.
+
+ @throws css::uno::RuntimeException when a value of the requested type
+ cannot be obtained.
+*/
+template<typename T> inline typename detail::Optional<T>::type doAccess(
+ css::uno::Any const & any)
+{
+ auto opt = tryAccess<T>(any);
+ if (!opt) {
+ throw css::uno::RuntimeException(
+ OUString(
+ cppu_Any_extraction_failure_msg(
+ &any, cppu::UnoType<T>::get().getTypeLibType()),
+ SAL_NO_ACQUIRE));
+ }
+ return opt;
+}
+
+/** Access the value of a specific type stored in an Any, knowing the Any
+ contains a value of a matching type.
+
+ @attention A proxy returned from this function must not outlive the
+ corresponding Any passed into this function (as it may constitute a pointer
+ into the Any's internals). However, unlike with tryAccess, the benefit of
+ allowing this function to operate on temporaries appears to outweigh its
+ dangers.
+
+ @note Ideally this would be a public member function of css::uno::Any. See
+ tryAccess for details.
+
+ @tparam T the C++ representation of a UNO type that can be contained in a
+ UNO ANY. See tryAccess for details.
+
+ @param any an Any value.
+
+ @return a positive proxy for the value of the specified type obtained from
+ the given Any. See tryAccess for details.
+*/
+template<typename T> inline typename detail::Optional<T>::type forceAccess(
+ css::uno::Any const & any)
+{
+ auto opt = tryAccess<T>(any);
+ assert(opt);
+ return opt;
+}
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/char16_t2wchar_t.hxx b/include/o3tl/char16_t2wchar_t.hxx
new file mode 100644
index 0000000000..c2640f6271
--- /dev/null
+++ b/include/o3tl/char16_t2wchar_t.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+namespace o3tl
+{
+#if defined _WIN32
+// Helpers for safe conversion between wchar_t and char16_t in MSVC
+
+static_assert(sizeof(char16_t) == sizeof(wchar_t),
+ "These helper functions are only applicable to implementations with 16-bit wchar_t");
+
+// While other implementations define wchar_t as 32-bit integral value, and mostly use
+// char-based UTF-8 string APIs, in MSVC wchar_t is (non-conformant) 16-bit, and Unicode
+// support is implemented by Unicode-aware WinAPI functions taking UTF-16 LE strings,
+// and also stdlib functions taking those strings.
+//
+// In LibreOffice, internal string representation is also UTF-16 with system endianness
+// (sal_Unicode that is typedef for char16_t); so it is an important implementation concept
+// to treat internal strings as directly usable by WinAPI/stdlib functions and vice versa.
+// Also, it's important to use safe conversion between unrelated underlying C++ types
+// used for MSVC and LibreOffice string storage without plain reinterpret_cast that brings
+// risks of masking errors like casting char buffers to wchar_t/char16_t.
+//
+// Use these helpers for wchar_t (WSTR, WCHAR, OLESTR etc) to char16_t (sal_Unicode) string
+// conversions instead of reinterpret-cast in Windows-specific code.
+inline wchar_t* toW(char16_t* p) { return reinterpret_cast<wchar_t*>(p); }
+inline wchar_t const* toW(char16_t const* p) { return reinterpret_cast<wchar_t const*>(p); }
+inline char16_t* toU(wchar_t* p) { return reinterpret_cast<char16_t*>(p); }
+inline char16_t const* toU(wchar_t const* p) { return reinterpret_cast<char16_t const*>(p); }
+
+inline std::u16string_view toU(std::wstring_view v) { return { toU(v.data()), v.size() }; }
+inline std::wstring_view toW(std::u16string_view v) { return { toW(v.data()), v.size() }; }
+#endif
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/concepts.hxx b/include/o3tl/concepts.hxx
new file mode 100644
index 0000000000..261d063ebc
--- /dev/null
+++ b/include/o3tl/concepts.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <concepts>
+
+// LLVM 12 libc++ only provides a bare-bones <concepts> that lacks most of its C++20 content, so
+// replicate here fore now what we need:
+
+#if defined __cpp_lib_concepts
+
+namespace o3tl
+{
+using std::integral;
+using std::signed_integral;
+using std::unsigned_integral;
+}
+
+#else
+
+#include <type_traits>
+
+namespace o3tl
+{
+// Taken from the C++20 spec:
+
+template <typename T> concept integral = std::is_integral_v<T>;
+
+template <typename T> concept signed_integral = integral<T>&& std::is_signed_v<T>;
+
+template <typename T> concept unsigned_integral = integral<T> && !signed_integral<T>;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/cow_wrapper.hxx b/include/o3tl/cow_wrapper.hxx
new file mode 100644
index 0000000000..cc823f60ec
--- /dev/null
+++ b/include/o3tl/cow_wrapper.hxx
@@ -0,0 +1,387 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_O3TL_COW_WRAPPER_HXX
+#define INCLUDED_O3TL_COW_WRAPPER_HXX
+
+#include <osl/interlck.h>
+
+#include <optional>
+#include <cstddef>
+
+namespace o3tl
+{
+ /** Thread-unsafe refcounting
+
+ This is the default locking policy for cow_wrapper. No
+ locking/guarding against concurrent access is performed
+ whatsoever.
+ */
+ struct UnsafeRefCountingPolicy
+ {
+ typedef std::size_t ref_count_t;
+ static void incrementCount( ref_count_t& rCount ) { ++rCount; }
+ static bool decrementCount( ref_count_t& rCount ) { return --rCount != 0; }
+ };
+
+ /** Thread-safe refcounting
+
+ Use this to have the cow_wrapper refcounting mechanisms employ
+ the thread-safe oslInterlockedCount .
+ */
+ struct ThreadSafeRefCountingPolicy
+ {
+ typedef oslInterlockedCount ref_count_t;
+ static void incrementCount( ref_count_t& rCount ) { osl_atomic_increment(&rCount); }
+ static bool decrementCount( ref_count_t& rCount )
+ {
+ return osl_atomic_decrement(&rCount) != 0;
+ }
+ };
+
+ /** Copy-on-write wrapper.
+
+ This template provides copy-on-write semantics for the wrapped
+ type: when copying, the operation is performed shallow,
+ i.e. different cow_wrapper objects share the same underlying
+ instance. Only when accessing the underlying object via
+ non-const methods, a unique copy is provided.
+
+ The type parameter <code>T</code> must satisfy the following
+ requirements: it must be default-constructible, copyable (it
+ need not be assignable), and be of non-reference type. Note
+ that, despite the fact that this template provides access to
+ the wrapped type via pointer-like methods
+ (<code>operator->()</code> and <code>operator*()</code>), it does
+ <em>not</em> work like e.g. the std smart pointer wrappers
+ (shared_ptr, unique_ptr, etc.). Internally, the cow_wrapper
+ holds a by-value instance of the wrapped object. This is to
+ avoid one additional heap allocation, and providing access via
+ <code>operator->()</code>/<code>operator*()</code> is because
+ <code>operator.()</code> cannot be overridden.
+
+ Regarding thread safety: this wrapper is <em>not</em>
+ thread-safe per se, because cow_wrapper has no way of
+ synchronizing the potentially many different cow_wrapper
+ instances, that reference a single shared value_type
+ instance. That said, when passing
+ <code>ThreadSafeRefCountingPolicy</code> as the
+ <code>MTPolicy</code> parameter, accessing a thread-safe
+ pointee through multiple cow_wrapper instances might be
+ thread-safe, if the individual pointee methods are
+ thread-safe, <em>including</em> pointee's copy
+ constructor. Any wrapped object that needs external
+ synchronisation (e.g. via an external mutex, which arbitrates
+ access to object methods, and can be held across multiple
+ object method calls) cannot easily be dealt with in a
+ thread-safe way, because, as noted, objects are shared behind
+ the client's back.
+
+ @attention if one wants to use the pimpl idiom together with
+ cow_wrapper (i.e. put an opaque type into the cow_wrapper),
+ then <em>all<em> methods in the surrounding class needs to be
+ non-inline (<em>including</em> destructor, copy constructor
+ and assignment operator).
+
+ @example
+ <pre>
+class cow_wrapper_client_impl;
+
+class cow_wrapper_client
+{
+public:
+ cow_wrapper_client();
+ cow_wrapper_client( const cow_wrapper_client& );
+ cow_wrapper_client( cow_wrapper_client&& );
+ ~cow_wrapper_client();
+
+ cow_wrapper_client& operator=( const cow_wrapper_client& );
+ cow_wrapper_client& operator=( cow_wrapper_client&& );
+
+ void modify( int nVal );
+ int queryUnmodified() const;
+
+private:
+ o3tl::cow_wrapper< cow_wrapper_client_impl > maImpl;
+};
+ </pre>
+ and the implementation file would look like this:
+ <pre>
+class cow_wrapper_client_impl
+{
+public:
+ void setValue( int nVal ) { mnValue = nVal; }
+ int getValue() const { return mnValue; }
+
+private:
+ int mnValue;
+}
+
+cow_wrapper_client::cow_wrapper_client() :
+ maImpl()
+{
+}
+cow_wrapper_client::cow_wrapper_client( const cow_wrapper_client& rSrc ) :
+ maImpl( rSrc.maImpl )
+{
+}
+cow_wrapper_client::cow_wrapper_client( cow_wrapper_client& rSrc ) :
+ maImpl( std::move( rSrc.maImpl ) )
+{
+}
+cow_wrapper_client::~cow_wrapper_client()
+{
+}
+cow_wrapper_client& cow_wrapper_client::operator=( const cow_wrapper_client& rSrc )
+{
+ maImpl = rSrc.maImpl;
+ return *this;
+}
+cow_wrapper_client& cow_wrapper_client::operator=( cow_wrapper_client&& rSrc )
+{
+ maImpl = std::move( rSrc.maImpl );
+ return *this;
+}
+void cow_wrapper_client::modify( int nVal )
+{
+ maImpl->setValue( nVal );
+}
+int cow_wrapper_client::queryUnmodified() const
+{
+ return maImpl->getValue();
+}
+ </pre>
+ */
+ template<typename T, class MTPolicy=UnsafeRefCountingPolicy> class cow_wrapper
+ {
+ /** shared value object - gets cloned before cow_wrapper hands
+ out a non-const reference to it
+ */
+ struct impl_t
+ {
+ impl_t(const impl_t&) = delete;
+ impl_t& operator=(const impl_t&) = delete;
+
+ impl_t() :
+ m_value(),
+ m_ref_count(1)
+ {
+ }
+
+ explicit impl_t( const T& v ) :
+ m_value(v),
+ m_ref_count(1)
+ {
+ }
+
+ explicit impl_t( T&& v ) :
+ m_value(std::move(v)),
+ m_ref_count(1)
+ {
+ }
+
+ T m_value;
+ typename MTPolicy::ref_count_t m_ref_count;
+ };
+
+ void release()
+ {
+ if( m_pimpl && !MTPolicy::decrementCount(m_pimpl->m_ref_count) )
+ {
+ delete m_pimpl;
+ m_pimpl = nullptr;
+ }
+ }
+
+ public:
+ typedef T value_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef MTPolicy mt_policy;
+
+ /** Default-construct wrapped type instance
+ */
+ cow_wrapper() :
+ m_pimpl( new impl_t() )
+ {
+ }
+
+ /** Copy-construct wrapped type instance from given object
+ */
+ explicit cow_wrapper( const value_type& r ) :
+ m_pimpl( new impl_t(r) )
+ {
+ }
+
+ /** Move-construct wrapped type instance from given object
+ */
+ explicit cow_wrapper( value_type&& r ) :
+ m_pimpl( new impl_t(std::move(r)) )
+ {
+ }
+
+ /** Shallow-copy given cow_wrapper
+ */
+ explicit cow_wrapper( const cow_wrapper& rSrc ) : // nothrow
+ m_pimpl( rSrc.m_pimpl )
+ {
+ MTPolicy::incrementCount( m_pimpl->m_ref_count );
+ }
+
+ /** Move-construct and steal rSrc shared resource
+ */
+ explicit cow_wrapper( cow_wrapper&& rSrc ) noexcept :
+ m_pimpl( rSrc.m_pimpl )
+ {
+ rSrc.m_pimpl = nullptr;
+ }
+
+ // Only intended to be used by std::optional specialisations
+ explicit cow_wrapper( std::nullopt_t ) noexcept :
+ m_pimpl( nullptr )
+ {
+ }
+
+ // Only intended to be used by std::optional specialisations
+ explicit cow_wrapper( const cow_wrapper& rSrc, std::nullopt_t ) : // nothrow
+ m_pimpl( rSrc.m_pimpl )
+ {
+ if (m_pimpl)
+ MTPolicy::incrementCount( m_pimpl->m_ref_count );
+ }
+
+ ~cow_wrapper() // nothrow, if ~T does not throw
+ {
+ release();
+ }
+
+ /// now sharing rSrc cow_wrapper instance with us
+ cow_wrapper& operator=( const cow_wrapper& rSrc ) // nothrow
+ {
+ // this already guards against self-assignment
+ MTPolicy::incrementCount( rSrc.m_pimpl->m_ref_count );
+
+ release();
+ m_pimpl = rSrc.m_pimpl;
+
+ return *this;
+ }
+
+ /// stealing rSrc's resource
+ cow_wrapper& operator=(cow_wrapper&& rSrc) noexcept
+ {
+ // self-movement guts ourself, see also 17.6.4.9
+ release();
+ m_pimpl = rSrc.m_pimpl;
+
+ rSrc.m_pimpl = nullptr;
+
+ return *this;
+ }
+
+ /// unshare with any other cow_wrapper instance
+ value_type& make_unique()
+ {
+ if( m_pimpl->m_ref_count > 1 )
+ {
+ impl_t* pimpl = new impl_t(m_pimpl->m_value);
+ release();
+ m_pimpl = pimpl;
+ }
+
+ return m_pimpl->m_value;
+ }
+
+ /// true, if not shared with any other cow_wrapper instance
+ bool is_unique() const // nothrow
+ {
+ return !m_pimpl || m_pimpl->m_ref_count == 1;
+ }
+
+ /// return number of shared instances (1 for unique object)
+ typename MTPolicy::ref_count_t use_count() const // nothrow
+ {
+ return m_pimpl ? m_pimpl->m_ref_count : 0;
+ }
+
+ void swap(cow_wrapper& r) // never throws
+ {
+ std::swap(m_pimpl, r.m_pimpl);
+ }
+
+ pointer operator->() { return &make_unique(); }
+ value_type& operator*() { return make_unique(); }
+ const_pointer operator->() const { return &m_pimpl->m_value; }
+ const value_type& operator*() const { return m_pimpl->m_value; }
+
+ pointer get() { return &make_unique(); }
+ const_pointer get() const { return &m_pimpl->m_value; }
+
+ /// true, if both cow_wrapper internally share the same object
+ bool same_object( const cow_wrapper& rOther ) const
+ {
+ return rOther.m_pimpl == m_pimpl;
+ }
+
+ // Only intended to be used by std::optional specialisations
+ bool empty() const { return m_pimpl == nullptr; }
+ // Only intended to be used by std::optional specialisations
+ void set_empty()
+ {
+ if (m_pimpl)
+ {
+ release();
+ m_pimpl = nullptr;
+ }
+ }
+
+ private:
+ impl_t* m_pimpl;
+ };
+
+
+ template<class T, class P> inline bool operator==( const cow_wrapper<T,P>& a,
+ const cow_wrapper<T,P>& b )
+ {
+ return a.same_object(b) || *a == *b;
+ }
+
+ template<class T, class P> inline bool operator!=( const cow_wrapper<T,P>& a,
+ const cow_wrapper<T,P>& b )
+ {
+ return !a.same_object(b) && *a != *b;
+ }
+
+ template<class A, class B, class P> inline bool operator<( const cow_wrapper<A,P>& a,
+ const cow_wrapper<B,P>& b )
+ {
+ return *a < *b;
+ }
+
+ template<class T, class P> inline void swap( cow_wrapper<T,P>& a,
+ cow_wrapper<T,P>& b )
+ {
+ a.swap(b);
+ }
+
+}
+
+#endif /* INCLUDED_O3TL_COW_WRAPPER_HXX */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/cppunittraitshelper.hxx b/include/o3tl/cppunittraitshelper.hxx
new file mode 100644
index 0000000000..2a6fb88362
--- /dev/null
+++ b/include/o3tl/cppunittraitshelper.hxx
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_O3TL_CPPUNITTRAITSHELPER_HXX
+#define INCLUDED_O3TL_CPPUNITTRAITSHELPER_HXX
+
+#include <sal/config.h>
+
+#include <cstdint>
+#include <string>
+
+#include <cppunit/TestAssert.h>
+
+// ostream << char16_t is deleted since C++20 (but just keep outputting numeric values):
+template <> inline std::string CppUnit::assertion_traits<char16_t>::toString(char16_t const& x)
+{
+ return assertion_traits<std::uint_least16_t>::toString(std::uint_least16_t(x));
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/deleter.hxx b/include/o3tl/deleter.hxx
new file mode 100644
index 0000000000..76d8d5a357
--- /dev/null
+++ b/include/o3tl/deleter.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_O3TL_DELETER_HXX
+#define INCLUDED_O3TL_DELETER_HXX
+
+#include <sal/config.h>
+
+#include <cstdlib>
+
+#if defined(__COVERITY__)
+#define suppress_fun_call_w_exception(expr) \
+ do \
+ { \
+ try \
+ { \
+ expr; \
+ } \
+ catch (...) \
+ { \
+ std::terminate(); \
+ } \
+ } while (false)
+#else
+#define suppress_fun_call_w_exception(expr) \
+ do \
+ { \
+ expr; \
+ } while (false)
+#endif
+
+namespace o3tl
+{
+/** To markup std::unique_ptr that coverity warns might throw exceptions
+ which won't throw in practice, or where std::terminate is
+ an acceptable response if they do
+*/
+template <typename T> struct default_delete
+{
+ void operator()(T* p) noexcept { suppress_fun_call_w_exception(delete p); }
+};
+
+struct free_delete
+{
+ void operator()(void* p) { std::free(p); }
+};
+
+template <typename uniqueptr> void reset_preserve_ptr_during(uniqueptr& ptr)
+{
+ // HACK: for the case where the dtor of the obj held by ptr will trigger
+ // functions which expect ptr to still be set during the dtor.
+ // e.g. SdrObject::GetBroadcaster() is called during the destructor
+ // in SdrEdgeObj::Notify(). So delete first, then clear the pointer
+ delete ptr.get();
+ // coverity[leaked_storage] - not a leak, deleted on line above
+ (void)ptr.release();
+}
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/enumarray.hxx b/include/o3tl/enumarray.hxx
new file mode 100644
index 0000000000..4fd5cab315
--- /dev/null
+++ b/include/o3tl/enumarray.hxx
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_O3TL_ENUMARRAY_HXX
+#define INCLUDED_O3TL_ENUMARRAY_HXX
+
+#include <iterator>
+#include <type_traits>
+#include <utility>
+#include <array>
+#include <cassert>
+
+namespace o3tl {
+
+template<typename EA>
+class enumarray_iterator;
+template<typename EA>
+class enumarray_const_iterator;
+
+///
+/// This is a container convenience class for arrays indexed by enum values.
+///
+/// This assumes that the 'enum class' definition
+/// - starts at zero
+/// - has no holes in its sequence of values
+/// - defines a value called LAST which refers to the greatest constant.
+///
+/// \param E the 'enum class' type.
+/// \param V the value type to be stored in the array
+template<typename E, typename V>
+class enumarray final
+{
+public:
+ typedef enumarray<E, V> self_type;
+ typedef enumarray_iterator<self_type> iterator;
+ typedef enumarray_const_iterator<self_type> const_iterator;
+
+ typedef V value_type;
+ typedef E key_type;
+ typedef size_t size_type;
+
+ static const size_type max_index = static_cast<size_type>(E::LAST);
+
+ // If this ctor only had the args parameter pack, it would erroneously get picked as a better
+ // choice than the (implicit) copy ctor (taking a const lvalue reference) when a copy is made
+ // from a non-const lvalue enumarray; the easiest way to avoid that is the additional arg
+ // parameter; and to keep things simple that parameter is always passed by const lvalue
+ // reference for now even if there could be cases where passing it by rvalue reference might be
+ // beneficial or even necessary if V is a move-only type:
+ template<typename... T> constexpr enumarray(V const & arg, T && ...args):
+ detail_values{arg, std::forward<T>(args)...}
+ {
+ static_assert(sizeof... (T) == max_index);
+ }
+
+ // coverity[uninit_ctor] - by design:
+ enumarray() {}
+
+ const V& operator[](E index) const
+ {
+ assert(index>=static_cast<E>(0) && index<=E::LAST);
+ return detail_values[static_cast<size_type>(index)];
+ }
+
+ V& operator[](E index)
+ {
+ assert(index>=static_cast<E>(0) && index<=E::LAST);
+ return detail_values[static_cast<size_type>(index)];
+ }
+
+ void fill(V val)
+ { for (size_type i=0; i<=max_index; ++i) detail_values[i] = val; }
+
+ static size_type size() { return max_index + 1; }
+ iterator begin() { return iterator(*this, 0); }
+ iterator end() { return iterator(*this, size()); }
+ const_iterator begin() const { return const_iterator(*this, 0); }
+ const_iterator end() const { return const_iterator(*this, size()); }
+
+ V* data() { return detail_values.data(); }
+
+private:
+ std::array<V, max_index + 1> detail_values;
+};
+
+
+template<typename EA>
+class enumarray_iterator {
+ EA* m_buf;
+ size_t m_pos;
+public:
+ typedef enumarray_iterator<EA> self_type;
+ typedef typename EA::value_type value_type;
+ typedef typename EA::key_type key_type;
+ typedef std::bidirectional_iterator_tag iterator_category; //should be random access, but that would require define subtraction operators on the enums
+ typedef
+ typename std::make_signed<
+ typename std::underlying_type<typename EA::key_type>::type>::type
+ difference_type;
+ typedef typename EA::value_type* pointer;
+ typedef typename EA::value_type& reference;
+
+ enumarray_iterator(EA& b, size_t start_pos)
+ : m_buf(&b), m_pos(start_pos) {}
+ value_type& operator*() const { return (*m_buf)[static_cast<key_type>(m_pos)]; }
+ value_type* operator->() const { return &(operator*()); }
+ self_type& operator++() { ++m_pos; return *this; }
+ bool operator!=(self_type const & other) const { return m_buf != other.m_buf || m_pos != other.m_pos; }
+ bool operator==(self_type const & other) const { return m_buf == other.m_buf && m_pos == other.m_pos; }
+};
+
+template<typename EA>
+class enumarray_const_iterator {
+ EA const * m_buf;
+ size_t m_pos;
+public:
+ typedef enumarray_const_iterator<EA> self_type;
+ typedef typename EA::value_type const value_type;
+ typedef typename EA::key_type key_type;
+ typedef std::bidirectional_iterator_tag iterator_category; //should be random access, but that would require define subtraction operators on the enums
+ typedef
+ typename std::make_signed<
+ typename std::underlying_type<typename EA::key_type>::type>::type
+ difference_type;
+ typedef typename EA::value_type const * pointer;
+ typedef typename EA::value_type const & reference;
+
+ enumarray_const_iterator(EA const & b, size_t start_pos)
+ : m_buf(&b), m_pos(start_pos) {}
+ value_type& operator*() const { return (*m_buf)[static_cast<key_type>(m_pos)]; }
+ value_type* operator->() const { return &(operator*()); }
+ self_type& operator++() { ++m_pos; return *this; }
+ bool operator!=(self_type const & other) const { return m_buf != other.m_buf || m_pos != other.m_pos; }
+ bool operator==(self_type const & other) const { return m_buf == other.m_buf && m_pos == other.m_pos; }
+};
+
+}; // namespace o3tl
+
+#endif /* INCLUDED_O3TL_ENUMARRAY_HXX */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/enumrange.hxx b/include/o3tl/enumrange.hxx
new file mode 100644
index 0000000000..60f069c9f5
--- /dev/null
+++ b/include/o3tl/enumrange.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_O3TL_ENUMRANGE_HXX
+#define INCLUDED_O3TL_ENUMRANGE_HXX
+
+namespace o3tl {
+
+///
+/// This is a container convenience class iterating over scoped enumerations.
+///
+/// This assumes that the 'enum class' definition
+/// - starts at zero
+/// - has no holes in its sequence of values
+/// - defines a value called LAST which refers to the greatest constant.
+///
+/// Use like this:
+/// enum class COLOR { RED, GREEN, BLUE, LAST=BLUE };
+/// for( auto e : o3tl::enumrange<Color>() )
+/// ....;
+///
+/// \param T the 'enum class' type.
+template< typename T>
+class enumrange
+{
+public:
+ class Iterator
+ {
+ public:
+ Iterator( int value ) :
+ m_value( value )
+ {
+ }
+
+ T operator*( void ) const
+ {
+ return static_cast<T>(m_value);
+ }
+
+ void operator++( void )
+ {
+ ++m_value;
+ }
+
+ bool operator!=( Iterator rhs ) const
+ {
+ return m_value != rhs.m_value;
+ }
+
+ private:
+ int m_value;
+ };
+};
+
+template< typename T >
+typename enumrange<T>::Iterator begin( enumrange<T> )
+{
+ return typename enumrange<T>::Iterator( int(0) );
+}
+
+template< typename T >
+typename enumrange<T>::Iterator end( enumrange<T> )
+{
+ return typename enumrange<T>::Iterator( static_cast<int>(T::LAST) + 1 );
+}
+
+}; // namespace o3tl
+
+#endif /* INCLUDED_O3TL_ENUMRANGE_HXX */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/float_int_conversion.hxx b/include/o3tl/float_int_conversion.hxx
new file mode 100644
index 0000000000..403bed6221
--- /dev/null
+++ b/include/o3tl/float_int_conversion.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_O3TL_FLOAT_INT_CONVERSION_HXX
+#define INCLUDED_O3TL_FLOAT_INT_CONVERSION_HXX
+
+#include <sal/config.h>
+
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+namespace o3tl
+{
+// Return true iff `value` of floating-point type `F` converts to a value of integral type `I` no
+// smaller than `min`:
+template <typename F, typename I>
+constexpr std::enable_if_t<std::is_floating_point_v<F> && std::is_integral_v<I>, bool>
+convertsToAtLeast(F value, I min)
+{
+ // If `F(min)`, `F(min) - F(1)` are too large in magnitude for `F`'s precision, then they either
+ // fall into the same bucket, in which case we should return false if `value` represents that
+ // bucket, or they are on the boundary of two adjacent buckets, in which case we should return
+ // true if `value`represents the higher bucket containing `F(min)`:
+ return value > F(min) - F(1);
+}
+
+// Return true iff `value` of floating-point type `F` converts to a value of integral type `I` no
+// larger than `max`:
+template <typename F, typename I>
+constexpr std::enable_if_t<std::is_floating_point_v<F> && std::is_integral_v<I>, bool>
+convertsToAtMost(F value, I max)
+{
+ // If `F(max)`, `F(max) + F(1)` are too large in magnitude for `F`'s precision, then they either
+ // fall into the same bucket, in which case we should return false if `value` represents that
+ // bucket, or they are on the boundary of two adjacent buckets, in which case we should return
+ // true if `value`represents the lower bucket containing `F(max)`:
+ return value < F(max) + F(1);
+}
+
+// Casts a floating-point to an integer, avoiding overflow. Used like:
+// sal_Int64 n = o3tl::saturating_cast<sal_Int64>(f);
+template <typename I, typename F>
+constexpr std::enable_if_t<std::is_floating_point_v<F> && std::is_integral_v<I>, I>
+saturating_cast(F f)
+{
+ if constexpr (std::is_signed_v<I>)
+ if (!convertsToAtLeast(f, std::numeric_limits<I>::min()))
+ return std::numeric_limits<I>::min();
+ if (!convertsToAtMost(f, std::numeric_limits<I>::max()))
+ return std::numeric_limits<I>::max();
+ return f;
+}
+
+// Return `value` of floating-point type `F` rounded to the nearest integer away from zero (which
+// can be useful in calls to convertsToAtLeast/Most(roundAway(x), n), to reject x that are
+// smaller/larger than n because they have a fractional part):
+template <typename F> std::enable_if_t<std::is_floating_point_v<F>, F> roundAway(F value)
+{
+ return value >= 0 ? std::ceil(value) : std::floor(value);
+}
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/functional.hxx b/include/o3tl/functional.hxx
new file mode 100644
index 0000000000..9ca86924f8
--- /dev/null
+++ b/include/o3tl/functional.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ *
+ * Copyright (c) 1994
+ * Hewlett-Packard Company
+ *
+ * Copyright (c) 1996-1998
+ * Silicon Graphics Computer Systems, Inc.
+ *
+ * Copyright (c) 1997
+ * Moscow Center for SPARC Technology
+ *
+ * Copyright (c) 1999
+ * Boris Fomitchev
+ *
+ * This material is provided "as is", with absolutely no warranty expressed
+ * or implied. Any use is at your own risk.
+ *
+ * Permission to use or copy this software for any purpose is hereby granted
+ * without fee, provided the above notices are retained on all copies.
+ * Permission to modify the code and to distribute modified code is granted,
+ * provided the above notices are retained, and a notice that the code was
+ * modified is included with the above copyright notice.
+ *
+ */
+
+/*
+ * Lifted and paraphrased from STLport - with additions from Fridrich
+ * Strba and Thorsten Behrens
+ */
+
+#ifndef INCLUDED_O3TL_FUNCTIONAL_HXX
+#define INCLUDED_O3TL_FUNCTIONAL_HXX
+
+namespace o3tl
+{
+/// Select first value of a pair
+template<typename P>
+struct select1st
+{
+ typedef P argument_type;
+ typedef typename P::first_type result_type;
+ const result_type& operator()( const argument_type& cp ) const {
+ return cp.first;
+ }
+};
+
+/// Select second value of a pair
+template<typename P>
+struct select2nd
+{
+ typedef P argument_type;
+ typedef typename P::second_type result_type;
+ const result_type& operator()( const argument_type& cp ) const {
+ return cp.second;
+ }
+};
+
+} // namespace o3tl
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/hash_combine.hxx b/include/o3tl/hash_combine.hxx
new file mode 100644
index 0000000000..48e1426222
--- /dev/null
+++ b/include/o3tl/hash_combine.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <functional>
+#include <type_traits>
+
+namespace o3tl
+{
+template <typename T, typename N>
+inline std::enable_if_t<(sizeof(N) == 4)> hash_combine(N& nSeed, T const* pValue, size_t nCount)
+{
+ static_assert(sizeof(nSeed) == 4);
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ nSeed ^= std::hash<T>{}(*pValue) + 0x9E3779B9u + (nSeed << 6) + (nSeed >> 2);
+ ++pValue;
+ }
+}
+
+template <typename T, typename N>
+inline std::enable_if_t<(sizeof(N) == 4)> hash_combine(N& nSeed, T const& nValue)
+{
+ static_assert(sizeof(nSeed) == 4);
+ nSeed ^= std::hash<T>{}(nValue) + 0x9E3779B9u + (nSeed << 6) + (nSeed >> 2);
+}
+
+template <typename T, typename N>
+inline std::enable_if_t<(sizeof(N) == 8)> hash_combine(N& nSeed, T const* pValue, size_t nCount)
+{
+ static_assert(sizeof(nSeed) == 8);
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ nSeed ^= std::hash<T>{}(*pValue) + 0x9E3779B97F4A7C15llu + (nSeed << 12) + (nSeed >> 4);
+ ++pValue;
+ }
+}
+
+template <typename T, typename N>
+inline std::enable_if_t<(sizeof(N) == 8)> hash_combine(N& nSeed, T const& nValue)
+{
+ static_assert(sizeof(nSeed) == 8);
+ nSeed ^= std::hash<T>{}(nValue) + 0x9E3779B97F4A7C15llu + (nSeed << 12) + (nSeed >> 4);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/intcmp.hxx b/include/o3tl/intcmp.hxx
new file mode 100644
index 0000000000..dbc10d9052
--- /dev/null
+++ b/include/o3tl/intcmp.hxx
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <type_traits>
+#include <utility>
+
+#include <o3tl/safeint.hxx>
+
+namespace o3tl
+{
+// An approximation of the C++20 integer comparison functions
+// (<https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0586r2.html> "Safe integral
+// comparisons"), still missing from LLVM 12 libc++:
+#if defined __cpp_lib_integer_comparison_functions
+
+using std::cmp_equal;
+using std::cmp_not_equal;
+using std::cmp_less;
+using std::cmp_greater;
+using std::cmp_less_equal;
+using std::cmp_greater_equal;
+
+#else
+
+template <typename T1, typename T2> constexpr bool cmp_equal(T1 value1, T2 value2) noexcept
+{
+ // coverity[same_on_both_sides: FALSE]
+ if constexpr (std::is_signed_v<T1> == std::is_signed_v<T2>)
+ {
+ return value1 == value2;
+ }
+ else if constexpr (std::is_signed_v<T1>)
+ {
+ return value1 >= 0 && o3tl::make_unsigned(value1) == value2;
+ }
+ else
+ {
+ return value2 >= 0 && value1 == o3tl::make_unsigned(value2);
+ }
+}
+
+template <typename T1, typename T2> constexpr bool cmp_not_equal(T1 value1, T2 value2) noexcept
+{
+ return !cmp_equal(value1, value2);
+}
+
+template <typename T1, typename T2> constexpr bool cmp_less(T1 value1, T2 value2) noexcept
+{
+ if constexpr (std::is_signed_v<T1> == std::is_signed_v<T2>)
+ {
+ return value1 < value2;
+ }
+ else if constexpr (std::is_signed_v<T1>)
+ {
+ return value1 < 0 || o3tl::make_unsigned(value1) < value2;
+ }
+ else
+ {
+ return value2 >= 0 && value1 < o3tl::make_unsigned(value2);
+ }
+}
+
+template <typename T1, typename T2> constexpr bool cmp_greater(T1 value1, T2 value2) noexcept
+{
+ return cmp_less(value2, value1);
+}
+
+template <typename T1, typename T2> constexpr bool cmp_less_equal(T1 value1, T2 value2) noexcept
+{
+ return !cmp_greater(value1, value2);
+}
+
+template <typename T1, typename T2> constexpr bool cmp_greater_equal(T1 value1, T2 value2) noexcept
+{
+ return !cmp_less(value1, value2);
+}
+
+#endif
+
+// A convenient operator syntax around the standard integer comparison functions:
+template <typename T> struct IntCmp
+{
+ explicit constexpr IntCmp(T theValue)
+ : value(theValue)
+ {
+ }
+
+ T value;
+};
+
+template <typename T1, typename T2> constexpr bool operator==(IntCmp<T1> value1, IntCmp<T2> value2)
+{
+ return o3tl::cmp_equal(value1.value, value2.value);
+}
+
+template <typename T1, typename T2> constexpr bool operator!=(IntCmp<T1> value1, IntCmp<T2> value2)
+{
+ return o3tl::cmp_not_equal(value1.value, value2.value);
+}
+
+template <typename T1, typename T2> constexpr bool operator<(IntCmp<T1> value1, IntCmp<T2> value2)
+{
+ return o3tl::cmp_less(value1.value, value2.value);
+}
+
+template <typename T1, typename T2> constexpr bool operator>(IntCmp<T1> value1, IntCmp<T2> value2)
+{
+ return o3tl::cmp_greater(value1.value, value2.value);
+}
+
+template <typename T1, typename T2> constexpr bool operator<=(IntCmp<T1> value1, IntCmp<T2> value2)
+{
+ return o3tl::cmp_less_equal(value1.value, value2.value);
+}
+
+template <typename T1, typename T2> constexpr bool operator>=(IntCmp<T1> value1, IntCmp<T2> value2)
+{
+ return o3tl::cmp_greater_equal(value1.value, value2.value);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/lazy_update.hxx b/include/o3tl/lazy_update.hxx
new file mode 100644
index 0000000000..6f9325dfd1
--- /dev/null
+++ b/include/o3tl/lazy_update.hxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_O3TL_LAZY_UPDATE_HXX
+#define INCLUDED_O3TL_LAZY_UPDATE_HXX
+
+#include <utility>
+
+namespace o3tl
+{
+ /** Update output object lazily
+
+ This template collects data in input type, and updates the
+ output type with the given update functor, but only if the
+ output is requested. Useful if updating is expensive, or input
+ changes frequently, but output is only comparatively seldom
+ used.
+
+ @example
+ <pre>
+LazyUpdate<InType,OutType,decltype(F)> myValue(F);
+*myValue = newInput;
+myValue->updateInput( this, that, those );
+
+output( *myValue );
+ </pre>
+ or
+ <pre>
+output( myValue.getOutValue() );
+ </pre>
+ if the compiler does not recognize the const context.
+ */
+ template<typename In, typename Out, typename Func> class LazyUpdate {
+ public:
+ LazyUpdate(Func func): func_(std::move(func)), input_(), dirty_(true) {}
+
+ In const & getInValue() const { return input_; }
+
+ Out const & getOutValue() const { return update(); }
+
+ In & operator *() {
+ dirty_ = true;
+ return input_;
+ }
+
+ In * operator ->() {
+ dirty_ = true;
+ return &input_;
+ }
+
+ Out const & operator *() const { return update(); }
+
+ Out const * operator ->() const { return &update(); }
+
+ private:
+ Out const & update() const {
+ if (dirty_) {
+ output_ = func_(input_);
+ dirty_ = false;
+ }
+ return output_;
+ }
+
+ Func const func_;
+ In input_;
+ mutable Out output_;
+ mutable bool dirty_;
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/lru_map.hxx b/include/o3tl/lru_map.hxx
new file mode 100644
index 0000000000..7e046fd1db
--- /dev/null
+++ b/include/o3tl/lru_map.hxx
@@ -0,0 +1,297 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#ifndef INCLUDED_O3TL_LRU_MAP_HXX
+#define INCLUDED_O3TL_LRU_MAP_HXX
+
+#include <cassert>
+#include <list>
+#include <unordered_map>
+#include <cstddef>
+
+namespace o3tl
+{
+namespace detail
+{
+// Helper base class to keep total cost for lru_map with custom item size.
+// Custom size is specified by the ValueSize functor, the default of each
+// item counting as 1 is specified using the void type.
+template <class ValueSize> class lru_map_base
+{
+public:
+ // Returns total of ValueSize for all items.
+ size_t total_size() const { return mCurrentSize; }
+
+protected:
+ size_t mCurrentSize = 0; // sum of ValueSize for all items
+};
+
+// By default cost of each item is 1, so it doesn't need to be tracked.
+template <> class lru_map_base<void>
+{
+};
+} // namespace
+
+/** LRU map
+ *
+ * Similar to unordered_map (it actually uses it) with additionally functionality
+ * which removes the entries that have been "least recently used" when the size
+ * hits the specified capacity.
+ *
+ * It only implements the minimal methods needed and the implementation is NOT
+ * thread safe.
+ *
+ * The implementation is as simple as possible but it still uses O(1) complexity
+ * for most of the operations with a combination unordered map and linked list.
+ *
+ * It is optionally possible to specify a function for ValueSize template
+ * argument (that can be called as 'size_t func(Value)') that will return
+ * a size (cost) for an item instead of the default size of 1 for each item.
+ * The size of an item must not change for an item (if needed, re-insert
+ * the item). A newly inserted item is guaranteed to be in the container,
+ * even if its size exceeds the maximum size.
+ *
+ **/
+template <typename Key, typename Value, class KeyHash = std::hash<Key>,
+ class KeyEqual = std::equal_to<Key>, class ValueSize = void>
+class lru_map final : public detail::lru_map_base<ValueSize>
+{
+public:
+ typedef typename std::pair<Key, Value> key_value_pair_t;
+
+private:
+ typedef std::list<key_value_pair_t> list_t;
+ typedef typename list_t::iterator list_iterator_t;
+ typedef typename list_t::const_iterator list_const_iterator_t;
+
+ typedef std::unordered_map<Key, list_iterator_t, KeyHash, KeyEqual> map_t;
+ typedef typename map_t::iterator map_iterator_t;
+ typedef typename map_t::const_iterator map_const_iterator_t;
+
+ list_t mLruList;
+ map_t mLruMap;
+ size_t mMaxSize;
+
+ void addSize(const Value& value)
+ {
+ // by default total size is equal to number of items
+ if constexpr (!std::is_void_v<ValueSize>)
+ this->mCurrentSize += ValueSize()(value);
+ }
+
+ void removeSize(const Value& value)
+ {
+ // by default total size is equal to number of items
+ if constexpr (!std::is_void_v<ValueSize>)
+ {
+ size_t itemSize = ValueSize()(value);
+ assert(itemSize <= this->mCurrentSize);
+ this->mCurrentSize -= itemSize;
+ }
+ }
+
+ void removeOldestItem()
+ {
+ removeSize(mLruList.back().second);
+ // remove from map
+ mLruMap.erase(mLruList.back().first);
+ // remove from list
+ mLruList.pop_back();
+ }
+
+ void checkLRUItemInsert()
+ {
+ if constexpr (std::is_void_v<ValueSize>)
+ { // One added, so it's enough to remove one, if needed.
+ if (mLruMap.size() > mMaxSize)
+ removeOldestItem();
+ }
+ else
+ {
+ // This must leave at least one item (it's called from insert).
+ while (this->mCurrentSize > mMaxSize && mLruList.size() > 1)
+ removeOldestItem();
+ }
+ }
+
+ void checkLRUItemUpdate()
+ {
+ // Item update does not change total size by default.
+ if constexpr (!std::is_void_v<ValueSize>)
+ {
+ // This must leave at least one item (it's called from insert).
+ while (this->mCurrentSize > mMaxSize && mLruList.size() > 1)
+ removeOldestItem();
+ }
+ }
+
+ void checkLRUMaxSize()
+ {
+ if constexpr (std::is_void_v<ValueSize>)
+ {
+ while (mLruMap.size() > mMaxSize)
+ removeOldestItem();
+ }
+ else
+ {
+ while (this->mCurrentSize > mMaxSize)
+ removeOldestItem();
+ }
+ }
+
+ void clearSize()
+ {
+ if constexpr (!std::is_void_v<ValueSize>)
+ {
+#ifdef DBG_UTIL
+ for (const key_value_pair_t& item : mLruList)
+ removeSize(item.second);
+ assert(this->mCurrentSize == 0);
+#else
+ this->mCurrentSize = 0;
+#endif
+ }
+ }
+
+public:
+ typedef list_iterator_t iterator;
+ typedef list_const_iterator_t const_iterator;
+
+ lru_map(size_t nMaxSize)
+ : mMaxSize(nMaxSize)
+ {
+ assert(mMaxSize > 0);
+ }
+ ~lru_map()
+ {
+ clearSize();
+ // Some code .e.g. SalBitmap likes to remove itself from a cache during it's destructor, which means we
+ // get calls into lru_map while we are in destruction, so use the swap-and-clear idiom to avoid those problems.
+ mLruMap.clear();
+ list_t().swap(mLruList);
+ }
+
+ void setMaxSize(size_t nMaxSize)
+ {
+ mMaxSize = nMaxSize;
+ assert(mMaxSize > 0);
+ checkLRUMaxSize();
+ }
+
+ void insert(key_value_pair_t& rPair)
+ {
+ map_iterator_t i = mLruMap.find(rPair.first);
+
+ if (i == mLruMap.end()) // doesn't exist -> add to queue and map
+ {
+ addSize(rPair.second);
+ // add to front of the list
+ mLruList.push_front(rPair);
+ // add the list position (iterator) to the map
+ auto it = mLruList.begin();
+ mLruMap[it->first] = it;
+ checkLRUItemInsert();
+ }
+ else // already exists -> replace value
+ {
+ // update total cost
+ removeSize(i->second->second);
+ addSize(rPair.second);
+ // replace value
+ i->second->second = rPair.second;
+ // bring to front of the lru list
+ mLruList.splice(mLruList.begin(), mLruList, i->second);
+ checkLRUItemUpdate();
+ }
+ }
+
+ void insert(key_value_pair_t&& rPair)
+ {
+ map_iterator_t i = mLruMap.find(rPair.first);
+
+ if (i == mLruMap.end()) // doesn't exist -> add to list and map
+ {
+ addSize(rPair.second);
+ // add to front of the list
+ mLruList.push_front(std::move(rPair));
+ // add the list position (iterator) to the map
+ auto it = mLruList.begin();
+ mLruMap[it->first] = it;
+ checkLRUItemInsert();
+ }
+ else // already exists -> replace value
+ {
+ removeSize(i->second->second);
+ addSize(rPair.second);
+ // replace value
+ i->second->second = std::move(rPair.second);
+ // push to back of the lru list
+ mLruList.splice(mLruList.begin(), mLruList, i->second);
+ checkLRUItemUpdate();
+ }
+ }
+
+ list_const_iterator_t find(const Key& key)
+ {
+ const map_iterator_t i = mLruMap.find(key);
+ if (i == mLruMap.cend()) // can't find entry for the key
+ {
+ // return empty iterator
+ return mLruList.cend();
+ }
+ else
+ {
+ // push to back of the lru list
+ mLruList.splice(mLruList.begin(), mLruList, i->second);
+ return i->second;
+ }
+ }
+
+ // reverse-iterates the list removing all items matching the predicate
+ template <class UnaryPredicate> void remove_if(UnaryPredicate pred)
+ {
+ auto it = mLruList.rbegin();
+ while (it != mLruList.rend())
+ {
+ if (pred(*it))
+ {
+ removeSize(it->second);
+ mLruMap.erase(it->first);
+ it = decltype(it){ mLruList.erase(std::next(it).base()) };
+ }
+ else
+ ++it;
+ }
+ }
+
+ list_const_iterator_t begin() const { return mLruList.cbegin(); }
+
+ list_const_iterator_t end() const { return mLruList.cend(); }
+
+ size_t size() const
+ {
+ assert(mLruMap.size() == mLruList.size());
+ return mLruMap.size();
+ }
+
+ // size_t total_size() const; - only if custom ValueSize
+
+ void clear()
+ {
+ clearSize();
+ mLruMap.clear();
+ mLruList.clear();
+ }
+};
+}
+
+#endif /* INCLUDED_O3TL_LRU_MAP_HXX */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/make_shared.hxx b/include/o3tl/make_shared.hxx
new file mode 100644
index 0000000000..5d4d98e42b
--- /dev/null
+++ b/include/o3tl/make_shared.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_O3TL_MAKE_SHARED_HXX
+#define INCLUDED_O3TL_MAKE_SHARED_HXX
+
+#include <o3tl/deleter.hxx>
+#include <memory>
+#include <type_traits>
+
+namespace o3tl
+{
+/** Allocate an array stored in a shared_ptr, calling operator delete[].
+ Note that this is only allowed for arithmetic types because shared_ptr
+ implicitly converts to sub-types.
+ */
+template <typename T> std::shared_ptr<T> make_shared_array(size_t const size)
+{
+ static_assert(std::is_arithmetic<T>::value, "only arrays of arithmetic types allowed");
+ return std::shared_ptr<T>(new T[size], std::default_delete<T[]>());
+}
+
+/** To markup std::shared_ptr that coverity warns might throw exceptions
+ which won't throw in practice, or where std::terminate is
+ an acceptable response if they do
+*/
+template <class T, class... Args> std::shared_ptr<T> make_shared(Args&&... args)
+{
+#if defined(__COVERITY__)
+ return std::shared_ptr<T>(new T(std::forward<Args>(args)...), o3tl::default_delete<T>());
+#else
+ return std::make_shared<T>(std::forward<Args>(args)...);
+#endif
+}
+
+} // namespace o3tl
+
+#endif // INCLUDED_O3TL_MAKE_SHARED_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/numeric.hxx b/include/o3tl/numeric.hxx
new file mode 100644
index 0000000000..9980319a64
--- /dev/null
+++ b/include/o3tl/numeric.hxx
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_O3TL_NUMERIC_HXX
+#define INCLUDED_O3TL_NUMERIC_HXX
+
+#include <stdexcept>
+
+namespace o3tl
+{
+ struct divide_by_zero final : public std::runtime_error
+ {
+ explicit divide_by_zero()
+ : std::runtime_error("divide by zero")
+ {
+ }
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/runtimetooustring.hxx b/include/o3tl/runtimetooustring.hxx
new file mode 100644
index 0000000000..6d6f27ac18
--- /dev/null
+++ b/include/o3tl/runtimetooustring.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_O3TL_RUNTIMETOOUSTRING_HXX
+#define INCLUDED_O3TL_RUNTIMETOOUSTRING_HXX
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstring>
+
+#include <rtl/textcvt.h>
+#include <rtl/textenc.h>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+
+namespace o3tl
+{
+/** Convert an NTBS from the C++ runtime to an OUString.
+
+ This is used to convert an NTBS as provided by std::exception::what or
+ std::type_info::name into an OUString in a "lossless" way. The conversion
+ is done using RTL_TEXTENCODING_ISO_8859_1, so each char in the input maps
+ to one Unicode character in the output.
+*/
+inline OUString runtimeToOUString(char const* runtimeString)
+{
+ OUString s;
+ bool ok = rtl_convertStringToUString(
+ &s.pData, runtimeString, std::strlen(runtimeString), RTL_TEXTENCODING_ISO_8859_1,
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR));
+ assert(ok);
+ (void)ok;
+ return s;
+}
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/safeCoInitUninit.hxx b/include/o3tl/safeCoInitUninit.hxx
new file mode 100644
index 0000000000..0ceb4a9746
--- /dev/null
+++ b/include/o3tl/safeCoInitUninit.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#if defined _WIN32
+#include <prewin.h>
+
+// for CoInitializeEx / CoUninitialize
+#include <combaseapi.h>
+
+#include <postwin.h>
+
+// for std::abort
+#include <cstdlib>
+
+namespace o3tl
+{
+// Helpers for safe calls to CoInitializeEx and CoUninitialize in MSVC
+// Indeed if a thread has been already initialized with a concurrency model
+// (in LO case COINIT_APARTMENTTHREADED or COINIT_MULTITHREADED)
+// CoInitializeEx can't succeed without calling first CoUninitialize
+// also, CoUninitialize must be called the number of times CoInitializeEx has been called
+inline HRESULT safeCoInitializeEx(DWORD dwCoInit, int& nbReinit)
+{
+ HRESULT hr;
+ while ((hr = CoInitializeEx(nullptr, dwCoInit)) == RPC_E_CHANGED_MODE)
+ {
+ // so we're in RPC_E_CHANGED_MODE case
+ // the pb was it was already initialized with a different concurrency model
+ // close this init
+ CoUninitialize();
+ // and increment counter for dtr part
+ ++nbReinit;
+
+ // and keep on the loop if there were multi initializations
+ }
+ if (FAILED(hr))
+ std::abort();
+ return hr;
+}
+
+inline void safeCoUninitializeReinit(DWORD dwCoInit, int nbReinit)
+{
+ CoUninitialize();
+ // Put back all the inits, if there were, before the use of the caller to safeCoInitializeEx
+ for (int i = 0; i < nbReinit; ++i)
+ CoInitializeEx(nullptr, dwCoInit);
+}
+}
+#endif
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/safeint.hxx b/include/o3tl/safeint.hxx
new file mode 100644
index 0000000000..a32c6beea1
--- /dev/null
+++ b/include/o3tl/safeint.hxx
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <cassert>
+#include <limits>
+#include <type_traits>
+
+#if defined(_MSC_VER)
+#include <safeint.h>
+#else
+#ifndef __has_builtin
+# define __has_builtin(x) 0
+#endif
+#endif
+
+namespace o3tl
+{
+
+template <typename T> inline constexpr T saturating_add(T a, T b)
+{
+ if (b >= 0) {
+ if (a <= std::numeric_limits<T>::max() - b) {
+ return a + b;
+ } else {
+ return std::numeric_limits<T>::max();
+ }
+ } else {
+ if (a >= std::numeric_limits<T>::min() - b) {
+ return a + b;
+ } else {
+ return std::numeric_limits<T>::min();
+ }
+ }
+}
+
+template <typename T> inline constexpr T saturating_sub(T a, T b)
+{
+ if (b >= 0) {
+ if (a >= std::numeric_limits<T>::min() + b) {
+ return a - b;
+ } else {
+ return std::numeric_limits<T>::min();
+ }
+ } else {
+ if (a <= std::numeric_limits<T>::max() + b) {
+ return a - b;
+ } else {
+ return std::numeric_limits<T>::max();
+ }
+ }
+}
+
+template<typename T> inline
+typename std::enable_if<std::is_signed<T>::value, T>::type saturating_toggle_sign(
+ T a)
+{
+ if (a == std::numeric_limits<T>::min())
+ return std::numeric_limits<T>::max();
+ return a * -1;
+}
+
+#if defined(_MSC_VER)
+
+template<typename T> inline bool checked_multiply(T a, T b, T& result)
+{
+ return !msl::utilities::SafeMultiply(a, b, result);
+}
+
+template<typename T> inline bool checked_add(T a, T b, T& result)
+{
+ return !msl::utilities::SafeAdd(a, b, result);
+}
+
+template<typename T> inline bool checked_sub(T a, T b, T& result)
+{
+ return !msl::utilities::SafeSubtract(a, b, result);
+}
+
+#elif (defined __GNUC__ && !defined __clang__) || (__has_builtin(__builtin_mul_overflow) && !(defined ANDROID && defined __clang__) && !(defined(__clang__) && (defined(__arm__) || defined(__i386__))))
+// 32-bit clang fails with undefined reference to `__mulodi4'
+
+template<typename T> inline bool checked_multiply(T a, T b, T& result)
+{
+ return __builtin_mul_overflow(a, b, &result);
+}
+
+template<typename T> inline bool checked_add(T a, T b, T& result)
+{
+ return __builtin_add_overflow(a, b, &result);
+}
+
+template<typename T> inline bool checked_sub(T a, T b, T& result)
+{
+ return __builtin_sub_overflow(a, b, &result);
+}
+
+#else
+
+//https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
+template<typename T> inline typename std::enable_if<std::is_signed<T>::value, bool>::type checked_multiply(T a, T b, T& result)
+{
+ if (a > 0) { /* a is positive */
+ if (b > 0) { /* a and b are positive */
+ if (a > (std::numeric_limits<T>::max() / b)) {
+ return true; /* Handle error */
+ }
+ } else { /* a positive, b nonpositive */
+ if (b < (std::numeric_limits<T>::min() / a)) {
+ return true; /* Handle error */
+ }
+ } /* a positive, b nonpositive */
+ } else { /* a is nonpositive */
+ if (b > 0) { /* a is nonpositive, b is positive */
+ if (a < (std::numeric_limits<T>::min() / b)) {
+ return true; /* Handle error */
+ }
+ } else { /* a and b are nonpositive */
+ if ( (a != 0) && (b < (std::numeric_limits<T>::max() / a))) {
+ return true; /* Handle error */
+ }
+ } /* End if a and b are nonpositive */
+ } /* End if a is nonpositive */
+
+ result = a * b;
+
+ return false;
+}
+
+//https://www.securecoding.cert.org/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
+template<typename T> inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type checked_multiply(T a, T b, T& result)
+{
+ if (b && a > std::numeric_limits<T>::max() / b) {
+ return true;/* Handle error */
+ }
+
+ result = a * b;
+
+ return false;
+}
+
+//https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
+template<typename T> inline typename std::enable_if<std::is_signed<T>::value, bool>::type checked_add(T a, T b, T& result)
+{
+ if (((b > 0) && (a > (std::numeric_limits<T>::max() - b))) ||
+ ((b < 0) && (a < (std::numeric_limits<T>::min() - b)))) {
+ return true;
+ }
+
+ result = a + b;
+
+ return false;
+}
+
+//https://www.securecoding.cert.org/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
+template<typename T> inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type checked_add(T a, T b, T& result)
+{
+ if (std::numeric_limits<T>::max() - a < b) {
+ return true;/* Handle error */
+ }
+
+ result = a + b;
+
+ return false;
+}
+
+//https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
+template<typename T> inline typename std::enable_if<std::is_signed<T>::value, bool>::type checked_sub(T a, T b, T& result)
+{
+ if ((b > 0 && a < std::numeric_limits<T>::min() + b) ||
+ (b < 0 && a > std::numeric_limits<T>::max() + b)) {
+ return true;
+ }
+
+ result = a - b;
+
+ return false;
+}
+
+//https://www.securecoding.cert.org/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
+template<typename T> inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type checked_sub(T a, T b, T& result)
+{
+ if (a < b) {
+ return true;
+ }
+
+ result = a - b;
+
+ return false;
+}
+
+#endif
+
+template<typename T> constexpr std::enable_if_t<std::is_signed_v<T>, std::make_unsigned_t<T>>
+make_unsigned(T value)
+{
+ assert(value >= 0);
+ return value;
+}
+
+template<typename T1, typename T2> constexpr std::enable_if_t<std::is_unsigned_v<T1>, T1>
+clamp_to_unsigned(T2 value) {
+ if constexpr (std::is_unsigned_v<T2>) {
+ // coverity[result_independent_of_operands] - suppress warning for template
+ return value <= std::numeric_limits<T1>::max() ? value : std::numeric_limits<T1>::max();
+ } else {
+ static_assert(std::is_signed_v<T2>);
+ return value < 0 ? 0 : clamp_to_unsigned<T1>(make_unsigned(value));
+ }
+}
+
+// An implicit conversion from T2 to T1, useful in places where an explicit conversion from T2 to
+// T1 is needed (e.g., in list initialization, if the implicit conversion would be narrowing) but
+// tools like -fsanitize=implicit-conversion should still be able to detect truncation:
+template<typename T1, typename T2> constexpr T1 narrowing(T2 value) { return value; }
+
+// std::min wrapped to inform coverity that the result is now deemed sanitized
+// coverity[ -taint_source ]
+template<typename T> [[nodiscard]] inline T sanitizing_min(T a, T b)
+{
+ return std::min(a, b);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/sorted_vector.hxx b/include/o3tl/sorted_vector.hxx
new file mode 100644
index 0000000000..0f31bc5176
--- /dev/null
+++ b/include/o3tl/sorted_vector.hxx
@@ -0,0 +1,424 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_O3TL_SORTED_VECTOR_HXX
+#define INCLUDED_O3TL_SORTED_VECTOR_HXX
+
+#include <vector>
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+
+namespace o3tl
+{
+
+// forward declared because it's default template arg for sorted_vector
+template<class Value, class Compare>
+struct find_unique;
+
+/** Represents a sorted vector of values.
+
+ @tpl Value class of item to be stored in container
+ @tpl Compare comparison method
+ @tpl Find look up index of a Value in the array
+*/
+template<
+ typename Value,
+ typename Compare = std::less<Value>,
+ template<typename, typename> class Find = find_unique,
+ bool = std::is_copy_constructible<Value>::value >
+class sorted_vector
+{
+private:
+ typedef Find<Value, Compare> Find_t;
+ typedef typename std::vector<Value> vector_t;
+ typedef typename std::vector<Value>::iterator iterator;
+public:
+ typedef typename std::vector<Value>::const_iterator const_iterator;
+ typedef typename std::vector<Value>::const_reverse_iterator const_reverse_iterator;
+ typedef typename std::vector<Value>::difference_type difference_type;
+ typedef typename std::vector<Value>::size_type size_type;
+ typedef Value value_type;
+
+ constexpr sorted_vector( std::initializer_list<Value> init )
+ : m_vector(init)
+ {
+ std::sort(m_vector.begin(), m_vector.end(), Compare());
+ }
+ sorted_vector() = default;
+ sorted_vector(sorted_vector const&) = default;
+ sorted_vector(sorted_vector&&) = default;
+
+ sorted_vector& operator=(sorted_vector const&) = default;
+ sorted_vector& operator=(sorted_vector&&) = default;
+
+ // MODIFIERS
+
+ std::pair<const_iterator,bool> insert( Value&& x )
+ {
+ std::pair<const_iterator, bool> const ret(Find_t()(m_vector.begin(), m_vector.end(), x));
+ if (!ret.second)
+ {
+ const_iterator const it = m_vector.insert(m_vector.begin() + (ret.first - m_vector.begin()), std::move(x));
+ return std::make_pair(it, true);
+ }
+ return std::make_pair(ret.first, false);
+ }
+
+ std::pair<const_iterator,bool> insert( const Value& x )
+ {
+ std::pair<const_iterator, bool> const ret(Find_t()(m_vector.begin(), m_vector.end(), x));
+ if (!ret.second)
+ {
+ const_iterator const it = m_vector.insert(m_vector.begin() + (ret.first - m_vector.begin()), x);
+ return std::make_pair(it, true);
+ }
+ return std::make_pair(ret.first, false);
+ }
+
+ size_type erase( const Value& x )
+ {
+ std::pair<const_iterator, bool> const ret(Find_t()(m_vector.begin(), m_vector.end(), x));
+ if (ret.second)
+ {
+ m_vector.erase(m_vector.begin() + (ret.first - m_vector.begin()));
+ return 1;
+ }
+ return 0;
+ }
+
+ void erase_at(size_t index)
+ {
+ m_vector.erase(m_vector.begin() + index);
+ }
+
+ // like C++ 2011: erase with const_iterator (doesn't change sort order)
+ const_iterator erase(const_iterator const& position)
+ { // C++98 has vector::erase(iterator), so call that
+ return m_vector.erase(m_vector.begin() + (position - m_vector.begin()));
+ }
+
+ void erase(const_iterator const& first, const_iterator const& last)
+ {
+ m_vector.erase(m_vector.begin() + (first - m_vector.begin()),
+ m_vector.begin() + (last - m_vector.begin()));
+ }
+
+ /**
+ * make erase return the removed element, otherwise there is no useful way of extracting a std::unique_ptr
+ * from this.
+ */
+ Value erase_extract( size_t index )
+ {
+ Value val = std::move(m_vector[index]);
+ m_vector.erase(m_vector.begin() + index);
+ return val;
+ }
+
+ void clear()
+ {
+ m_vector.clear();
+ }
+
+ void swap(sorted_vector & other)
+ {
+ m_vector.swap(other.m_vector);
+ }
+
+ void reserve(size_type amount)
+ {
+ m_vector.reserve(amount);
+ }
+
+ // ACCESSORS
+
+ size_type size() const
+ {
+ return m_vector.size();
+ }
+
+ bool empty() const
+ {
+ return m_vector.empty();
+ }
+
+ // Only return a const iterator, so that the vector cannot be directly updated.
+ const_iterator begin() const
+ {
+ return m_vector.begin();
+ }
+
+ // Only return a const iterator, so that the vector cannot be directly updated.
+ const_iterator end() const
+ {
+ return m_vector.end();
+ }
+
+ // Only return a const iterator, so that the vector cannot be directly updated.
+ const_reverse_iterator rbegin() const
+ {
+ return m_vector.rbegin();
+ }
+
+ // Only return a const iterator, so that the vector cannot be directly updated.
+ const_reverse_iterator rend() const
+ {
+ return m_vector.rend();
+ }
+
+ const Value& front() const
+ {
+ return m_vector.front();
+ }
+
+ const Value& back() const
+ {
+ return m_vector.back();
+ }
+
+ const Value& operator[]( size_t index ) const
+ {
+ return m_vector.operator[]( index );
+ }
+
+ // OPERATIONS
+
+ const_iterator lower_bound( const Value& x ) const
+ {
+ return std::lower_bound( m_vector.begin(), m_vector.end(), x, Compare() );
+ }
+
+ const_iterator upper_bound( const Value& x ) const
+ {
+ return std::upper_bound( m_vector.begin(), m_vector.end(), x, Compare() );
+ }
+
+ /* Searches the container for an element with a value of x
+ * and returns an iterator to it if found, otherwise it returns an
+ * iterator to sorted_vector::end (the element past the end of the container).
+ *
+ * Only return a const iterator, so that the vector cannot be directly updated.
+ */
+ const_iterator find( const Value& x ) const
+ {
+ std::pair<const_iterator, bool> const ret(Find_t()(m_vector.begin(), m_vector.end(), x));
+ return (ret.second) ? ret.first : m_vector.end();
+ }
+
+ size_type count(const Value& v) const
+ {
+ return find(v) != end() ? 1 : 0;
+ }
+
+ bool operator==(const sorted_vector & other) const
+ {
+ return m_vector == other.m_vector;
+ }
+
+ bool operator!=(const sorted_vector & other) const
+ {
+ return m_vector != other.m_vector;
+ }
+
+ void insert(sorted_vector<Value,Compare,Find> const& rOther)
+ {
+ // optimization for the rather common case that we are overwriting this with the contents
+ // of another sorted vector
+ if ( empty() )
+ m_vector.insert(m_vector.begin(), rOther.m_vector.begin(), rOther.m_vector.end());
+ else
+ insert_internal( rOther.m_vector );
+ }
+
+ void insert_sorted_unique_vector(const std::vector<Value>& rOther)
+ {
+ assert( std::is_sorted(rOther.begin(), rOther.end(), Compare()));
+ assert( std::unique(rOther.begin(), rOther.end(), compare_equal) == rOther.end());
+ if ( empty() )
+ m_vector.insert(m_vector.begin(), rOther.m_vector.begin(), rOther.m_vector.end());
+ else
+ insert_internal( rOther );
+ }
+
+ void insert_sorted_unique_vector(std::vector<Value>&& rOther)
+ {
+ assert( std::is_sorted(rOther.begin(), rOther.end(), Compare()));
+ assert( std::unique(rOther.begin(), rOther.end(), compare_equal) == rOther.end());
+ if ( empty() )
+ m_vector.swap( rOther );
+ else
+ insert_internal( rOther );
+ }
+
+ /* Clear() elements in the vector, and free them one by one. */
+ void DeleteAndDestroyAll()
+ {
+ for (const_iterator it = m_vector.begin(); it != m_vector.end(); ++it)
+ {
+ delete *it;
+ }
+
+ clear();
+ }
+
+ // fdo#58793: some existing code in Writer (SwpHintsArray)
+ // routinely modifies the members of the vector in a way that
+ // violates the sort order, and then re-sorts the array.
+ // This is a kludge to enable that code to work.
+ // If you are calling this function, you are Doing It Wrong!
+ void Resort()
+ {
+ std::stable_sort(m_vector.begin(), m_vector.end(), Compare());
+ }
+
+private:
+ static bool compare_equal( const Value& v1, const Value& v2 )
+ { // Synthetize == check from < check for std::unique asserts above.
+ return !Compare()( v1, v2 ) && !Compare()( v2, v1 );
+ }
+
+ void insert_internal( const std::vector<Value>& rOther )
+ {
+ // Do a union in one pass rather than repeated insert() that could repeatedly
+ // move large amounts of data.
+ vector_t tmp;
+ tmp.reserve( m_vector.size() + rOther.size());
+ std::set_union( m_vector.begin(), m_vector.end(),
+ rOther.begin(), rOther.end(),
+ std::back_inserter( tmp ), Compare());
+ m_vector.swap( tmp );
+ }
+
+ vector_t m_vector;
+};
+
+/* Specialise the template for cases like Value = std::unique_ptr<T>, where
+ MSVC2017 needs some help
+*/
+template<
+ typename Value,
+ typename Compare,
+ template<typename, typename> class Find >
+class sorted_vector<Value,Compare,Find,false> : public sorted_vector<Value, Compare, Find, true>
+{
+public:
+ using sorted_vector<Value, Compare, Find, true>::sorted_vector;
+ typedef sorted_vector<Value, Compare, Find, true> super_sorted_vector;
+
+ sorted_vector(sorted_vector const&) = delete;
+ sorted_vector& operator=(sorted_vector const&) = delete;
+
+ sorted_vector() = default;
+ sorted_vector(sorted_vector&&) = default;
+ sorted_vector& operator=(sorted_vector&&) = default;
+
+ /**
+ * implement find for sorted_vectors containing std::unique_ptr
+ */
+ typename super_sorted_vector::const_iterator find( typename Value::element_type const * x ) const
+ {
+ Value tmp(const_cast<typename Value::element_type*>(x));
+ auto ret = super_sorted_vector::find(tmp);
+ // coverity[ resource_leak : FALSE] - this is only a pretend unique_ptr, to avoid allocating a temporary
+ tmp.release();
+ return ret;
+ }
+ /**
+ * implement upper_bound for sorted_vectors containing std::unique_ptr
+ */
+ typename super_sorted_vector::const_iterator upper_bound( typename Value::element_type const * x ) const
+ {
+ Value tmp(const_cast<typename Value::element_type*>(x));
+ auto ret = super_sorted_vector::upper_bound(tmp);
+ // coverity[ resource_leak : FALSE] - this is only a pretend unique_ptr, to avoid allocating a temporary
+ tmp.release();
+ return ret;
+ }
+ /**
+ * implement lower_bound for sorted_vectors containing std::unique_ptr
+ */
+ typename super_sorted_vector::const_iterator lower_bound( typename Value::element_type const * x ) const
+ {
+ Value tmp(const_cast<typename Value::element_type*>(x));
+ auto ret = super_sorted_vector::lower_bound(tmp);
+ // coverity[ resource_leak : FALSE] - this is only a pretend unique_ptr, to avoid allocating a temporary
+ tmp.release();
+ return ret;
+ }
+};
+
+
+/** Implements an ordering function over a pointer, where the comparison uses the < operator on the pointed-to types.
+ Very useful for the cases where we put pointers to objects inside a sorted_vector.
+*/
+template <class T> struct less_ptr_to
+{
+ bool operator() ( T* const& lhs, T* const& rhs ) const
+ {
+ return (*lhs) < (*rhs);
+ }
+};
+
+template <class T> struct less_uniqueptr_to
+{
+ bool operator() ( std::unique_ptr<T> const& lhs, std::unique_ptr<T> const& rhs ) const
+ {
+ return (*lhs) < (*rhs);
+ }
+};
+
+/** the elements are totally ordered by Compare,
+ for no 2 elements !Compare(a,b) && !Compare(b,a) is true
+ */
+template<class Value, class Compare>
+struct find_unique
+{
+ typedef typename sorted_vector<Value, Compare,
+ o3tl::find_unique> ::const_iterator const_iterator;
+ std::pair<const_iterator, bool> operator()(
+ const_iterator first, const_iterator last,
+ Value const& v)
+ {
+ const_iterator const it = std::lower_bound(first, last, v, Compare());
+ return std::make_pair(it, (it != last && !Compare()(v, *it)));
+ }
+};
+
+/** the elements are partially ordered by Compare,
+ 2 elements are allowed if they are not the same element (pointer equal)
+ */
+template<class Value, class Compare>
+struct find_partialorder_ptrequals
+{
+ typedef typename sorted_vector<Value, Compare,
+ o3tl::find_partialorder_ptrequals>::const_iterator const_iterator;
+ std::pair<const_iterator, bool> operator()(
+ const_iterator first, const_iterator last,
+ Value const& v)
+ {
+ std::pair<const_iterator, const_iterator> const its =
+ std::equal_range(first, last, v, Compare());
+ for (const_iterator it = its.first; it != its.second; ++it)
+ {
+ if (v == *it)
+ {
+ return std::make_pair(it, true);
+ }
+ }
+ return std::make_pair(its.first, false);
+ }
+};
+
+} // namespace o3tl
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/sprintf.hxx b/include/o3tl/sprintf.hxx
new file mode 100644
index 0000000000..373fff744c
--- /dev/null
+++ b/include/o3tl/sprintf.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstddef>
+#include <cstdio>
+#include <utility>
+
+#include <o3tl/safeint.hxx>
+
+namespace o3tl
+{
+// A drop-in replacement for safe uses of std::sprintf where it is statically known that the
+// provided buffer is large enough. Compared to a plain use of std::sprintf, using o3tl::sprintf
+// for one makes it explicit that the call is considered safe and for another avoids deprecation
+// warnings on platforms like the macOS 13 SDK that mark std::sprintf as deprecated. Many simple
+// uses of std::sprintf across the code base can be replaced with alternative code using e.g.
+// OString::number. This is for the remaining formatting-rich cases for which there is no easy
+// replacement yet in our C++17 baseline. Ultimately, it should be removed again once alternatives
+// for those remaining cases, like C++20 std::format, are available.
+template <std::size_t N, typename... T>
+int sprintf(char (&s)[N], char const* format, T&&... arguments)
+{
+ auto const n = std::snprintf(s, N, format, std::forward<T>(arguments)...);
+ assert(n < 0 || o3tl::make_unsigned(n) < N);
+ (void)n;
+ return n;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/string_view.hxx b/include/o3tl/string_view.hxx
new file mode 100644
index 0000000000..1e5db5eb02
--- /dev/null
+++ b/include/o3tl/string_view.hxx
@@ -0,0 +1,558 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstddef>
+#include <string>
+#include <string_view>
+
+#include <o3tl/intcmp.hxx>
+#include <rtl/character.hxx>
+#include <rtl/ustring.h>
+#include <rtl/math.h>
+#include <sal/types.h>
+
+namespace o3tl
+{
+// Like OUString::equalsAscii/OUString::equalsAsciiL, but for std::u16string_view:
+inline bool equalsAscii(std::u16string_view s1, std::string_view s2)
+{
+ return s1.size() == s2.size()
+ && rtl_ustr_ascii_shortenedCompare_WithLength(s1.data(), s1.size(), s2.data(), s2.size())
+ == 0;
+}
+
+// Like OUString::compareToAscii, but for std::u16string_view and std::string_view:
+inline int compareToAscii(std::u16string_view s1, std::string_view s2)
+{
+ return rtl_ustr_asciil_reverseCompare_WithLength(s1.data(), s1.size(), s2.data(), s2.size());
+};
+
+// Like OUString::equalsIgnoreAsciiCase, but for two std::u16string_view:
+inline bool equalsIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2)
+{
+ if (s1.size() != s2.size())
+ return false;
+ if (s1.data() == s2.data())
+ return true;
+ return rtl_ustr_compareIgnoreAsciiCase_WithLength(s1.data(), s1.size(), s2.data(), s2.size())
+ == 0;
+};
+
+inline bool equalsIgnoreAsciiCase(std::u16string_view s1, std::string_view s2)
+{
+ return s1.size() == s2.size()
+ && (rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength(s1.data(), s1.size(),
+ s2.data(), s2.size())
+ == 0);
+}
+
+inline bool equalsIgnoreAsciiCase(std::string_view s1, std::string_view s2)
+{
+ if (s1.size() != s2.size())
+ return false;
+ if (s1.data() == s2.data())
+ return true;
+ return rtl_str_compareIgnoreAsciiCase_WithLength(s1.data(), s1.size(), s2.data(), s2.size())
+ == 0;
+};
+
+// Like OUString::compareToIgnoreAsciiCase, but for two std::u16string_view:
+inline int compareToIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2)
+{
+ return rtl_ustr_compareIgnoreAsciiCase_WithLength(s1.data(), s1.size(), s2.data(), s2.size());
+};
+
+// Like OUString::matchIgnoreAsciiCase, but for two std::u16string_view:
+inline bool matchIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2,
+ sal_Int32 fromIndex = 0)
+{
+ return rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength(
+ s1.data() + fromIndex, s1.size() - fromIndex, s2.data(), s2.size(), s2.size())
+ == 0;
+}
+
+// Like OUString::matchIgnoreAsciiCase, but for std::u16string_view and std::string_view:
+inline bool matchIgnoreAsciiCase(std::u16string_view s1, std::string_view s2,
+ sal_Int32 fromIndex = 0)
+{
+ return rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength(
+ s1.data() + fromIndex, s1.size() - fromIndex, s2.data(), s2.size())
+ == 0;
+}
+
+// Like OUString::endsWithIgnoreAsciiCase, but for std::u16string_view
+inline bool endsWithIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2,
+ std::u16string_view* rest = nullptr)
+{
+ auto const b = s2.size() <= s1.size() && matchIgnoreAsciiCase(s1, s2, s1.size() - s2.size());
+ if (b && rest != nullptr)
+ {
+ *rest = s1.substr(0, s1.size() - s2.size());
+ }
+ return b;
+}
+
+inline bool endsWithIgnoreAsciiCase(std::u16string_view s1, std::string_view s2,
+ std::u16string_view* rest = nullptr)
+{
+ auto const b = s2.size() <= s1.size()
+ && rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
+ s1.data() + s1.size() - s2.size(), s2.size(), s2.data(), s2.size())
+ == 0;
+ if (b && rest != nullptr)
+ {
+ *rest = s1.substr(0, s1.size() - s2.size());
+ }
+ return b;
+}
+
+// Similar to O[U]String::getToken, returning the first token of a std::[u16]string_view starting
+// at a given position.
+//
+// Attention: There are two sets of o3tl::getToken overloads here. This first set has an interface
+// based on std::size_t length parameters, and its semantics don't match those of
+// O[U]String::getToken exactly (buf if needed, it can be extended to return the n'th token instead
+// of just the first, and/or support an initial position of npos, to make the semantics match).
+template <typename charT, typename traits = std::char_traits<charT>>
+inline std::basic_string_view<charT, traits> getToken(std::basic_string_view<charT, traits> sv,
+ charT delimiter, std::size_t& position)
+{
+ assert(position <= sv.size());
+ auto const n = sv.find(delimiter, position);
+ std::basic_string_view<charT, traits> t;
+ if (n == std::string_view::npos)
+ {
+ t = sv.substr(position);
+ position = std::string_view::npos;
+ }
+ else
+ {
+ t = sv.substr(position, n - position);
+ position = n + 1;
+ }
+ return t;
+}
+// The following two overloads prevent overload resolution mistakes that would occur with their
+// template counterpart, when sv is of a type that is implicitly convertible to basic_string_view
+// (like OString or OUString), in which case overload resolution would erroneously choose the
+// three-argument overloads (taking sv, nToken, cTok) from the second set of
+// o3tl::getToken overloads below:
+inline std::string_view getToken(std::string_view sv, char delimiter, std::size_t& position)
+{
+ return getToken<char>(sv, delimiter, position);
+}
+inline std::u16string_view getToken(std::u16string_view sv, char16_t delimiter,
+ std::size_t& position)
+{
+ return getToken<char16_t>(sv, delimiter, position);
+}
+
+// Similar to O[U]String::getToken.
+//
+// Attention: There are two sets of o3tl::getToken overloads here. This second set has an
+// interface based on sal_Int32 length parameters, and is meant to be a drop-in replacement for
+// O[U]String::getToken.
+template <typename charT, typename traits = std::char_traits<charT>>
+inline std::basic_string_view<charT, traits> getToken(std::basic_string_view<charT, traits> pStr,
+ sal_Int32 nToken, charT cTok,
+ sal_Int32& rnIndex)
+{
+ assert(o3tl::IntCmp(rnIndex) <= o3tl::IntCmp(pStr.size()));
+
+ // Return an empty string and set rnIndex to -1 if either nToken or rnIndex is
+ // negative:
+ if (rnIndex >= 0 && nToken >= 0)
+ {
+ const charT* pOrgCharStr = pStr.data();
+ const charT* pCharStr = pOrgCharStr + rnIndex;
+ sal_Int32 nLen = pStr.size() - rnIndex;
+ sal_Int32 nTokCount = 0;
+ const charT* pCharStrStart = pCharStr;
+ while (nLen > 0)
+ {
+ if (*pCharStr == cTok)
+ {
+ nTokCount++;
+
+ if (nTokCount > nToken)
+ break;
+ if (nTokCount == nToken)
+ pCharStrStart = pCharStr + 1;
+ }
+
+ pCharStr++;
+ nLen--;
+ }
+ if (nTokCount >= nToken)
+ {
+ if (nLen > 0)
+ rnIndex = pCharStr - pOrgCharStr + 1;
+ else
+ rnIndex = -1;
+ return std::basic_string_view<charT, traits>(pCharStrStart, pCharStr - pCharStrStart);
+ }
+ }
+
+ rnIndex = -1;
+ return std::basic_string_view<charT, traits>();
+}
+// The following two overloads prevent deduction failures that would occur with their template
+// counterpart, when sv is of a type that is implicitly convertible to basic_string_view (like
+// OString or OUString):
+inline std::string_view getToken(std::string_view sv, sal_Int32 nToken, char cTok,
+ sal_Int32& rnIndex)
+{
+ return getToken<char>(sv, nToken, cTok, rnIndex);
+}
+inline std::u16string_view getToken(std::u16string_view sv, sal_Int32 nToken, char16_t cTok,
+ sal_Int32& rnIndex)
+{
+ return getToken<char16_t>(sv, nToken, cTok, rnIndex);
+}
+inline std::string_view getToken(std::string_view sv, sal_Int32 nToken, char cTok)
+{
+ sal_Int32 nIndex = 0;
+ return getToken<char>(sv, nToken, cTok, nIndex);
+}
+inline std::u16string_view getToken(std::u16string_view sv, sal_Int32 nToken, char16_t cTok)
+{
+ sal_Int32 nIndex = 0;
+ return getToken<char16_t>(sv, nToken, cTok, nIndex);
+}
+
+// Implementations of C++20 std::basic_string_view::starts_with and
+// std::basic_string_view::ends_with, until we can use those directly on all platforms:
+template <typename charT, typename traits = std::char_traits<charT>>
+constexpr bool starts_with(std::basic_string_view<charT, traits> sv,
+ std::basic_string_view<charT, traits> x) noexcept
+{
+#if defined __cpp_lib_starts_ends_with
+ return sv.starts_with(x);
+#else
+ return sv.substr(0, x.size()) == x;
+#endif
+}
+template <typename charT, typename traits = std::char_traits<charT>>
+constexpr bool starts_with(std::basic_string_view<charT, traits> sv, charT x) noexcept
+{
+#if defined __cpp_lib_starts_ends_with
+ return sv.starts_with(x);
+#else
+ return !sv.empty() && traits::eq(sv.front(), x);
+#endif
+}
+template <typename charT, typename traits = std::char_traits<charT>>
+constexpr bool starts_with(std::basic_string_view<charT, traits> sv, charT const* x)
+{
+#if defined __cpp_lib_starts_ends_with
+ return sv.starts_with(x);
+#else
+ return starts_with(sv, std::basic_string_view<charT, traits>(x));
+#endif
+}
+template <typename charT, typename traits = std::char_traits<charT>>
+constexpr bool ends_with(std::basic_string_view<charT, traits> sv,
+ std::basic_string_view<charT, traits> x) noexcept
+{
+#if defined __cpp_lib_starts_ends_with
+ return sv.ends_with(x);
+#else
+ return sv.size() >= x.size()
+ && sv.compare(sv.size() - x.size(), std::basic_string_view<charT, traits>::npos, x) == 0;
+#endif
+}
+template <typename charT, typename traits = std::char_traits<charT>>
+constexpr bool ends_with(std::basic_string_view<charT, traits> sv, charT x) noexcept
+{
+#if defined __cpp_lib_starts_ends_with
+ return sv.ends_with(x);
+#else
+ return !sv.empty() && traits::eq(sv.back(), x);
+#endif
+}
+template <typename charT, typename traits = std::char_traits<charT>>
+constexpr bool ends_with(std::basic_string_view<charT, traits> sv, charT const* x)
+{
+#if defined __cpp_lib_starts_ends_with
+ return sv.ends_with(x);
+#else
+ return ends_with(sv, std::basic_string_view<charT, traits>(x));
+#endif
+}
+// The following overloads prevent deduction failures that would occur with their template
+// counterparts, when x is of a type that is implicitly convertible to basic_string_view (like
+// OString or OUString, and we only bother to provide overloads for the char and char16_t cases, not
+// also for char32_t and wchar_t, nor for C++20 char8_t):
+constexpr bool starts_with(std::string_view sv, std::string_view x) noexcept
+{
+ return starts_with<char>(sv, x);
+}
+constexpr bool starts_with(std::u16string_view sv, std::u16string_view x) noexcept
+{
+ return starts_with<char16_t>(sv, x);
+}
+constexpr bool ends_with(std::string_view sv, std::string_view x) noexcept
+{
+ return ends_with<char>(sv, x);
+}
+constexpr bool ends_with(std::u16string_view sv, std::u16string_view x) noexcept
+{
+ return ends_with<char16_t>(sv, x);
+}
+
+// Variants of C++20 std::basic_string_view::starts_with and
+// std::basic_string_view::ends_with that have a rest out parameter, similar to our OString and
+// OUString startsWith and endsWith member functions:
+template <typename charT, typename traits = std::char_traits<charT>>
+constexpr bool starts_with(std::basic_string_view<charT, traits> sv,
+ std::basic_string_view<charT, traits> x,
+ std::basic_string_view<charT, traits>* rest) noexcept
+{
+ assert(rest != nullptr);
+ auto const found = starts_with(sv, x);
+ if (found)
+ {
+ *rest = sv.substr(x.length());
+ }
+ return found;
+}
+template <typename charT, typename traits = std::char_traits<charT>>
+constexpr bool starts_with(std::basic_string_view<charT, traits> sv, charT x,
+ std::basic_string_view<charT, traits>* rest) noexcept
+{
+ assert(rest != nullptr);
+ auto const found = starts_with(sv, x);
+ if (found)
+ {
+ *rest = sv.substr(1);
+ }
+ return found;
+}
+template <typename charT, typename traits = std::char_traits<charT>>
+constexpr bool starts_with(std::basic_string_view<charT, traits> sv, charT const* x,
+ std::basic_string_view<charT, traits>* rest)
+{
+ assert(rest != nullptr);
+ auto const found = starts_with(sv, x);
+ if (found)
+ {
+ *rest = sv.substr(traits::length(x));
+ }
+ return found;
+}
+template <typename charT, typename traits = std::char_traits<charT>>
+constexpr bool ends_with(std::basic_string_view<charT, traits> sv,
+ std::basic_string_view<charT, traits> x,
+ std::basic_string_view<charT, traits>* rest) noexcept
+{
+ assert(rest != nullptr);
+ auto const found = ends_with(sv, x);
+ if (found)
+ {
+ *rest = sv.substr(0, sv.length() - x.length());
+ }
+ return found;
+}
+template <typename charT, typename traits = std::char_traits<charT>>
+constexpr bool ends_with(std::basic_string_view<charT, traits> sv, charT x,
+ std::basic_string_view<charT, traits>* rest) noexcept
+{
+ assert(rest != nullptr);
+ auto const found = ends_with(sv, x);
+ if (found)
+ {
+ *rest = sv.substr(0, sv.length() - 1);
+ }
+ return found;
+}
+template <typename charT, typename traits = std::char_traits<charT>>
+constexpr bool ends_with(std::basic_string_view<charT, traits> sv, charT const* x,
+ std::basic_string_view<charT, traits>* rest)
+{
+ assert(rest != nullptr);
+ auto const found = ends_with(sv, x);
+ if (found)
+ {
+ *rest = sv.substr(0, sv.length() - traits::length(x));
+ }
+ return found;
+}
+// The following overloads prevent deduction failures that would occur with their template
+// counterparts, when x is of a type that is implicitly convertible to basic_string_view (like
+// OString or OUString, and we only bother to provide overloads for the char and char16_t cases, not
+// also for char32_t and wchar_t, nor for C++20 char8_t):
+constexpr bool starts_with(std::string_view sv, std::string_view x, std::string_view* rest) noexcept
+{
+ return starts_with<char>(sv, x, rest);
+}
+constexpr bool starts_with(std::u16string_view sv, std::u16string_view x,
+ std::u16string_view* rest) noexcept
+{
+ return starts_with<char16_t>(sv, x, rest);
+}
+constexpr bool ends_with(std::string_view sv, std::string_view x, std::string_view* rest) noexcept
+{
+ return ends_with<char>(sv, x, rest);
+}
+constexpr bool ends_with(std::u16string_view sv, std::u16string_view x,
+ std::u16string_view* rest) noexcept
+{
+ return ends_with<char16_t>(sv, x, rest);
+}
+
+namespace internal
+{
+inline bool implIsWhitespace(sal_Unicode c)
+{
+ /* Space or Control character? */
+ if ((c <= 32) && c)
+ return true;
+
+ /* Only in the General Punctuation area Space or Control characters are included? */
+ if ((c < 0x2000) || (c > 0x2029))
+ return false;
+
+ if ((c <= 0x200B) || /* U+2000 - U+200B All Spaces */
+ (c >= 0x2028)) /* U+2028 LINE SEPARATOR, U+2029 PARAGRAPH SEPARATOR */
+ return true;
+
+ return false;
+}
+} // namespace internal
+
+// Like OUString::trim, but for std::[u16]string_view:
+template <typename charT, typename traits = std::char_traits<charT>>
+std::basic_string_view<charT, traits> trim(std::basic_string_view<charT, traits> str)
+{
+ auto pFirst = str.data();
+ auto pLast = pFirst + str.size();
+
+ while ((pFirst < pLast) && internal::implIsWhitespace(*pFirst))
+ ++pFirst;
+
+ if (pFirst == pLast)
+ return {};
+
+ do
+ --pLast;
+ while (internal::implIsWhitespace(*pLast));
+
+ return std::basic_string_view<charT, traits>(pFirst, pLast - pFirst + 1);
+}
+
+// "deduction guides"
+
+inline auto trim(std::string_view str) { return trim<>(str); }
+inline auto trim(std::u16string_view str) { return trim<>(str); }
+
+// Like OString::toInt32, but for std::string_view:
+inline sal_Int32 toInt32(std::u16string_view str, sal_Int16 radix = 10)
+{
+ sal_Int64 n = rtl_ustr_toInt64_WithLength(str.data(), radix, str.size());
+ if (n < SAL_MIN_INT32 || n > SAL_MAX_INT32)
+ n = 0;
+ return n;
+}
+inline sal_Int32 toInt32(std::string_view str, sal_Int16 radix = 10)
+{
+ sal_Int64 n = rtl_str_toInt64_WithLength(str.data(), radix, str.size());
+ if (n < SAL_MIN_INT32 || n > SAL_MAX_INT32)
+ n = 0;
+ return n;
+}
+
+// Like OString::toUInt32, but for std::string_view:
+inline sal_uInt32 toUInt32(std::u16string_view str, sal_Int16 radix = 10)
+{
+ sal_Int64 n = rtl_ustr_toInt64_WithLength(str.data(), radix, str.size());
+ if (n < 0 || n > SAL_MAX_UINT32)
+ n = 0;
+ return n;
+}
+inline sal_uInt32 toUInt32(std::string_view str, sal_Int16 radix = 10)
+{
+ sal_Int64 n = rtl_str_toInt64_WithLength(str.data(), radix, str.size());
+ if (n < 0 || n > SAL_MAX_UINT32)
+ n = 0;
+ return n;
+}
+
+// Like OString::toInt64, but for std::string_view:
+inline sal_Int64 toInt64(std::u16string_view str, sal_Int16 radix = 10)
+{
+ return rtl_ustr_toInt64_WithLength(str.data(), radix, str.size());
+}
+inline sal_Int64 toInt64(std::string_view str, sal_Int16 radix = 10)
+{
+ return rtl_str_toInt64_WithLength(str.data(), radix, str.size());
+}
+
+// Like OString::toDouble, but for std::string_view:
+inline double toDouble(std::u16string_view str)
+{
+ return rtl_math_uStringToDouble(str.data(), str.data() + str.size(), '.', 0, nullptr, nullptr);
+}
+inline double toDouble(std::string_view str)
+{
+ return rtl_math_stringToDouble(str.data(), str.data() + str.size(), '.', 0, nullptr, nullptr);
+}
+
+// Like OUString::iterateCodePoints, but for std::string_view:
+inline sal_uInt32 iterateCodePoints(std::u16string_view string, sal_Int32* indexUtf16,
+ sal_Int32 incrementCodePoints = 1)
+{
+ std::size_t n;
+ char16_t cu;
+ sal_uInt32 cp;
+ assert(indexUtf16 != nullptr);
+ n = *indexUtf16;
+ assert(n <= string.length());
+ while (incrementCodePoints < 0)
+ {
+ assert(n > 0);
+ cu = string[--n];
+ if (rtl::isLowSurrogate(cu) && n != 0 && rtl::isHighSurrogate(string[n - 1]))
+ {
+ --n;
+ }
+ ++incrementCodePoints;
+ }
+ assert(n < string.length());
+ cu = string[n];
+ if (rtl::isHighSurrogate(cu) && string.length() - n >= 2 && rtl::isLowSurrogate(string[n + 1]))
+ {
+ cp = rtl::combineSurrogates(cu, string[n + 1]);
+ }
+ else
+ {
+ cp = cu;
+ }
+ while (incrementCodePoints > 0)
+ {
+ assert(n < string.length());
+ cu = string[n++];
+ if (rtl::isHighSurrogate(cu) && n != string.length() && rtl::isLowSurrogate(string[n]))
+ {
+ ++n;
+ }
+ --incrementCodePoints;
+ }
+ assert(n <= string.length());
+ *indexUtf16 = n;
+ return cp;
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/strong_int.hxx b/include/o3tl/strong_int.hxx
new file mode 100644
index 0000000000..8210795589
--- /dev/null
+++ b/include/o3tl/strong_int.hxx
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_O3TL_STRONG_INT_HXX
+#define INCLUDED_O3TL_STRONG_INT_HXX
+
+#include <sal/config.h>
+#include <limits>
+#include <cassert>
+#include <type_traits>
+
+namespace o3tl
+{
+
+#if !defined __COVERITY__
+
+namespace detail {
+
+template<typename T1, typename T2> constexpr
+typename std::enable_if<
+ std::is_signed<T1>::value && std::is_signed<T2>::value, bool>::type
+isInRange(T2 value) {
+ return value >= std::numeric_limits<T1>::min()
+ && value <= std::numeric_limits<T1>::max();
+}
+
+template<typename T1, typename T2> constexpr
+typename std::enable_if<
+ std::is_signed<T1>::value && std::is_unsigned<T2>::value, bool>::type
+isInRange(T2 value) {
+ return value
+ <= static_cast<typename std::make_unsigned<T1>::type>(
+ std::numeric_limits<T1>::max());
+}
+
+template<typename T1, typename T2> constexpr
+typename std::enable_if<
+ std::is_unsigned<T1>::value && std::is_signed<T2>::value, bool>::type
+isInRange(T2 value) {
+ return value >= 0
+ && (static_cast<typename std::make_unsigned<T2>::type>(value)
+ <= std::numeric_limits<T1>::max());
+}
+
+template<typename T1, typename T2> constexpr
+typename std::enable_if<
+ std::is_unsigned<T1>::value && std::is_unsigned<T2>::value, bool>::type
+isInRange(T2 value) {
+ return value <= std::numeric_limits<T1>::max();
+}
+
+}
+
+#endif
+
+///
+/// Wrap up an integer type so that we prevent accidental conversion to other integer types.
+///
+/// e.g.
+/// typedef o3tl::strong_int<unsigned, struct MyIntTag> MyInt;
+///
+/// \param UNDERLYING_TYPE the underlying scalar type
+/// \param PHANTOM_TYPE a type tag, used to distinguish this instantiation of the template
+/// from other instantiations with the same UNDERLYING_TYPE.
+///
+template <typename UNDERLYING_TYPE, typename PHANTOM_TYPE>
+struct strong_int
+{
+public:
+ template<typename T> explicit constexpr strong_int(
+ T value,
+ typename std::enable_if<std::is_integral<T>::value, int>::type = 0):
+ m_value(value)
+ {
+#if !defined __COVERITY__
+ // catch attempts to pass in out-of-range values early
+ assert(detail::isInRange<UNDERLYING_TYPE>(value)
+ && "out of range");
+#endif
+ }
+ strong_int() : m_value(0) {}
+
+ explicit constexpr operator UNDERLYING_TYPE() const { return m_value; }
+ explicit operator bool() const { return m_value != 0; }
+ UNDERLYING_TYPE get() const { return m_value; }
+
+ bool operator<(strong_int const & other) const { return m_value < other.m_value; }
+ bool operator<=(strong_int const & other) const { return m_value <= other.m_value; }
+ bool operator>(strong_int const & other) const { return m_value > other.m_value; }
+ bool operator>=(strong_int const & other) const { return m_value >= other.m_value; }
+ bool operator==(strong_int const & other) const { return m_value == other.m_value; }
+ bool operator!=(strong_int const & other) const { return m_value != other.m_value; }
+ strong_int& operator++() { ++m_value; return *this; }
+ strong_int operator++(int) { UNDERLYING_TYPE nOldValue = m_value; ++m_value; return strong_int(nOldValue); }
+ strong_int& operator--() { --m_value; return *this; }
+ strong_int operator--(int) { UNDERLYING_TYPE nOldValue = m_value; --m_value; return strong_int(nOldValue); }
+ strong_int& operator+=(strong_int const & other) { m_value += other.m_value; return *this; }
+ strong_int& operator-=(strong_int const & other) { m_value -= other.m_value; return *this; }
+ strong_int& operator%=(strong_int const & other) { m_value %= other.m_value; return *this; }
+ strong_int& operator*=(strong_int const & other) { m_value *= other.m_value; return *this; }
+ strong_int& operator/=(strong_int const & other) { m_value /= other.m_value; return *this; }
+ [[nodiscard]]
+ strong_int operator%(strong_int const & other) const { return strong_int(m_value % other.m_value); }
+ [[nodiscard]]
+ strong_int operator-() const { return strong_int(-m_value); }
+ [[nodiscard]]
+ strong_int operator*(strong_int const & other) const { return strong_int(m_value * other.m_value); }
+ [[nodiscard]]
+ strong_int operator/(strong_int const & other) const { return strong_int(m_value / other.m_value); }
+
+ bool anyOf(strong_int v) const {
+ return *this == v;
+ }
+
+ template<typename... Args>
+ bool anyOf(strong_int first, Args... args) const {
+ return *this == first || anyOf(args...);
+ }
+
+private:
+ UNDERLYING_TYPE m_value;
+};
+
+template <typename UT, typename PT>
+strong_int<UT,PT> operator+(strong_int<UT,PT> const & lhs, strong_int<UT,PT> const & rhs)
+{
+ return strong_int<UT,PT>(lhs.get() + rhs.get());
+}
+
+template <typename UT, typename PT>
+strong_int<UT,PT> operator-(strong_int<UT,PT> const & lhs, strong_int<UT,PT> const & rhs)
+{
+ return strong_int<UT,PT>(lhs.get() - rhs.get());
+}
+
+}; // namespace o3tl
+
+#endif /* INCLUDED_O3TL_STRONG_INT_HXX */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/temporary.hxx b/include/o3tl/temporary.hxx
new file mode 100644
index 0000000000..50d006e26d
--- /dev/null
+++ b/include/o3tl/temporary.hxx
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_O3TL_TEMPORARY_HXX
+#define INCLUDED_O3TL_TEMPORARY_HXX
+
+#include <sal/config.h>
+
+namespace o3tl
+{
+// Cast an rvalue to an lvalue. Can be useful if a function parameter is a pointer/reference to T,
+// and some call site doesn't need the value beyond the call itself (e.g., in a call like
+// std::modf(x, &o3tl::temporary(double())) to obtain the fractional part of x, ignoring the
+// integral part).
+template <typename T> constexpr T& temporary(T&& x) { return static_cast<T&>(x); }
+template <typename T> constexpr T& temporary(T&) = delete;
+}
+
+#endif /* INCLUDED_O3TL_TEMPORARY_HXX */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/typed_flags_set.hxx b/include/o3tl/typed_flags_set.hxx
new file mode 100644
index 0000000000..413ee9579d
--- /dev/null
+++ b/include/o3tl/typed_flags_set.hxx
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_O3TL_TYPED_FLAGS_SET_HXX
+#define INCLUDED_O3TL_TYPED_FLAGS_SET_HXX
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <type_traits>
+
+#include <o3tl/underlyingenumvalue.hxx>
+
+namespace o3tl {
+
+namespace detail {
+
+template<typename T> constexpr
+typename std::enable_if<std::is_signed<T>::value, bool>::type isNonNegative(
+ T value)
+{
+ return value >= 0;
+}
+
+template<typename T> constexpr
+typename std::enable_if<std::is_unsigned<T>::value, bool>::type isNonNegative(T)
+{
+ return true;
+}
+
+}
+
+template<typename T> struct typed_flags {};
+
+/// Mark a (scoped) enumeration as a set of bit flags, with accompanying
+/// operations.
+///
+/// template<>
+/// struct o3tl::typed_flags<TheE>: o3tl::is_typed_flags<TheE, TheM> {};
+///
+/// All relevant values must be non-negative. (Typically, the enumeration's
+/// underlying type will either be fixed and unsigned, or it will be unfixed---
+/// and can thus default to a signed type---and all enumerators will have non-
+/// negative values.)
+///
+/// \param E the enumeration type.
+/// \param M the all-bits-set value for the bit flags.
+template<typename E, typename std::underlying_type<E>::type M>
+struct is_typed_flags {
+ static_assert(
+ M >= 0, "is_typed_flags expects only non-negative bit values");
+
+ typedef E Self;
+
+ class Wrap {
+ public:
+ typedef is_typed_flags Unwrapped;
+
+ explicit constexpr Wrap(typename std::underlying_type<E>::type value):
+ value_(value)
+ {
+ assert(detail::isNonNegative(value));
+ assert(
+ static_cast<typename std::underlying_type<E>::type>(~0) == M
+ // avoid "operands don't affect result" warnings when M
+ // covers all bits of the underlying type
+ || (value & ~M) == 0);
+ }
+
+ constexpr operator E() const { return static_cast<E>(value_); }
+
+ explicit constexpr operator typename std::underlying_type<E>::type()
+ const
+ { return value_; }
+
+ explicit constexpr operator bool() const { return value_ != 0; }
+
+ private:
+ typename std::underlying_type<E>::type value_;
+ };
+
+ static typename std::underlying_type<E>::type const mask = M;
+};
+
+}
+
+template<typename E>
+constexpr typename o3tl::typed_flags<E>::Wrap operator ~(E rhs) {
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(rhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::typed_flags<E>::mask
+ & ~o3tl::to_underlying(rhs));
+}
+
+template<typename E> constexpr typename o3tl::typed_flags<E>::Wrap operator ~(
+ typename o3tl::typed_flags<E>::Wrap rhs)
+{
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::typed_flags<E>::mask
+ & ~o3tl::to_underlying<E>(rhs));
+}
+
+template<typename E> constexpr typename o3tl::typed_flags<E>::Wrap operator ^(
+ E lhs, E rhs)
+{
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(lhs)));
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(rhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::to_underlying(lhs)
+ ^ o3tl::to_underlying(rhs));
+}
+
+template<typename E> constexpr typename o3tl::typed_flags<E>::Wrap operator ^(
+ E lhs, typename o3tl::typed_flags<E>::Wrap rhs)
+{
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(lhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::to_underlying(lhs)
+ ^ o3tl::to_underlying<E>(rhs));
+}
+
+template<typename E> constexpr typename o3tl::typed_flags<E>::Wrap operator ^(
+ typename o3tl::typed_flags<E>::Wrap lhs, E rhs)
+{
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(rhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::to_underlying<E>(lhs)
+ ^ o3tl::to_underlying(rhs));
+}
+
+template<typename W> constexpr
+typename o3tl::typed_flags<typename W::Unwrapped::Self>::Wrap operator ^(
+ W lhs, W rhs)
+{
+ return static_cast<W>(
+ o3tl::to_underlying<typename W::Unwrapped::Self>(lhs)
+ ^ o3tl::to_underlying<typename W::Unwrapped::Self>(rhs));
+}
+
+template<typename E>
+constexpr typename o3tl::typed_flags<E>::Wrap operator &(E lhs, E rhs) {
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(lhs)));
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(rhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::to_underlying(lhs)
+ & o3tl::to_underlying(rhs));
+}
+
+template<typename E> constexpr typename o3tl::typed_flags<E>::Wrap operator &(
+ E lhs, typename o3tl::typed_flags<E>::Wrap rhs)
+{
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(lhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::to_underlying(lhs)
+ & o3tl::to_underlying<E>(rhs));
+}
+
+template<typename E> constexpr typename o3tl::typed_flags<E>::Wrap operator &(
+ typename o3tl::typed_flags<E>::Wrap lhs, E rhs)
+{
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(rhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::to_underlying<E>(lhs)
+ & o3tl::to_underlying(rhs));
+}
+
+template<typename W> constexpr
+typename o3tl::typed_flags<typename W::Unwrapped::Self>::Wrap operator &(
+ W lhs, W rhs)
+{
+ return static_cast<W>(
+ o3tl::to_underlying<typename W::Unwrapped::Self>(lhs)
+ & o3tl::to_underlying<typename W::Unwrapped::Self>(rhs));
+}
+
+template<typename E>
+constexpr typename o3tl::typed_flags<E>::Wrap operator |(E lhs, E rhs) {
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(lhs)));
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(rhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::to_underlying(lhs)
+ | o3tl::to_underlying(rhs));
+}
+
+template<typename E> constexpr typename o3tl::typed_flags<E>::Wrap operator |(
+ E lhs, typename o3tl::typed_flags<E>::Wrap rhs)
+{
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(lhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::to_underlying(lhs)
+ | o3tl::to_underlying<E>(rhs));
+}
+
+template<typename E> constexpr typename o3tl::typed_flags<E>::Wrap operator |(
+ typename o3tl::typed_flags<E>::Wrap lhs, E rhs)
+{
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(rhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::to_underlying<E>(lhs)
+ | o3tl::to_underlying(rhs));
+}
+
+template<typename W> constexpr
+typename o3tl::typed_flags<typename W::Unwrapped::Self>::Wrap operator |(
+ W lhs, W rhs)
+{
+ return static_cast<W>(
+ o3tl::to_underlying<typename W::Unwrapped::Self>(lhs)
+ | o3tl::to_underlying<typename W::Unwrapped::Self>(rhs));
+}
+
+template<typename E>
+inline typename o3tl::typed_flags<E>::Self operator &=(E & lhs, E rhs) {
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(lhs)));
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(rhs)));
+ lhs = lhs & rhs;
+ return lhs;
+}
+
+template<typename E>
+inline typename o3tl::typed_flags<E>::Self operator &=(
+ E & lhs, typename o3tl::typed_flags<E>::Wrap rhs)
+{
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(lhs)));
+ lhs = lhs & rhs;
+ return lhs;
+}
+
+template<typename E>
+inline typename o3tl::typed_flags<E>::Self operator |=(E & lhs, E rhs) {
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(lhs)));
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(rhs)));
+ lhs = lhs | rhs;
+ return lhs;
+}
+
+template<typename E>
+inline typename o3tl::typed_flags<E>::Self operator |=(
+ E & lhs, typename o3tl::typed_flags<E>::Wrap rhs)
+{
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(lhs)));
+ lhs = lhs | rhs;
+ return lhs;
+}
+
+template<typename E>
+inline typename o3tl::typed_flags<E>::Self operator ^=(E & lhs, E rhs) {
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(lhs)));
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(rhs)));
+ lhs = lhs ^ rhs;
+ return lhs;
+}
+
+template<typename E>
+inline typename o3tl::typed_flags<E>::Self operator ^=(
+ E & lhs, typename o3tl::typed_flags<E>::Wrap rhs)
+{
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::to_underlying(lhs)));
+ lhs = lhs ^ rhs;
+ return lhs;
+}
+
+#endif /* INCLUDED_O3TL_TYPED_FLAGS_SET_HXX */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/underlyingenumvalue.hxx b/include/o3tl/underlyingenumvalue.hxx
new file mode 100644
index 0000000000..a913ab3853
--- /dev/null
+++ b/include/o3tl/underlyingenumvalue.hxx
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_O3TL_UNDERLYINGENUMVALUE_HXX
+#define INCLUDED_O3TL_UNDERLYINGENUMVALUE_HXX
+
+#include <sal/config.h>
+
+#include <type_traits>
+
+namespace o3tl
+{
+// An implementation of C++23 std::to_underlying
+// For a value e of an enumeration type T, return the corresponding value of T's underlying type:
+template <typename T> constexpr std::underlying_type_t<T> to_underlying(T e)
+{
+ return static_cast<std::underlying_type_t<T>>(e);
+}
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/unit_conversion.hxx b/include/o3tl/unit_conversion.hxx
new file mode 100644
index 0000000000..54eb8cd246
--- /dev/null
+++ b/include/o3tl/unit_conversion.hxx
@@ -0,0 +1,269 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <o3tl/safeint.hxx>
+#include <sal/types.h>
+
+#include <array>
+#include <cassert>
+#include <numeric>
+#include <utility>
+#include <type_traits>
+
+namespace o3tl
+{
+// Length units
+enum class Length
+{
+ mm100 = 0, // 1/100th mm
+ mm10, // 1/10 mm, corresponds to MapUnit::Map10thMM
+ mm, // millimeter
+ cm, // centimeter
+ m, // meter
+ km, // kilometer
+ emu, // English Metric Unit: 1/360000 cm, 1/914400 in
+ twip, // "Twentieth of a point" aka "dxa": 1/20 pt
+ pt, // Point: 1/72 in
+ pc, // Pica: 1/6 in, corresponds to FieldUnit::PICA and MeasureUnit::PICA
+ in1000, // 1/1000 in, corresponds to MapUnit::Map1000thInch
+ in100, // 1/100 in, corresponds to MapUnit::Map100thInch
+ in10, // 1/10 in, corresponds to MapUnit::Map10thInch
+ in, // inch
+ ft, // foot
+ mi, // mile
+ master, // PPT Master Unit: 1/576 in
+ px, // "pixel" unit: 15 twip (96 ppi), corresponds to MeasureUnit::PIXEL
+ ch, // "char" unit: 210 twip (14 px), corresponds to FieldUnit::CHAR
+ line, // "line" unit: 312 twip, corresponds to FieldUnit::LINE
+ count, // <== add new units above this last entry
+ invalid = -1
+};
+
+// If other categories of units would be needed (like time), a separate scoped enum
+// should be created, respective conversion array prepared in detail namespace, and
+// respective md(NewUnit, NewUnit) overload introduced, which would allow using
+// o3tl::convert(), o3tl::convertSaturate() and o3tl::getConversionMulDiv() with the
+// new category in a type-safe way, without mixing unrelated units.
+
+namespace detail
+{
+// Common utilities
+
+// A special function to avoid compiler warning comparing signed and unsigned values
+template <typename I> constexpr bool isBetween(I n, sal_Int64 min, sal_Int64 max)
+{
+ assert(max > 0 && min < 0);
+ if constexpr (std::is_signed_v<I>)
+ return n >= min && n <= max;
+ else
+ return n <= sal_uInt64(max);
+}
+
+// Ensure correct rounding for both positive and negative integers
+template <typename I, std::enable_if_t<std::is_integral_v<I>, int> = 0>
+constexpr sal_Int64 MulDiv(I n, sal_Int64 m, sal_Int64 d)
+{
+ assert(m > 0 && d > 0);
+ assert(isBetween(n, (SAL_MIN_INT64 + d / 2) / m, (SAL_MAX_INT64 - d / 2) / m));
+ return (n >= 0 ? (n * m + d / 2) : (n * m - d / 2)) / d;
+}
+template <typename F, std::enable_if_t<std::is_floating_point_v<F>, int> = 0>
+constexpr double MulDiv(F f, sal_Int64 m, sal_Int64 d)
+{
+ assert(m > 0 && d > 0);
+ return f * (double(m) / d);
+}
+
+template <typename I, std::enable_if_t<std::is_integral_v<I>, int> = 0>
+constexpr sal_Int64 MulDiv(I n, sal_Int64 m, sal_Int64 d, bool& bOverflow, sal_Int64 nDefault)
+{
+ if (!isBetween(n, (SAL_MIN_INT64 + d / 2) / m, (SAL_MAX_INT64 - d / 2) / m))
+ {
+ bOverflow = true;
+ return nDefault;
+ }
+ bOverflow = false;
+ return MulDiv(n, m, d);
+}
+
+template <typename I, std::enable_if_t<std::is_integral_v<I>, int> = 0>
+constexpr sal_Int64 MulDivSaturate(I n, sal_Int64 m, sal_Int64 d)
+{
+ if (sal_Int64 d_2 = d / 2; !isBetween(n, (SAL_MIN_INT64 + d_2) / m, (SAL_MAX_INT64 - d_2) / m))
+ {
+ if (n >= 0)
+ {
+ if (m > d && std::make_unsigned_t<I>(n) > sal_uInt64(SAL_MAX_INT64 / m * d - d_2))
+ return SAL_MAX_INT64; // saturate
+ return saturating_add<sal_uInt64>(n, d_2) / d * m; // divide before multiplication
+ }
+ else if constexpr (std::is_signed_v<I>) // n < 0; don't compile for unsigned n
+ {
+ if (m > d && n < SAL_MIN_INT64 / m * d + d_2)
+ return SAL_MIN_INT64; // saturate
+ return saturating_sub<sal_Int64>(n, d_2) / d * m; // divide before multiplication
+ }
+ }
+ return MulDiv(n, m, d);
+}
+
+template <class M, class N> constexpr std::common_type_t<M, N> asserting_gcd(M m, N n)
+{
+ auto ret = std::gcd(m, n);
+ assert(ret != 0);
+ return ret;
+}
+
+// Packs integral multiplier and divisor for conversion from one unit to another
+struct m_and_d
+{
+ sal_Int64 m; // multiplier
+ sal_Int64 d; // divisor
+ constexpr m_and_d(sal_Int64 _m, sal_Int64 _d)
+ : m(_m / asserting_gcd(_m, _d)) // make sure to use smallest quotients here because
+ , d(_d / asserting_gcd(_m, _d)) // they will be multiplied when building final table
+ {
+ assert(_m > 0 && _d > 0);
+ }
+};
+
+// Resulting static array N x N of all quotients to convert between all units. The
+// quotients are minimal to allow largest range of converted numbers without overflow.
+// Maybe o3tl::enumarray could be used here, but it's not constexpr yet.
+template <int N> constexpr auto prepareMDArray(const m_and_d (&mdBase)[N])
+{
+ std::array<std::array<sal_Int64, N>, N> a{};
+ for (int i = 0; i < N; ++i)
+ {
+ a[i][i] = 1;
+ for (int j = 0; j < i; ++j)
+ {
+ assert(mdBase[i].m < SAL_MAX_INT64 / mdBase[j].d);
+ assert(mdBase[i].d < SAL_MAX_INT64 / mdBase[j].m);
+ const sal_Int64 m = mdBase[i].m * mdBase[j].d, d = mdBase[i].d * mdBase[j].m;
+ const sal_Int64 g = asserting_gcd(m, d);
+ a[i][j] = m / g;
+ a[j][i] = d / g;
+ }
+ }
+ return a;
+}
+
+// A generic template used for fundamental arithmetic types
+template <typename U> constexpr sal_Int64 md(U i, U /*j*/) { return i; }
+
+// Length units implementation
+
+// Array of conversion quotients for mm, used to build final conversion table. Entries
+// are { multiplier, divider } to convert respective unit *to* mm. Order of elements
+// corresponds to order in o3tl::Length enum (Length::count and Length::invalid omitted).
+constexpr m_and_d mdBaseLen[] = {
+ { 1, 100 }, // mm100 => mm
+ { 1, 10 }, // mm10 => mm
+ { 1, 1 }, // mm => mm
+ { 10, 1 }, // cm => mm
+ { 1000, 1 }, // m => mm
+ { 1000000, 1 }, // km => mm
+ { 1, 36000 }, // emu => mm
+ { 254, 10 * 1440 }, // twip => mm
+ { 254, 10 * 72 }, // pt => mm
+ { 254, 10 * 6 }, // pc => mm
+ { 254, 10000 }, // in1000 => mm
+ { 254, 1000 }, // in100 => mm
+ { 254, 100 }, // in10 => mm
+ { 254, 10 }, // in => mm
+ { 254 * 12, 10 }, // ft => mm
+ { 254 * 12 * 5280, 10 }, // mi => mm
+ { 254, 10 * 576 }, // master => mm
+ { 254 * 15, 10 * 1440 }, // px => mm
+ { 254 * 210, 10 * 1440 }, // ch => mm
+ { 254 * 312, 10 * 1440 }, // line => mm
+};
+static_assert(std::size(mdBaseLen) == static_cast<int>(Length::count),
+ "mdBaseL must have an entry for each unit in o3tl::Length");
+
+// The resulting multipliers and divisors array
+constexpr auto aLengthMDArray = prepareMDArray(mdBaseLen);
+
+// an overload taking Length
+constexpr sal_Int64 md(Length i, Length j)
+{
+ const int nI = static_cast<int>(i), nJ = static_cast<int>(j);
+ assert(nI >= 0 && o3tl::make_unsigned(nI) < aLengthMDArray.size());
+ assert(nJ >= 0 && o3tl::make_unsigned(nJ) < aLengthMDArray.size());
+ return aLengthMDArray[nI][nJ];
+}
+
+// here might go overloads of md() taking other units ...
+}
+
+// Unchecked conversion. Takes a number value, multiplier and divisor
+template <typename N> constexpr auto convert(N n, sal_Int64 mul, sal_Int64 div)
+{
+ return detail::MulDiv(n, mul, div);
+}
+
+// Unchecked conversion. Takes a number value and units defined in this header
+template <typename N, typename U> constexpr auto convert(N n, U from, U to)
+{
+ return convert(n, detail::md(from, to), detail::md(to, from));
+}
+
+// Convert to twips - for convenience as we do this a lot
+template <typename N> constexpr auto toTwips(N number, Length from)
+{
+ return convert(number, from, Length::twip);
+}
+
+// Returns nDefault if intermediate multiplication overflows sal_Int64 (only for integral types).
+// On return, bOverflow indicates if overflow happened. nDefault is returned when overflow occurs.
+template <typename N, typename U>
+constexpr auto convert(N n, U from, U to, bool& bOverflow, sal_Int64 nDefault = 0)
+{
+ return detail::MulDiv(n, detail::md(from, to), detail::md(to, from), bOverflow, nDefault);
+}
+
+// Conversion with saturation (only for integral types). For too large input returns SAL_MAX_INT64.
+// When intermediate multiplication would overflow, but the end result is in sal_Int64 range, the
+// precision is decreased because of inversion of multiplication and division.
+template <typename N, typename U> constexpr auto convertSaturate(N n, U from, U to)
+{
+ return detail::MulDivSaturate(n, detail::md(from, to), detail::md(to, from));
+}
+
+// Conversion with saturation (only for integral types), optimized for return types smaller than
+// sal_Int64. In this case, it's easier to clamp input values to known bounds, than to do some
+// preprocessing to handle too large input values, just to clamp the result anyway. Use it like:
+//
+// sal_Int32 n = convertNarrowing<sal_Int32, o3tl::Length::mm100, o3tl::Length::emu>(m);
+template <typename Out, auto from, auto to, typename N,
+ std::enable_if_t<
+ std::is_integral_v<N> && std::is_integral_v<Out> && sizeof(Out) < sizeof(sal_Int64),
+ int> = 0>
+constexpr Out convertNarrowing(N n)
+{
+ constexpr sal_Int64 nMin = convertSaturate(std::numeric_limits<Out>::min(), to, from);
+ constexpr sal_Int64 nMax = convertSaturate(std::numeric_limits<Out>::max(), to, from);
+ if (static_cast<sal_Int64>(n) > nMax)
+ return std::numeric_limits<Out>::max();
+ if (static_cast<sal_Int64>(n) < nMin)
+ return std::numeric_limits<Out>::min();
+ return convert(n, from, to);
+}
+
+// Return a pair { multiplier, divisor } for a given conversion
+template <typename U> constexpr std::pair<sal_Int64, sal_Int64> getConversionMulDiv(U from, U to)
+{
+ return { detail::md(from, to), detail::md(to, from) };
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/unreachable.hxx b/include/o3tl/unreachable.hxx
new file mode 100644
index 0000000000..604f7a2f77
--- /dev/null
+++ b/include/o3tl/unreachable.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_O3TL_UNREACHABLE_HXX
+#define INCLUDED_O3TL_UNREACHABLE_HXX
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <utility>
+
+// An approximation of C++23 std::unreachable
+// (<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0627r6.pdf> "Function to mark
+// unreachable code").
+
+#if defined __cpp_lib_unreachable
+
+#define O3TL_UNREACHABLE (::std::unreachable())
+
+#else
+
+// This fallback implementation is inspired by LLVM's LLVM_BUILTIN_UNREACHABLE
+// (llvm/include/llvm/Support/Compiler.h).
+
+#if defined _MSC_VER
+#define O3TL_UNREACHABLE_detail __assume(false)
+#else // assuming Clang or GCC with support for:
+#define O3TL_UNREACHABLE_detail __builtin_unreachable()
+#endif
+
+#define O3TL_UNREACHABLE \
+ do \
+ { \
+ assert(false); \
+ O3TL_UNREACHABLE_detail; \
+ } while (false)
+
+#endif
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/unsafe_downcast.hxx b/include/o3tl/unsafe_downcast.hxx
new file mode 100644
index 0000000000..b49994eff1
--- /dev/null
+++ b/include/o3tl/unsafe_downcast.hxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_O3TL_UNSAFE_DOWNCAST_HXX
+#define INCLUDED_O3TL_UNSAFE_DOWNCAST_HXX
+
+#include <cassert>
+#include <type_traits>
+
+namespace o3tl
+{
+// Do a downcast from polymorphic `BasePtr` to `DerivedPtr` when it is known that `p` is actually
+// pointing to an object of derived type (or is a nullptr). This is potentially cheaper than
+// dynamic_cast and helps to avoid Coverity warnings about unchecked dynamic_cast.
+template <typename DerivedPtr, typename BasePtr>
+std::enable_if_t<
+ (std::is_pointer_v<
+ DerivedPtr> && std::is_pointer_v<BasePtr> && std::is_base_of_v<std::remove_pointer_t<BasePtr>, std::remove_pointer_t<DerivedPtr>>),
+ DerivedPtr>
+unsafe_downcast(BasePtr p)
+{
+ assert(p == nullptr || dynamic_cast<DerivedPtr>(p) != nullptr);
+ return static_cast<DerivedPtr>(p);
+}
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/o3tl/vector_pool.hxx b/include/o3tl/vector_pool.hxx
new file mode 100644
index 0000000000..eec1a1a3c5
--- /dev/null
+++ b/include/o3tl/vector_pool.hxx
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_O3TL_VECTOR_POOL_HXX
+#define INCLUDED_O3TL_VECTOR_POOL_HXX
+
+#include <utility>
+#include <vector>
+
+namespace o3tl
+{
+ namespace detail
+ {
+ template<typename ValueType, class Container> class simple_pool_impl :
+ public Container
+ {
+ typedef typename Container::value_type value_type;
+ std::ptrdiff_t mnFirstFreeIndex;
+
+ public:
+ simple_pool_impl() :
+ mnFirstFreeIndex(-1)
+ {}
+
+ std::ptrdiff_t alloc()
+ {
+ return store(ValueType());
+ }
+
+ std::ptrdiff_t store(const ValueType& rCopy)
+ {
+ if( mnFirstFreeIndex != -1 )
+ {
+ std::ptrdiff_t nIdx=mnFirstFreeIndex;
+ mnFirstFreeIndex = this->at(mnFirstFreeIndex).nextFree;
+ this->at(nIdx).value = rCopy;
+ this->at(nIdx).nextFree = -1;
+
+ return nIdx;
+ }
+ else
+ {
+ this->push_back(value_type(rCopy));
+ return this->size()-1;
+ }
+ }
+
+ void free( std::ptrdiff_t nIdx )
+ {
+ this->at(nIdx).nextFree = mnFirstFreeIndex;
+ mnFirstFreeIndex = nIdx;
+ }
+
+ const ValueType& get( std::ptrdiff_t nIdx ) const
+ {
+ return this->operator[](nIdx).value;
+ }
+ ValueType& get( std::ptrdiff_t nIdx )
+ {
+ return this->operator[](nIdx).value;
+ }
+ };
+
+ template< typename ValueType > struct struct_from_value
+ {
+ struct type
+ {
+ type() :
+ value(),
+ nextFree(-1)
+ {}
+ explicit type( ValueType val ) :
+ value(std::move(val)),
+ nextFree(-1)
+ {}
+
+ ValueType value;
+ std::ptrdiff_t nextFree;
+ };
+ };
+ }
+
+ /** Simple vector-based memory pool allocator
+
+ This template can be used to provide simple pooled memory
+ allocation from a container class that adheres to the stl
+ random access container concept. Note that alloc/free works
+ with _indices_ into the container!
+
+ @example
+ <pre>
+vector_pool<type> myPool;
+int nIdx=myPool.alloc();
+myPool[nIdx] = myVal;
+ ... do stuff ...
+myPool.free(nIdx);
+ </pre>
+ */
+ template<typename ValueType> struct vector_pool :
+ public detail::simple_pool_impl<ValueType,
+ std::vector<typename detail::struct_from_value<ValueType>::type > >
+ {};
+}
+
+#endif /* INCLUDED_O3TL_VECTOR_POOL_HXX */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/vector_utils.hxx b/include/o3tl/vector_utils.hxx
new file mode 100644
index 0000000000..1fe34bcaf4
--- /dev/null
+++ b/include/o3tl/vector_utils.hxx
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <unordered_set>
+#include <vector>
+
+namespace o3tl
+{
+// removes duplicated elements in a vector
+template <typename T> void remove_duplicates(std::vector<T>& rVector)
+{
+ std::unordered_set<T> aSet;
+ auto aEnd = std::copy_if(rVector.begin(), rVector.end(), rVector.begin(),
+ [&aSet](T const& rElement) { return aSet.insert(rElement).second; });
+ rVector.erase(aEnd, rVector.end());
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */