@@ -601,7 +601,7 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
601601 // / rounds up to the next allocation quantum by calling `malloc_good_size`.
602602 // / Otherwise, just return the passed-in size, which is always valid even if
603603 // / not necessarily optimal.
604- size_t goodSize (size_t size) {
604+ static size_t goodSize (size_t size) {
605605#if defined(__APPLE__) && defined(__MACH__)
606606 return malloc_good_size (size);
607607#else
@@ -693,6 +693,29 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
693693 }
694694 };
695695
696+ // / The struct used for element storage. The `Elem` member is considered to be
697+ // / the first element of a variable-length array, whose size is determined by
698+ // / the allocation.
699+ struct ElementStorage {
700+ uint32_t Capacity;
701+ ElemTy Elem;
702+
703+ static ElementStorage *allocate (size_t capacity) {
704+ auto headerSize = offsetof (ElementStorage, Elem);
705+ auto size = goodSize (headerSize + capacity * sizeof (ElemTy));
706+
707+ auto *ptr = reinterpret_cast <ElementStorage *>(malloc (size));
708+ if (!ptr)
709+ swift::crash (" Could not allocate memory." );
710+
711+ ptr->Capacity = (size - headerSize) / sizeof (ElemTy);
712+
713+ return ptr;
714+ }
715+
716+ ElemTy *data () { return &Elem; }
717+ };
718+
696719 // / A simple linked list representing pointers that need to be freed.
697720 struct FreeListNode {
698721 FreeListNode *Next;
@@ -723,17 +746,14 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
723746 std::atomic<uint32_t > ElementCount{0 };
724747
725748 // / The array of elements.
726- std::atomic<ElemTy *> Elements{nullptr };
749+ std::atomic<ElementStorage *> Elements{nullptr };
727750
728751 // / The array of indices.
729752 std::atomic<IndexStorage *> Indices{nullptr };
730753
731754 // / The writer lock, which must be taken before any mutation of the table.
732755 StaticMutex WriterLock;
733756
734- // / The maximum number of elements that the current elements array can hold.
735- uint32_t ElementCapacity{0 };
736-
737757 // / The list of pointers to be freed once no readers are active.
738758 FreeListNode *FreeList{nullptr };
739759
@@ -754,22 +774,18 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
754774
755775 // / Grow the elements array, adding the old array to the free list and
756776 // / returning the new array with all existing elements copied into it.
757- ElemTy *resize (ElemTy *elements, size_t elementCount) {
777+ ElementStorage *resize (ElementStorage *elements, size_t elementCount) {
758778 // Grow capacity by 25%, making sure we grow by at least 1.
759779 size_t newCapacity =
760780 std::max (elementCount + (elementCount >> 2 ), elementCount + 1 );
761- size_t newSize = newCapacity * sizeof (ElemTy);
762-
763- newSize = goodSize (newSize);
764- newCapacity = newSize / sizeof (ElemTy);
781+ auto *newElements = ElementStorage::allocate (newCapacity);
765782
766- ElemTy *newElements = static_cast <ElemTy *>(malloc (newSize));
767783 if (elements) {
768- memcpy (newElements, elements, elementCount * sizeof (ElemTy));
784+ memcpy (newElements->data (), elements->data (),
785+ elementCount * sizeof (ElemTy));
769786 FreeListNode::add (&FreeList, elements);
770787 }
771788
772- ElementCapacity = newCapacity;
773789 Elements.store (newElements, std::memory_order_release);
774790 return newElements;
775791 }
@@ -919,16 +935,17 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
919935 // should not happen often.
920936 IndexStorage *indices;
921937 size_t elementCount;
922- ElemTy *elements;
923- ElemTy *elements2;
938+ ElementStorage *elements;
939+ ElementStorage *elements2;
924940 do {
925941 elements = Elements.load (std::memory_order_acquire);
926942 indices = Indices.load (std::memory_order_acquire);
927943 elementCount = ElementCount.load (std::memory_order_acquire);
928944 elements2 = Elements.load (std::memory_order_acquire);
929945 } while (elements != elements2);
930946
931- return Snapshot (this , indices, elements, elementCount);
947+ ElemTy *elementsPtr = elements ? elements->data () : nullptr ;
948+ return Snapshot (this , indices, elementsPtr, elementCount);
932949 }
933950
934951 // / Get an element by key, or insert a new element for that key if one is not
@@ -958,8 +975,9 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
958975 auto indicesCapacityLog2 = indices->CapacityLog2 ;
959976 auto elementCount = ElementCount.load (std::memory_order_relaxed);
960977 auto *elements = Elements.load (std::memory_order_relaxed);
978+ auto *elementsPtr = elements ? elements->data () : nullptr ;
961979
962- auto found = this ->find (key, indices, elementCount, elements );
980+ auto found = this ->find (key, indices, elementCount, elementsPtr );
963981 if (found.first ) {
964982 call (found.first , false );
965983 deallocateFreeListIfSafe ();
@@ -973,15 +991,15 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
973991 auto emptyCount = indicesCapacity - (elementCount + 1 );
974992 auto proportion = indicesCapacity / emptyCount;
975993 if (proportion >= ResizeProportion) {
976- indices = resize (indices, indicesCapacityLog2, elements );
977- found = find (key, indices, elementCount, elements );
994+ indices = resize (indices, indicesCapacityLog2, elementsPtr );
995+ found = find (key, indices, elementCount, elementsPtr );
978996 assert (!found.first && " Shouldn't suddenly find the key after rehashing" );
979997 }
980998
981- if (elementCount >= ElementCapacity ) {
999+ if (!elements || elementCount >= elements-> Capacity ) {
9821000 elements = resize (elements, elementCount);
9831001 }
984- auto *element = &elements[elementCount];
1002+ auto *element = &elements-> data () [elementCount];
9851003
9861004 // Order matters: fill out the element, then update the count,
9871005 // then update the index.
@@ -1010,7 +1028,6 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
10101028 Indices.store (nullptr , std::memory_order_relaxed);
10111029 ElementCount.store (0 , std::memory_order_relaxed);
10121030 Elements.store (nullptr , std::memory_order_relaxed);
1013- ElementCapacity = 0 ;
10141031
10151032 FreeListNode::add (&FreeList, indices);
10161033 FreeListNode::add (&FreeList, elements);
0 commit comments