@@ -40,41 +40,33 @@ template<unsigned BASE_CAPACITY, unsigned POOL_SIZE> struct TableRepMap {
4040 " BASE_CAPACITY must be a power of 2" );
4141
4242 TableRepMap ()
43- : entries(static_cast <ustring::TableRep**>(
44- calloc (BASE_CAPACITY, sizeof (ustring::TableRep*))))
45- , memory_usage(sizeof (*this )
46- + sizeof(ustring::TableRep*) * BASE_CAPACITY)
43+ : memory_usage(sizeof (*this ))
4744 {
45+ resize_entries (BASE_CAPACITY);
4846 allocate_pool_block ();
4947 }
5048
5149 ~TableRepMap ()
5250 { /* just let memory leak */
5351 }
5452
55- void clear ()
53+ // Free all allocated resources. Note that this leaves the TableRepMap
54+ // in an unusable state.
55+ void free_resources ()
5656 {
5757 ustring_write_lock_t lock (mutex);
58- // Destroy all TableRep objects. The destructor safely handles the
59- // case where the internal std::string aliases the pool chars.
60- for (size_t i = 0 ; i <= mask; ++i) {
61- if (entries[i])
62- entries[i]->~TableRep ();
63- }
64- // Free all pool allocations and large individual allocations.
58+ // Destroy TableRep objects FIRST, while the pool/large-alloc memory
59+ // they live in is still valid. Their destructors may read/write the
60+ // surrounding allocation and (on some platforms) the std::string
61+ // subobject owns a separate heap buffer that only gets freed here.
62+ destroy_entries ();
63+ entries.clear ();
64+ entries.shrink_to_fit ();
65+ // Now safe to free the backing buffers.
6566 all_pools.clear ();
6667 all_pools.shrink_to_fit ();
6768 large_allocs.clear ();
6869 large_allocs.shrink_to_fit ();
69- free (entries);
70- // Re-initialize to a fresh, usable state.
71- mask = BASE_CAPACITY - 1 ;
72- entries = static_cast <ustring::TableRep**>(
73- calloc (BASE_CAPACITY, sizeof (ustring::TableRep*)));
74- num_entries = 0 ;
75- memory_usage = sizeof (*this )
76- + sizeof (ustring::TableRep*) * BASE_CAPACITY;
77- allocate_pool_block ();
7870 }
7971
8072 size_t get_memory_usage ()
@@ -174,31 +166,35 @@ template<unsigned BASE_CAPACITY, unsigned POOL_SIZE> struct TableRepMap {
174166private:
175167 void grow ()
176168 {
177- size_t new_mask = mask * 2 + 1 ;
169+ // Temporarily hold the old entries while we are copying them
170+ std::vector<ustring::TableRep*> old_entries;
171+ old_entries.swap (entries);
172+ size_t old_num_entries = num_entries;
173+ size_t to_copy = old_num_entries;
178174
179- // NOTE: only increment by half because we are doubling the entries and freeing the old
180- memory_usage += (mask + 1 ) * sizeof (ustring::TableRep* );
175+ // Make bigger space for new entries table and new mask
176+ resize_entries ( 2 * old_entries. size () );
181177
182- ustring::TableRep** new_entries = static_cast <ustring::TableRep**>(
183- calloc (new_mask + 1 , sizeof (ustring::TableRep*)));
184- size_t to_copy = num_entries;
178+ // Copy each entry from old into the new, recomputing the hash because
179+ // the mask has changd.
185180 for (size_t i = 0 ; to_copy != 0 ; i++) {
186- if (entries [i] == 0 )
181+ if (old_entries [i] == nullptr )
187182 continue ;
188- size_t pos = entries[i]->hashed & new_mask, dist = 0 ;
183+ // i is old position, pos will be new position
184+ size_t pos = old_entries[i]->hashed & mask, dist = 0 ;
189185 for (;;) {
190- if (new_entries [pos] == 0 )
186+ if (entries [pos] == 0 )
191187 break ;
192188 ++dist;
193- pos = (pos + dist) & new_mask ; // quadratic probing
189+ pos = (pos + dist) & mask ; // quadratic probing
194190 }
195- new_entries[pos] = entries[i];
191+ entries[pos] = old_entries[i];
192+ old_entries[i] = nullptr ;
196193 to_copy--;
197194 }
198195
199- free (entries);
200- entries = new_entries;
201- mask = new_mask;
196+ // old_entries will free when we exit this function
197+ memory_usage -= sizeof (ustring::TableRep*) * old_entries.size ();
202198 }
203199
204200 ustring::TableRep* make_rep (string_view str, uint64_t hash)
@@ -239,18 +235,41 @@ template<unsigned BASE_CAPACITY, unsigned POOL_SIZE> struct TableRepMap {
239235 all_pools.emplace_back (pool);
240236 }
241237
242- OIIO_CACHE_ALIGN mutable ustring_mutex_t mutex;
243- size_t mask = BASE_CAPACITY - 1 ;
244- ustring::TableRep** entries = nullptr ;
245- size_t num_entries = 0 ;
246- char * pool = nullptr ; // Current pool block we're using
247- size_t pool_offset = 0 ; // Next offset within current block
248- size_t memory_usage = 0 ; // Total memory usage
238+ void destroy_entries ()
239+ {
240+ // Destroy all TableRep objects. The destructor safely handles the
241+ // case where the internal std::string aliases the pool chars.
242+ for (auto & e : entries) {
243+ if (e) {
244+ e->~TableRep ();
245+ e = nullptr ;
246+ }
247+ }
248+ }
249+
250+ void resize_entries (size_t newsize)
251+ {
252+ OIIO_CONTRACT_ASSERT (entries.empty ());
253+ OIIO_CONTRACT_ASSERT_MSG ((newsize & (newsize - 1 )) == 0 ,
254+ " New entries size must be power of 2" );
255+ entries.resize (newsize, nullptr );
256+ memory_usage += sizeof (ustring::TableRep*) * entries.size ();
257+ num_entries = 0 ;
258+ mask = newsize - 1 ;
259+ }
260+
261+ std::vector<ustring::TableRep*> entries;
262+ size_t mask = BASE_CAPACITY - 1 ;
263+ size_t num_entries = 0 ;
264+ char * pool = nullptr ; // Current pool block we're using
265+ size_t pool_offset = 0 ; // Next offset within current block
266+ size_t memory_usage = 0 ; // Total memory usage
249267 std::vector<std::unique_ptr<char []>> all_pools;
250268 std::vector<std::unique_ptr<char []>> large_allocs;
251269#ifdef USTRING_TRACK_NUM_LOOKUPS
252270 size_t num_lookups = 0 ;
253271#endif
272+ OIIO_CACHE_ALIGN mutable ustring_mutex_t mutex;
254273};
255274
256275#if 0
@@ -290,10 +309,10 @@ struct UstringTable {
290309 return num;
291310 }
292311
293- void clear ()
312+ void free_resources ()
294313 {
295314 for (auto & bin : bins)
296- bin.clear ();
315+ bin.free_resources ();
297316 }
298317
299318# ifdef USTRING_TRACK_NUM_LOOKUPS
@@ -749,7 +768,7 @@ static int ustring_cleanup_atexit_registered = []() {
749768 OIIO::print (" ustring: freeing table resources ({} bytes)\n " ,
750769 v3_1::ustring_table ().get_memory_usage ());
751770#endif
752- v3_1::ustring_table ().clear ();
771+ v3_1::ustring_table ().free_resources ();
753772 v3_1::reverse_map ().clear ();
754773 }
755774 });
0 commit comments