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.hxx59
-rw-r--r--include/o3tl/cow_wrapper.hxx365
-rw-r--r--include/o3tl/cppunittraitshelper.hxx27
-rw-r--r--include/o3tl/deleter.hxx61
-rw-r--r--include/o3tl/enumarray.hxx140
-rw-r--r--include/o3tl/enumrange.hxx87
-rw-r--r--include/o3tl/float_int_conversion.hxx57
-rw-r--r--include/o3tl/functional.hxx63
-rw-r--r--include/o3tl/lazy_update.hxx89
-rw-r--r--include/o3tl/lru_map.hxx185
-rw-r--r--include/o3tl/make_shared.hxx33
-rw-r--r--include/o3tl/numeric.hxx28
-rw-r--r--include/o3tl/runtimetooustring.hxx48
-rw-r--r--include/o3tl/safeint.hxx241
-rw-r--r--include/o3tl/sorted_vector.hxx376
-rw-r--r--include/o3tl/span.hxx90
-rw-r--r--include/o3tl/strong_int.hxx145
-rw-r--r--include/o3tl/temporary.hxx27
-rw-r--r--include/o3tl/typed_flags_set.hxx326
-rw-r--r--include/o3tl/underlyingenumvalue.hxx28
-rw-r--r--include/o3tl/unreachable.hxx39
-rw-r--r--include/o3tl/unsafe_downcast.hxx35
-rw-r--r--include/o3tl/vector_pool.hxx122
-rw-r--r--include/o3tl/vector_utils.hxx27
25 files changed, 3023 insertions, 0 deletions
diff --git a/include/o3tl/any.hxx b/include/o3tl/any.hxx
new file mode 100644
index 000000000..0acccff64
--- /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 000000000..6ffab02e7
--- /dev/null
+++ b/include/o3tl/char16_t2wchar_t.hxx
@@ -0,0 +1,59 @@
+/* -*- 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_CHAR16_T2WCHAR_T_HXX
+#define INCLUDED_O3TL_CHAR16_T2WCHAR_T_HXX
+
+#include <sal/config.h>
+
+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);
+}
+#endif
+
+}
+
+#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 000000000..b9c463b85
--- /dev/null
+++ b/include/o3tl/cow_wrapper.hxx
@@ -0,0 +1,365 @@
+/* -*- 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 <memory>
+#include <osl/interlck.h>
+
+#include <utility>
+#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 )
+ {
+ if( rCount == 1 ) // caller is already the only/last reference
+ return false;
+ else
+ 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;
+ }
+
+ ~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;
+ }
+
+ 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 000000000..d9f75a61b
--- /dev/null
+++ b/include/o3tl/cppunittraitshelper.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_CPPUNITTRAITSHELPER_HXX
+#define INCLUDED_O3TL_CPPUNITTRAITSHELPER_HXX
+
+#include <sal/config.h>
+
+#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<unsigned>::toString(unsigned(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 000000000..4e90ca5a6
--- /dev/null
+++ b/include/o3tl/deleter.hxx
@@ -0,0 +1,61 @@
+/* -*- 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 <com/sun/star/uno/Exception.hpp>
+#include <sal/log.hxx>
+
+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
+ {
+#if defined(__COVERITY__)
+ try
+ {
+ delete p;
+ }
+ catch (const css::uno::Exception&)
+ {
+ SAL_WARN("vcl.app", "Fatal exception: " << exceptionToString(ex));
+ std::terminate();
+ }
+ catch (const std::exception& e)
+ {
+ SAL_WARN("vcl.app", "Fatal exception: " << e.what());
+ std::terminate();
+ }
+#else
+ delete p;
+#endif
+ }
+};
+
+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();
+ (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 000000000..a3c09d56b
--- /dev/null
+++ b/include/o3tl/enumarray.hxx
@@ -0,0 +1,140 @@
+/* -*- 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 <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);
+
+ 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; }
+
+//private:
+ V detail_values[max_index + 1];
+};
+
+
+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 000000000..60f069c9f
--- /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 000000000..dfaea5ce3
--- /dev/null
+++ b/include/o3tl/float_int_conversion.hxx
@@ -0,0 +1,57 @@
+/* -*- 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 <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>
+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>
+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);
+}
+
+// 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 000000000..9ca86924f
--- /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/lazy_update.hxx b/include/o3tl/lazy_update.hxx
new file mode 100644
index 000000000..951a9bb7b
--- /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 const & func): func_(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 000000000..e822fde02
--- /dev/null
+++ b/include/o3tl/lru_map.hxx
@@ -0,0 +1,185 @@
+/* -*- 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>
+
+namespace o3tl
+{
+
+/** 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.
+ *
+ **/
+template<typename Key, typename Value, class KeyHash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+class lru_map final
+{
+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;
+ const size_t mMaxSize;
+
+ void checkLRU()
+ {
+ if (mLruMap.size() > mMaxSize)
+ {
+ // remove from map
+ mLruMap.erase(mLruList.back().first);
+ // remove from list
+ mLruList.pop_back();
+ }
+ }
+
+public:
+ typedef list_iterator_t iterator;
+ typedef list_const_iterator_t const_iterator;
+
+ // a size of 0 effectively disables the LRU cleanup code
+ lru_map(size_t nMaxSize)
+ : mMaxSize(nMaxSize ? nMaxSize : std::min(mLruMap.max_size(), mLruList.max_size()))
+ {}
+ ~lru_map()
+ {
+ // 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 aLruListTemp;
+ aLruListTemp.swap(mLruList);
+ }
+
+ 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
+ {
+ // 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;
+ checkLRU();
+ }
+ else // already exists -> replace value
+ {
+ // replace value
+ i->second->second = rPair.second;
+ // bring to front of the lru list
+ mLruList.splice(mLruList.begin(), mLruList, i->second);
+ }
+ }
+
+ 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
+ {
+ // 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;
+ checkLRU();
+ }
+ else // already exists -> replace value
+ {
+ // replace value
+ i->second->second = std::move(rPair.second);
+ // push to back of the lru list
+ mLruList.splice(mLruList.begin(), mLruList, i->second);
+ }
+ }
+
+ 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))
+ {
+ 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();
+ }
+
+ void clear()
+ {
+ 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 000000000..d42783c30
--- /dev/null
+++ b/include/o3tl/make_shared.hxx
@@ -0,0 +1,33 @@
+/* -*- 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 <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[]>());
+}
+
+} // 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 000000000..9980319a6
--- /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 000000000..7f2015805
--- /dev/null
+++ b/include/o3tl/runtimetooustring.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_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/safeint.hxx b/include/o3tl/safeint.hxx
new file mode 100644
index 000000000..6d8d1304f
--- /dev/null
+++ b/include/o3tl/safeint.hxx
@@ -0,0 +1,241 @@
+/* -*- 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_SAFEINT_HXX
+#define INCLUDED_O3TL_SAFEINT_HXX
+
+#include <sal/config.h>
+
+#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
+typename std::enable_if<std::is_signed<T>::value, T>::type 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
+typename std::enable_if<std::is_unsigned<T>::value, T>::type saturating_add(
+ T a, T b)
+{
+ 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_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_unsigned<T>::value, T>::type saturating_sub(
+ T a, T b)
+{
+ if (a >= std::numeric_limits<T>::min() + b) {
+ return a - b;
+ } else {
+ return std::numeric_limits<T>::min();
+ }
+}
+
+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(__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;
+}
+
+}
+
+#endif
+
+/* 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 000000000..28ef75817
--- /dev/null
+++ b/include/o3tl/sorted_vector.hxx
@@ -0,0 +1,376 @@
+/* -*- 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 <functional>
+#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>::difference_type difference_type;
+ typedef typename std::vector<Value>::size_type size_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( size_t index )
+ {
+ m_vector.erase(m_vector.begin() + index);
+ }
+
+ // like C++ 2011: erase with const_iterator (doesn't change sort order)
+ void erase(const_iterator const& position)
+ { // C++98 has vector::erase(iterator), so call that
+ 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();
+ }
+
+ 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
+ {
+ for (const_iterator it = rOther.m_vector.begin(); it != rOther.m_vector.end(); ++it)
+ {
+ insert(*it);
+ }
+ }
+ }
+
+ /* 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:
+
+ 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);
+ 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);
+ 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);
+ 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/span.hxx b/include/o3tl/span.hxx
new file mode 100644
index 000000000..8af8ba846
--- /dev/null
+++ b/include/o3tl/span.hxx
@@ -0,0 +1,90 @@
+/* -*- 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_SPAN_HXX
+#define INCLUDED_O3TL_SPAN_HXX
+
+#include <sal/config.h>
+
+#include <config_global.h>
+
+#if HAVE_CPP_SPAN
+
+#include <span>
+
+namespace o3tl { using std::span; }
+
+#else
+
+#include <cassert>
+#include <cstddef>
+#include <iterator>
+
+namespace o3tl {
+
+/** A barebones approximation of C++20 <span>.
+*/
+template<typename T>
+class span {
+public:
+ using value_type = T;
+ using pointer = value_type *;
+ using const_pointer = value_type const *;
+ using reference = value_type &;
+ using const_reference = value_type const &;
+ using const_iterator = const_pointer;
+ using iterator = pointer;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+
+ constexpr span() noexcept : data_(nullptr), size_(0) {}
+
+ template<std::size_t N>
+ constexpr span (T (&a)[N]) noexcept : data_(a), size_(N) {}
+
+ constexpr span (T *a, size_type len) noexcept
+ : data_(a), size_(len)
+ {
+ // not terribly sure about this, might need to strengthen it
+ assert(a != nullptr || len == 0);
+ }
+
+ constexpr bool empty() const noexcept { return size_ == 0; }
+
+ constexpr iterator begin() const noexcept { return data_; }
+ constexpr iterator end() const noexcept { return begin() + size(); }
+
+ reverse_iterator rbegin() const noexcept
+ { return reverse_iterator(end()); }
+ reverse_iterator rend() const noexcept
+ { return reverse_iterator(begin()); }
+
+ constexpr size_type size() const noexcept { return size_; }
+
+ constexpr reference operator [](size_type pos) const {
+ assert(pos < size());
+ return data_[pos];
+ }
+
+ constexpr pointer data() const noexcept { return data_; }
+
+private:
+ pointer data_;
+ size_type size_;
+};
+
+} // namespace o3tl
+
+#endif
+
+#endif
+
+/* 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 000000000..006a36c89
--- /dev/null
+++ b/include/o3tl/strong_int.hxx
@@ -0,0 +1,145 @@
+/* -*- 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; }
+
+ 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 000000000..7b6c9a1f9
--- /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 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 000000000..bf795908b
--- /dev/null
+++ b/include/o3tl/typed_flags_set.hxx
@@ -0,0 +1,326 @@
+/* -*- 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>
+#include <sal/types.h>
+
+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::underlyingEnumValue(rhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::typed_flags<E>::mask
+ & ~o3tl::underlyingEnumValue(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::underlyingEnumValue<E>(rhs));
+}
+
+template<typename E> constexpr typename o3tl::typed_flags<E>::Wrap operator ^(
+ E lhs, E rhs)
+{
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::underlyingEnumValue(lhs)));
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::underlyingEnumValue(rhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::underlyingEnumValue(lhs)
+ ^ o3tl::underlyingEnumValue(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::underlyingEnumValue(lhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::underlyingEnumValue(lhs)
+ ^ o3tl::underlyingEnumValue<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::underlyingEnumValue(rhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::underlyingEnumValue<E>(lhs)
+ ^ o3tl::underlyingEnumValue(rhs));
+}
+
+template<typename W> constexpr
+typename o3tl::typed_flags<typename W::Unwrapped::Self>::Wrap operator ^(
+ W lhs, W rhs)
+{
+ return static_cast<W>(
+ o3tl::underlyingEnumValue<typename W::Unwrapped::Self>(lhs)
+ ^ o3tl::underlyingEnumValue<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::underlyingEnumValue(lhs)));
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::underlyingEnumValue(rhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::underlyingEnumValue(lhs)
+ & o3tl::underlyingEnumValue(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::underlyingEnumValue(lhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::underlyingEnumValue(lhs)
+ & o3tl::underlyingEnumValue<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::underlyingEnumValue(rhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::underlyingEnumValue<E>(lhs)
+ & o3tl::underlyingEnumValue(rhs));
+}
+
+template<typename W> constexpr
+typename o3tl::typed_flags<typename W::Unwrapped::Self>::Wrap operator &(
+ W lhs, W rhs)
+{
+ return static_cast<W>(
+ o3tl::underlyingEnumValue<typename W::Unwrapped::Self>(lhs)
+ & o3tl::underlyingEnumValue<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::underlyingEnumValue(lhs)));
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::underlyingEnumValue(rhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::underlyingEnumValue(lhs)
+ | o3tl::underlyingEnumValue(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::underlyingEnumValue(lhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::underlyingEnumValue(lhs)
+ | o3tl::underlyingEnumValue<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::underlyingEnumValue(rhs)));
+ return static_cast<typename o3tl::typed_flags<E>::Wrap>(
+ o3tl::underlyingEnumValue<E>(lhs)
+ | o3tl::underlyingEnumValue(rhs));
+}
+
+template<typename W> constexpr
+typename o3tl::typed_flags<typename W::Unwrapped::Self>::Wrap operator |(
+ W lhs, W rhs)
+{
+ return static_cast<W>(
+ o3tl::underlyingEnumValue<typename W::Unwrapped::Self>(lhs)
+ | o3tl::underlyingEnumValue<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::underlyingEnumValue(lhs)));
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::underlyingEnumValue(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::underlyingEnumValue(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::underlyingEnumValue(lhs)));
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::underlyingEnumValue(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::underlyingEnumValue(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::underlyingEnumValue(lhs)));
+ assert(
+ o3tl::detail::isNonNegative(
+ o3tl::underlyingEnumValue(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::underlyingEnumValue(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 000000000..1684b2da1
--- /dev/null
+++ b/include/o3tl/underlyingenumvalue.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_UNDERLYINGENUMVALUE_HXX
+#define INCLUDED_O3TL_UNDERLYINGENUMVALUE_HXX
+
+#include <sal/config.h>
+
+#include <type_traits>
+
+namespace o3tl
+{
+// 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> underlyingEnumValue(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/unreachable.hxx b/include/o3tl/unreachable.hxx
new file mode 100644
index 000000000..bc33d34b5
--- /dev/null
+++ b/include/o3tl/unreachable.hxx
@@ -0,0 +1,39 @@
+/* -*- 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>
+
+// A better replacement for assert(false) to indicate a place in the code that should not be
+// reachable. This should improve on the sometimes poor false-positive warnings emitted by
+// compilers when they cannot detect that some condition flagged by assert(false) cannot occur,
+// either because assert is reduced to a no-op by NDEBUG or because assert is not marked as noreturn
+// in the MSVC headers. This 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
+
+/* 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 000000000..b49994eff
--- /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 000000000..1b94f316f
--- /dev/null
+++ b/include/o3tl/vector_pool.hxx
@@ -0,0 +1,122 @@
+/* -*- 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 <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( const ValueType& val ) :
+ value(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 000000000..dc6d80a9f
--- /dev/null
+++ b/include/o3tl/vector_utils.hxx
@@ -0,0 +1,27 @@
+/* -*- 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 <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: */