diff options
Diffstat (limited to 'include/o3tl')
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: */ |