diff options
author | Luboš Luňák <l.lunak@collabora.com> | 2022-01-07 12:43:48 +0100 |
---|---|---|
committer | Luboš Luňák <l.lunak@collabora.com> | 2022-01-09 15:43:53 +0100 |
commit | dd6ff1fd7fea0cfae75e5f65afed9d5f871d1313 (patch) | |
tree | 929b067780680b173ce7592c0042b493ef66fb83 | |
parent | 9ba45189c76373d0464cc06902270914888162a3 (diff) |
implement anyToHash() and use it for SdrCustomShapeGeometryItem
Using anyLess() still has quite some cost with bsc#1183308, this
makes the cost almost unnoticeable.
Since some values of Any are not handled, return empty std::optional
for those cases.
Change-Id: Ib45a81441e8bb456c4749f9bc53a981f09bbb1a5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/128109
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
-rw-r--r-- | comphelper/Library_comphelper.mk | 1 | ||||
-rw-r--r-- | comphelper/source/misc/anytohash.cxx | 210 | ||||
-rw-r--r-- | include/comphelper/anytohash.hxx | 48 | ||||
-rw-r--r-- | include/svx/sdasitm.hxx | 8 | ||||
-rw-r--r-- | solenv/clang-format/excludelist | 1 | ||||
-rw-r--r-- | svx/source/items/customshapeitem.cxx | 51 |
6 files changed, 308 insertions, 11 deletions
diff --git a/comphelper/Library_comphelper.mk b/comphelper/Library_comphelper.mk index 9da69fca91a8..dd2260c111f9 100644 --- a/comphelper/Library_comphelper.mk +++ b/comphelper/Library_comphelper.mk @@ -86,6 +86,7 @@ $(eval $(call gb_Library_add_exception_objects,comphelper,\ comphelper/source/misc/accimplaccess \ comphelper/source/misc/AccessibleImplementationHelper \ comphelper/source/misc/anycompare \ + comphelper/source/misc/anytohash \ comphelper/source/misc/anytostring \ comphelper/source/misc/asyncnotification \ comphelper/source/misc/asyncquithandler \ diff --git a/comphelper/source/misc/anytohash.cxx b/comphelper/source/misc/anytohash.cxx new file mode 100644 index 000000000000..4e97ea124d41 --- /dev/null +++ b/comphelper/source/misc/anytohash.cxx @@ -0,0 +1,210 @@ +/* -*- 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 . + */ + + +#include <comphelper/anytohash.hxx> + +#include <o3tl/hash_combine.hxx> +#include <typelib/typedescription.hxx> + +#include <com/sun/star/uno/Sequence.hxx> + +#include "typedescriptionref.hxx" + +using namespace ::com::sun::star; +using ::com::sun::star::uno::TypeDescription; +using ::comphelper::detail::TypeDescriptionRef; + +namespace comphelper { +namespace { + +std::optional<size_t> hashValue( size_t hash, + void const * val, typelib_TypeDescriptionReference * typeRef ) +{ + o3tl::hash_combine( hash, typeRef->eTypeClass ); + if (typeRef->eTypeClass == typelib_TypeClass_VOID) { + return hash; + } + assert(val != nullptr); + + switch (typeRef->eTypeClass) { + case typelib_TypeClass_INTERFACE: { + return std::nullopt; // not implemented + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: { + TypeDescription typeDescr( typeRef ); + if (!typeDescr.is()) + typeDescr.makeComplete(); + if (!typeDescr.is()) + return std::nullopt; + + typelib_CompoundTypeDescription * compType = + reinterpret_cast< typelib_CompoundTypeDescription * >( + typeDescr.get() ); + sal_Int32 nDescr = compType->nMembers; + + if (compType->pBaseTypeDescription) { + std::optional<size_t> tmpHash = hashValue( + hash, val, reinterpret_cast< + typelib_TypeDescription * >( + compType->pBaseTypeDescription)->pWeakRef); + if(!tmpHash.has_value()) + return std::nullopt; + hash = *tmpHash; + } + + typelib_TypeDescriptionReference ** ppTypeRefs = + compType->ppTypeRefs; + sal_Int32 * memberOffsets = compType->pMemberOffsets; + + for ( sal_Int32 nPos = 0; nPos < nDescr; ++nPos ) + { + TypeDescriptionRef memberType( ppTypeRefs[ nPos ] ); + if (!memberType.is()) + return std::nullopt; + + std::optional<size_t> tmpHash = hashValue( hash, + static_cast< char const * >( + val ) + memberOffsets[ nPos ], + memberType->pWeakRef ); + if(!tmpHash.has_value()) + return std::nullopt; + hash = *tmpHash; + } + break; + } + case typelib_TypeClass_SEQUENCE: { + TypeDescriptionRef typeDescr( typeRef ); + if (!typeDescr.is()) + return std::nullopt; + + typelib_TypeDescriptionReference * elementTypeRef = + reinterpret_cast< + typelib_IndirectTypeDescription * >(typeDescr.get())->pType; + TypeDescriptionRef elementTypeDescr( elementTypeRef ); + if (!elementTypeDescr.is()) + return std::nullopt; + + sal_Int32 nElementSize = elementTypeDescr->nSize; + uno_Sequence * seq = + *static_cast< uno_Sequence * const * >(val); + sal_Int32 nElements = seq->nElements; + + if (nElements > 0) + { + char const * pElements = seq->elements; + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + std::optional<size_t> tmpHash = hashValue( hash, + pElements + (nElementSize * nPos), + elementTypeDescr->pWeakRef ); + if(!tmpHash.has_value()) + return std::nullopt; + hash = *tmpHash; + } + } + break; + } + case typelib_TypeClass_ANY: { + uno_Any const * pAny = static_cast< uno_Any const * >(val); + return hashValue( hash, pAny->pData, pAny->pType ); + } + case typelib_TypeClass_TYPE: { + OUString const & str = OUString::unacquired( + &(*static_cast< + typelib_TypeDescriptionReference * const * >(val) + )->pTypeName ); + o3tl::hash_combine( hash, str.hashCode() ); + break; + } + case typelib_TypeClass_STRING: { + OUString const & str = OUString::unacquired( + static_cast< rtl_uString * const * >(val) ); + o3tl::hash_combine( hash, str.hashCode() ); + break; + } + case typelib_TypeClass_ENUM: { + TypeDescription typeDescr( typeRef ); + if (!typeDescr.is()) + typeDescr.makeComplete(); + if (!typeDescr.is()) + return std::nullopt; + + o3tl::hash_combine( hash, *static_cast< int const * >(val)); + break; + } + case typelib_TypeClass_BOOLEAN: + if (*static_cast< sal_Bool const * >(val)) + o3tl::hash_combine( hash, true ); + else + o3tl::hash_combine( hash, false ); + break; + case typelib_TypeClass_CHAR: { + o3tl::hash_combine( hash, *static_cast< sal_Unicode const * >(val)); + break; + } + case typelib_TypeClass_FLOAT: + o3tl::hash_combine( hash, *static_cast< float const * >(val) ); + break; + case typelib_TypeClass_DOUBLE: + o3tl::hash_combine( hash, *static_cast< double const * >(val) ); + break; + case typelib_TypeClass_BYTE: + o3tl::hash_combine( hash, *static_cast< sal_Int8 const * >(val) ); + break; + case typelib_TypeClass_SHORT: + o3tl::hash_combine( hash, *static_cast< sal_Int16 const * >(val) ); + break; + case typelib_TypeClass_UNSIGNED_SHORT: + o3tl::hash_combine( hash, *static_cast< sal_uInt16 const * >(val) ); + break; + case typelib_TypeClass_LONG: + o3tl::hash_combine( hash, *static_cast< sal_Int32 const * >(val) ); + break; + case typelib_TypeClass_UNSIGNED_LONG: + o3tl::hash_combine( hash, *static_cast< sal_uInt32 const * >(val) ); + break; + case typelib_TypeClass_HYPER: + o3tl::hash_combine( hash, *static_cast< sal_Int64 const * >(val) ); + break; + case typelib_TypeClass_UNSIGNED_HYPER: + o3tl::hash_combine( hash, *static_cast< sal_uInt64 const * >(val) ); + break; +// case typelib_TypeClass_UNKNOWN: +// case typelib_TypeClass_SERVICE: +// case typelib_TypeClass_MODULE: + default: + return std::nullopt; + } + return hash; +} + +} // anon namespace + + +std::optional<size_t> anyToHash( uno::Any const & value ) +{ + size_t hash = 0; + return hashValue( hash, value.getValue(), value.getValueTypeRef()); +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/comphelper/anytohash.hxx b/include/comphelper/anytohash.hxx new file mode 100644 index 000000000000..0aa14f47e8de --- /dev/null +++ b/include/comphelper/anytohash.hxx @@ -0,0 +1,48 @@ +/* -*- 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_COMPHELPER_ANYTOHASH_HXX +#define INCLUDED_COMPHELPER_ANYTOHASH_HXX + +#include <comphelper/comphelperdllapi.h> + +#include <optional> + +namespace com::sun::star::uno +{ +class Any; +} + +namespace comphelper +{ +/** Tries to get a hash value for an ANY value. + + Not all cases may be implemented, in which case no value is returned. + + @param value + ANY value + @return + hash of given ANY value, or not available +*/ +COMPHELPER_DLLPUBLIC std::optional<size_t> anyToHash(css::uno::Any const& value); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/svx/sdasitm.hxx b/include/svx/sdasitm.hxx index ebd022980e6e..f5c381167396 100644 --- a/include/svx/sdasitm.hxx +++ b/include/svx/sdasitm.hxx @@ -48,7 +48,15 @@ private: css::uno::Sequence< css::beans::PropertyValue > aPropSeq; + // For fast comparisons keep a hash of the content, computed on demand + // (unusable state is if anyToHash() returns no hash). + enum HashState { Unknown, Valid, Unusable }; + mutable HashState aHashState = HashState::Unknown; + mutable size_t aHash; + void SetPropSeq( const css::uno::Sequence< css::beans::PropertyValue >& rPropSeq ); + inline void UpdateHash() const; + inline void InvalidateHash(); public: diff --git a/solenv/clang-format/excludelist b/solenv/clang-format/excludelist index b8bebd05da48..8ead6910664f 100644 --- a/solenv/clang-format/excludelist +++ b/solenv/clang-format/excludelist @@ -1380,6 +1380,7 @@ comphelper/source/misc/accessibletexthelper.cxx comphelper/source/misc/accessiblewrapper.cxx comphelper/source/misc/accimplaccess.cxx comphelper/source/misc/anycompare.cxx +comphelper/source/misc/anytohash.cxx comphelper/source/misc/anytostring.cxx comphelper/source/misc/asyncnotification.cxx comphelper/source/misc/backupfilehelper.cxx diff --git a/svx/source/items/customshapeitem.cxx b/svx/source/items/customshapeitem.cxx index 0bba5ca6eb33..3ba698a3c158 100644 --- a/svx/source/items/customshapeitem.cxx +++ b/svx/source/items/customshapeitem.cxx @@ -21,6 +21,7 @@ #include <o3tl/any.hxx> #include <comphelper/anycompare.hxx> +#include <comphelper/anytohash.hxx> #include <svx/sdasitm.hxx> #include <com/sun/star/beans/PropertyValue.hpp> @@ -128,6 +129,7 @@ void SdrCustomShapeGeometryItem::SetPropertyValue( const css::beans::PropertyVal aPropHashMap[ rPropVal.Name ] = nIndex; } + InvalidateHash(); } void SdrCustomShapeGeometryItem::SetPropertyValue( const OUString& rSequenceName, const css::beans::PropertyValue& rPropVal ) @@ -176,6 +178,7 @@ void SdrCustomShapeGeometryItem::SetPropertyValue( const OUString& rSequenceName } } } + InvalidateHash(); } void SdrCustomShapeGeometryItem::ClearPropertyValue( const OUString& rPropName ) @@ -212,6 +215,7 @@ void SdrCustomShapeGeometryItem::ClearPropertyValue( const OUString& rPropName ) aPropSeq.realloc( nLength - 1 ); } aPropHashMap.erase( aHashIter ); // removing property from hashmap + InvalidateHash(); } SdrCustomShapeGeometryItem::~SdrCustomShapeGeometryItem() @@ -224,13 +228,15 @@ bool SdrCustomShapeGeometryItem::operator==( const SfxPoolItem& rCmp ) const return false; const SdrCustomShapeGeometryItem& other = static_cast<const SdrCustomShapeGeometryItem&>(rCmp); // This is called often by SfxItemPool, and comparing uno sequences is relatively slow. - // Optimize by checking the list of properties that this class keeps for the sequence, - // if the sizes are different, the sequences are different too, which should allow a cheap - // return for many cases. - if( aPropHashMap.size() != other.aPropHashMap.size()) + // So keep a hash of the sequence and if either of the sequences has a usable hash, + // compare using that. + UpdateHash(); + other.UpdateHash(); + if( aHashState != other.aHashState ) return false; - if( aPropPairHashMap.size() != other.aPropPairHashMap.size()) + if( aHashState == HashState::Valid && aHash != other.aHash ) return false; + return aPropSeq == other.aPropSeq; } @@ -238,16 +244,38 @@ bool SdrCustomShapeGeometryItem::operator<( const SfxPoolItem& rCmp ) const { assert(dynamic_cast<const SdrCustomShapeGeometryItem*>( &rCmp )); const SdrCustomShapeGeometryItem& other = static_cast<const SdrCustomShapeGeometryItem&>(rCmp); - // Again, optimize by checking the list of properties and compare by their count if different - // (this is operator< for sorting purposes, so the ordering can be somewhat arbitrary). - if( aPropHashMap.size() != other.aPropHashMap.size()) - return aPropHashMap.size() < other.aPropHashMap.size(); - if( aPropPairHashMap.size() != other.aPropPairHashMap.size()) - return aPropPairHashMap.size() < other.aPropPairHashMap.size(); + // Again, try to optimize by checking hashes first (this is operator< for sorting purposes, + // so the ordering can be somewhat arbitrary). + UpdateHash(); + other.UpdateHash(); + if( aHashState != other.aHashState ) + return aHashState < other.aHashState; + if( aHashState == HashState::Valid ) + return aHash < other.aHash; + return comphelper::anyLess( css::uno::makeAny( aPropSeq ), css::uno::makeAny( other.aPropSeq )); } +void SdrCustomShapeGeometryItem::UpdateHash() const +{ + if( aHashState != HashState::Unknown ) + return; + std::optional< size_t > hash = comphelper::anyToHash( css::uno::makeAny( aPropSeq )); + if( hash.has_value()) + { + aHash = *hash; + aHashState = HashState::Valid; + } + else + aHashState = HashState::Unusable; +} + +void SdrCustomShapeGeometryItem::InvalidateHash() +{ + aHashState = HashState::Unknown; +} + bool SdrCustomShapeGeometryItem::GetPresentation( SfxItemPresentation ePresentation, MapUnit /*eCoreMetric*/, MapUnit /*ePresentationMetric*/, OUString &rText, const IntlWrapper&) const @@ -313,6 +341,7 @@ void SdrCustomShapeGeometryItem::SetPropSeq( const css::uno::Sequence< css::bean } } } + InvalidateHash(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |