Files
test2/intern/memutil/MEM_alloc_string_storage.hh
2024-07-30 12:23:09 +10:00

119 lines
3.4 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup intern_memutil
*
* Implement a static storage for complex, non-static allocation strings passed MEM_guardedalloc
* functions.
*/
#include <any>
#include <cassert>
#include <string>
#include <unordered_map>
namespace intern::memutil {
/**
* A 'static' storage of allocation strings, with a simple API to set and retrieve them.
*
* This is a templated wrapper around a std::unordered_map, to allow custom key types.
*/
template<typename keyT, template<typename> typename hashT> class AllocStringStorage {
std::unordered_map<keyT, std::string, hashT<keyT>> storage_;
public:
/**
* Check whether the given key exists in the storage.
*
* \return `true` if the \a key is found in storage, false otherwise.
*/
bool contains(const keyT &key)
{
return storage_.count(key) != 0;
}
/**
* Return the alloc string for the given key in the storage.
*
* \return A pointer to the stored string if \a key is found, `nullptr` otherwise.
*/
const char *find(const keyT &key)
{
if (storage_.count(key) != 0) {
return storage_[key].c_str();
}
return nullptr;
}
/**
* Insert the given alloc string in the storage, at the given key, and return a pointer
* to the stored string.
*
* \param alloc_string: The alloc string to store at \a key.
* \return A pointer to the inserted stored string.
*/
const char *insert(const keyT &key, std::string alloc_string)
{
#ifndef NDEBUG
assert(storage_.count(key) == 0);
#endif
return (storage_[key] = std::move(alloc_string)).c_str();
}
};
namespace internal {
/**
* The main container for all #AllocStringStorage.
*/
class AllocStringStorageContainer {
std::unordered_map<std::string, std::any> storage_;
public:
/**
* Create if necessary, and return the #AllocStringStorage for the given \a storage_identifier.
*
* The template arguments allow to define the type of key used for the mapping to allocation
* strings.
*/
template<typename keyT, template<typename> typename hashT>
std::any &ensure_storage(const std::string &storage_identifier)
{
if (storage_.count(storage_identifier) == 0) {
AllocStringStorage<keyT, hashT> storage_for_identifier;
return (storage_[storage_identifier] = std::make_any<AllocStringStorage<keyT, hashT>>(
std::move(storage_for_identifier)));
}
return storage_[storage_identifier];
}
};
/**
* Ensure that the static AllocStringStorageContainer is defined and created, and return a
* reference to it.
*/
AllocStringStorageContainer &ensure_storage_container();
} // namespace internal
/**
* Return a reference to the AllocStringStorage static data matching the given \a
* storage_identifier, creating it if needed.
*
* \note The storage is `thread_local` data, so access to it is thread-safe as long as it is not
* shared between threads by the user code.
*/
template<typename keyT, template<typename> typename hashT>
AllocStringStorage<keyT, hashT> &alloc_string_storage_get(const std::string &storage_identifier)
{
internal::AllocStringStorageContainer &storage_container = internal::ensure_storage_container();
std::any &storage = storage_container.ensure_storage<keyT, hashT>(storage_identifier);
return std::any_cast<AllocStringStorage<keyT, hashT> &>(storage);
}
} // namespace intern::memutil