From e255fd38426579695ce441a8b4ed8c4c82487138 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Mon, 22 Aug 2016 18:45:47 -0400 Subject: [PATCH] Bug 1262826 - specialize nsTHashtable> to share a common implementation; r=erahm We have a number of nsTHashtable> instantiations, mostly from IPDL-generated code. There's no reason in principle that all of these instantiations couldn't share code, since they're all storing POD entries of the same size. Let's specialize nsTHashtable for such types, providing a thin layer over a hashtable that stores void pointers. This change saves about 90K of space (!) on x86-64 Linux. --- xpcom/glue/moz.build | 1 + xpcom/glue/nsHashKeys.h | 32 +------- xpcom/glue/nsPointerHashKeys.h | 48 ++++++++++++ xpcom/glue/nsTHashtable.h | 139 +++++++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+), 31 deletions(-) create mode 100644 xpcom/glue/nsPointerHashKeys.h diff --git a/xpcom/glue/moz.build b/xpcom/glue/moz.build index be92935db3dd..95c18b273ed1 100644 --- a/xpcom/glue/moz.build +++ b/xpcom/glue/moz.build @@ -44,6 +44,7 @@ EXPORTS += [ 'nsIWeakReferenceUtils.h', 'nsJSThingHashtable.h', 'nsMemory.h', + 'nsPointerHashKeys.h', 'nsProxyRelease.h', 'nsQuickSort.h', 'nsRefPtrHashtable.h', diff --git a/xpcom/glue/nsHashKeys.h b/xpcom/glue/nsHashKeys.h index ed98410189f3..9c688691fcb4 100644 --- a/xpcom/glue/nsHashKeys.h +++ b/xpcom/glue/nsHashKeys.h @@ -18,6 +18,7 @@ #include "nsStringGlue.h" #include "nsCRTGlue.h" #include "nsUnicharUtils.h" +#include "nsPointerHashKeys.h" #include #include @@ -358,36 +359,6 @@ ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, CycleCollectionNoteChild(aCallback, aField.GetKey(), aName, aFlags); } -/** - * hashkey wrapper using T* KeyType - * - * @see nsTHashtable::EntryType for specification - */ -template -class nsPtrHashKey : public PLDHashEntryHdr -{ -public: - typedef T* KeyType; - typedef const T* KeyTypePointer; - - explicit nsPtrHashKey(const T* aKey) : mKey(const_cast(aKey)) {} - nsPtrHashKey(const nsPtrHashKey& aToCopy) : mKey(aToCopy.mKey) {} - ~nsPtrHashKey() {} - - KeyType GetKey() const { return mKey; } - bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; } - - static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; } - static PLDHashNumber HashKey(KeyTypePointer aKey) - { - return NS_PTR_TO_UINT32(aKey) >> 2; - } - enum { ALLOW_MEMMOVE = true }; - -protected: - T* MOZ_NON_OWNING_REF mKey; -}; - /** * hashkey wrapper using T* KeyType that sets key to nullptr upon * destruction. Relevant only in cases where a memory pointer-scanner @@ -408,7 +379,6 @@ public: ~nsClearingPtrHashKey() { nsPtrHashKey::mKey = nullptr; } }; -typedef nsPtrHashKey nsVoidPtrHashKey; typedef nsClearingPtrHashKey nsClearingVoidPtrHashKey; /** diff --git a/xpcom/glue/nsPointerHashKeys.h b/xpcom/glue/nsPointerHashKeys.h new file mode 100644 index 000000000000..a89843101cfa --- /dev/null +++ b/xpcom/glue/nsPointerHashKeys.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +/* Definitions for nsPtrHashKey and nsVoidPtrHashKey. */ + +#ifndef nsPointerHashKeys_h +#define nsPointerHashKeys_h + +#include "nscore.h" + +#include "mozilla/Attributes.h" + +/** + * hashkey wrapper using T* KeyType + * + * @see nsTHashtable::EntryType for specification + */ +template +class nsPtrHashKey : public PLDHashEntryHdr +{ +public: + typedef T* KeyType; + typedef const T* KeyTypePointer; + + explicit nsPtrHashKey(const T* aKey) : mKey(const_cast(aKey)) {} + nsPtrHashKey(const nsPtrHashKey& aToCopy) : mKey(aToCopy.mKey) {} + ~nsPtrHashKey() {} + + KeyType GetKey() const { return mKey; } + bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; } + + static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; } + static PLDHashNumber HashKey(KeyTypePointer aKey) + { + return NS_PTR_TO_UINT32(aKey) >> 2; + } + enum { ALLOW_MEMMOVE = true }; + +protected: + T* MOZ_NON_OWNING_REF mKey; +}; + +typedef nsPtrHashKey nsVoidPtrHashKey; + +#endif // nsPointerHashKeys_h diff --git a/xpcom/glue/nsTHashtable.h b/xpcom/glue/nsTHashtable.h index 10d6435f059c..705b0294e24e 100644 --- a/xpcom/glue/nsTHashtable.h +++ b/xpcom/glue/nsTHashtable.h @@ -8,6 +8,7 @@ #define nsTHashtable_h__ #include "PLDHashTable.h" +#include "nsPointerHashKeys.h" #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/fallible.h" @@ -435,4 +436,142 @@ ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, } } +/** + * For nsTHashtable with pointer entries, we can have a template specialization + * that layers a typed T* interface on top of a common implementation that + * works internally with void pointers. This arrangement saves code size and + * might slightly improve performance as well. + */ + +/** + * We need a separate entry type class for the inheritance structure of the + * nsTHashtable specialization below; nsVoidPtrHashKey is simply typedefed to a + * specialization of nsPtrHashKey, and the formulation: + * + * class nsTHashtable> : protected nsTHashtable + * + * is not going to turn out very well, since we'd wind up with an nsTHashtable + * instantiation that is its own base class. + */ +namespace detail { + +class VoidPtrHashKey : public nsPtrHashKey +{ + typedef nsPtrHashKey Base; + +public: + explicit VoidPtrHashKey(const void* aKey) : Base(aKey) {} +}; + +} // namespace detail + +/** + * See the main nsTHashtable documentation for descriptions of this class's + * methods. + */ +template +class nsTHashtable> : protected nsTHashtable<::detail::VoidPtrHashKey> +{ + typedef nsTHashtable<::detail::VoidPtrHashKey> Base; + typedef nsPtrHashKey EntryType; + + // We play games with reinterpret_cast'ing between these two classes, so + // try to ensure that playing said games is reasonable. + static_assert(sizeof(nsPtrHashKey) == sizeof(::detail::VoidPtrHashKey), + "hash keys must be the same size"); + + nsTHashtable(const nsTHashtable& aOther) = delete; + nsTHashtable& operator=(const nsTHashtable& aOther) = delete; + +public: + nsTHashtable() = default; + explicit nsTHashtable(uint32_t aInitLength) + : Base(aInitLength) + {} + + ~nsTHashtable() = default; + + nsTHashtable(nsTHashtable&&) = default; + + /* Wrapper functions */ + using Base::GetGeneration; + using Base::Count; + using Base::IsEmpty; + using Base::Clear; + + using Base::ShallowSizeOfExcludingThis; + using Base::ShallowSizeOfIncludingThis; + +#ifdef DEBUG + using Base::MarkImmutable; +#endif + + EntryType* GetEntry(T* aKey) const + { + return reinterpret_cast(Base::GetEntry(aKey)); + } + + bool Contains(T* aKey) const + { + return Base::Contains(aKey); + } + + EntryType* PutEntry(T* aKey) + { + return reinterpret_cast(Base::PutEntry(aKey)); + } + + MOZ_MUST_USE + EntryType* PutEntry(T* aKey, const mozilla::fallible_t&) + { + return reinterpret_cast( + Base::PutEntry(aKey, mozilla::fallible)); + } + + void RemoveEntry(T* aKey) + { + Base::RemoveEntry(aKey); + } + + void RemoveEntry(EntryType* aEntry) + { + Base::RemoveEntry(reinterpret_cast<::detail::VoidPtrHashKey*>(aEntry)); + } + + void RawRemoveEntry(EntryType* aEntry) + { + Base::RawRemoveEntry(reinterpret_cast<::detail::VoidPtrHashKey*>(aEntry)); + } + + class Iterator : public Base::Iterator + { + public: + typedef nsTHashtable::Base::Iterator Base; + + explicit Iterator(nsTHashtable* aTable) : Base(aTable) {} + Iterator(Iterator&& aOther) : Base(mozilla::Move(aOther)) {} + ~Iterator() = default; + + EntryType* Get() const { return reinterpret_cast(Base::Get()); } + + private: + Iterator() = delete; + Iterator(const Iterator&) = delete; + Iterator& operator=(const Iterator&) = delete; + Iterator& operator=(Iterator&&) = delete; + }; + + Iterator Iter() { return Iterator(this); } + + Iterator ConstIter() const + { + return Iterator(const_cast(this)); + } + + void SwapElements(nsTHashtable& aOther) + { + Base::SwapElements(aOther); + } +}; + #endif // nsTHashtable_h__