31#include <fmt/format.h>
75 if (this->mask.
size() <= number) {
76 this->mask.
resize(number + 1);
78 if (this->mask[number]) {
79 if (!this->numbers_multi_usages.has_value()) {
80 this->numbers_multi_usages.emplace();
82 int &multi_usages_num = this->numbers_multi_usages->lookup_or_add(number, 1);
87 this->mask[number].set(
true);
91 if (this->max_value_in_use) {
95 this->max_value_in_use = number;
105 "Trying to unregister a number suffix higher than current size of the bit "
106 "vector, should never happen.");
108 if (this->numbers_multi_usages.has_value() && this->numbers_multi_usages->contains(number)) {
109 int &multi_usages_num = this->numbers_multi_usages->lookup(number);
112 if (multi_usages_num == 1) {
113 this->numbers_multi_usages->remove_contained(number);
120 this->mask[number].set(
false);
124 this->max_value_in_use.value()--;
127 this->max_value_in_use.reset();
140 if (this->mask.
size() < 2) {
148 std::optional<int64_t>
result = find_first_0_index(search_mask);
155 return int(this->mask.
size());
157 if (this->max_value_in_use) {
158 if (this->max_value_in_use.value() + 1 <=
MAX_NUMBER) {
159 return this->max_value_in_use.value() + 1;
195 type_map.full_names.clear();
196 type_map.base_name_to_num_suffix.clear();
200 if ((
id == ignore_id) || (!this->is_global && (id->
lib != library))) {
206 if (this->is_global) {
224 "The key (name) already exists in the namemap, should only happen when "
225 "`do_global` is true.");
238 val->mark_used(number);
252 if (this->is_global) {
258 val->mark_used(number);
266 false,
"Adding a name to Main namemaps that was already in it, should never happen.");
272 val->mark_used(number);
285 if (this->is_global) {
290 false,
"Removing a name from Main namemaps that was not in it, should never happen.");
303 false,
"Removing a name from Main namemaps that was not in it, should never happen.");
311 if (val ==
nullptr) {
315 val->get()->mark_unused(number);
316 if (!val->get()->max_value_in_use) {
329 if (*r_name_map ==
nullptr) {
332#ifdef DEBUG_PRINT_MEMORY_USAGE
339 printf(
"NameMap memory usage: full_names %.1fKB, base_names %.1fKB\n",
340 size_full_names / 1024.0,
341 size_base_names / 1024.0);
343 MEM_SAFE_DELETE(*r_name_map);
348 auto bmain_namemap_clear = [](
Main *bmain_iter) ->
void {
351 for (
Library *lib_iter =
static_cast<Library *
>(bmain_iter->libraries.first);
353 lib_iter =
static_cast<Library *
>(lib_iter->id.next))
361 "Main should always be part of its own `split_mains`");
363 bmain_namemap_clear(bmain_iter);
367 bmain_namemap_clear(&bmain);
377 const bool ensure_created)
393 const bool ensure_created)
395 if (
lib !=
nullptr) {
396 if (ensure_created &&
lib->runtime->name_map ==
nullptr) {
397 lib->runtime->name_map = MEM_new<UniqueName_Map>(__func__,
false);
398 lib->runtime->name_map->populate(bmain,
lib, ignore_id);
400 return lib->runtime->name_map;
402 if (ensure_created && bmain.
name_map ==
nullptr) {
403 bmain.
name_map = MEM_new<UniqueName_Map>(__func__,
false);
444 std::string &r_name_final)
450 r_name_final = fmt::format(
"{}.{:03}", base_name, number);
461 r_name_final = base_name;
465 while (r_name_final.size() > 8) {
468 BLI_strncpy(base_name_modified, r_name_final.c_str(), r_name_final.size() + 1);
469 base_name_modified[r_name_final.size() - 1] =
'\0';
474 r_name_final = base_name_modified;
477 if (!val || val->get()->max_value_in_use.value_or(0) <
MAX_NUMBER) {
484 const StringRef new_base_name = r_name_final;
485 r_name_final = fmt::format(
"{}_{:03}", r_name_final, suffix);
486 while (r_name_final.size() <
MAX_ID_NAME - 2 - 12) {
489 if (!val || val->get()->max_value_in_use.value_or(0) <
MAX_NUMBER) {
493 r_name_final = fmt::format(
"{}_{:03}", new_base_name, suffix);
501 "Impossible to find an available name for '%s' base name, even by editing that base "
502 "name. This should never happen in real-life scenarii. Now trying to brute-force "
503 "generate random names until a free one is found.",
507 r_name_final = fmt::format(
"{}_{}", new_base_name, uint32_t(
get_default_hash(r_name_final)));
510 if (!val || val->get()->max_value_in_use.value_or(0) <
MAX_NUMBER) {
518 std::string &r_name_full,
519 const bool do_unique_in_bmain)
530 bool is_name_changed =
false;
547 name_map->
add_name(type_map, r_name_full, name_base, number);
548 if (name_map_other !=
nullptr) {
549 name_map_other->
add_name(
GS(
id.
name), r_name_full, name_base, number);
551 return is_name_changed;
557 is_name_changed =
true;
562 const int number_to_use = val->get_smallest_unused();
574 name_map->
add_name(type_map, r_name_full, name_base, number_to_use);
575 if (name_map_other !=
nullptr) {
576 name_map_other->
add_name(
GS(
id.
name), r_name_full, name_base, number_to_use);
578 return is_name_changed;
584 std::string r_name_full = r_name;
586 const bool is_name_modified =
namemap_get_name(bmain,
id, r_name_full,
false);
589 return is_name_modified;
593 std::string r_name_full = r_name;
595 const bool is_name_modified =
namemap_get_name(bmain,
id, r_name_full,
true);
598 return is_name_modified;
604 if (
name.is_empty()) {
608 const short id_code =
GS(
id.
name);
611 if (name_map_local) {
616 if (name_map_global) {
630 return a.
lib ==
b.lib && a.
name ==
b.name;
638 bool is_valid =
true;
642 if (id_validated.
contains(id_iter)) {
648 if (!id_names_libs.
add(key)) {
652 "ID name '%s' (from library '%s') is found more than once",
654 id_iter->lib !=
nullptr ? id_iter->lib->filepath :
"<None>");
665 key.
name = id_iter->name;
666 if (!id_names_libs.
add(key)) {
670 "\tID has been renamed to '%s', but it still seems to be already in use",
674 CLOG_WARN(&
LOG,
"\tID has been renamed to '%s'", id_iter->name);
675 id_validated.
add(id_iter);
680 "ID name '%s' (from library '%s') is found more than once",
682 id_iter->lib !=
nullptr ? id_iter->lib->filepath :
"<None>");
687 if (name_map ==
nullptr) {
699 "ID name '%s' (from library '%s') exists in current Main, but is not listed in "
702 id_iter->lib !=
nullptr ? id_iter->lib->filepath :
"<None>");
707 "ID name '%s' (from library '%s') exists in current Main, but is not listed in "
710 id_iter->lib !=
nullptr ? id_iter->lib->filepath :
"<None>");
728 fmt::format(
"{}{}",
StringRef{
reinterpret_cast<char *
>(&idcode), 2}, id_name),
lib};
733 "ID name '%s' (from library '%s') is listed in the namemap, but does not "
734 "exists in current Main",
736 lib !=
nullptr ?
lib->filepath :
"<None>");
740 "ID name '%s' (from library '%s') is listed in the namemap, but does not "
741 "exists in current Main",
743 lib !=
nullptr ?
lib->filepath :
"<None>");
751 name_map = (
lib !=
nullptr) ?
lib->runtime->name_map :
nullptr;
752 }
while (
lib !=
nullptr);
754 if (is_valid || !do_fix) {
int BKE_idtype_idcode_to_index(short idcode)
short BKE_idtype_idcode_iter_step(int *idtype_index)
IDNewNameResult BKE_id_new_name_validate(Main &bmain, ListBase &lb, ID &id, const char *newname, IDNewNameMode mode, bool do_linked_data)
const char * BKE_id_name(const ID &id)
#define FOREACH_MAIN_ID_END
ListBase * which_libbase(Main *bmain, short type)
#define FOREACH_MAIN_LISTBASE_END
#define FOREACH_MAIN_LISTBASE_BEGIN(_bmain, _lb)
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
#define BLI_assert_unreachable()
#define BLI_assert_msg(a, msg)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
int BLI_str_utf8_invalid_strip(char *str, size_t str_len) ATTR_NONNULL(1)
size_t BLI_string_split_name_number(const char *name, char delim, char *r_name_left, int *r_number) ATTR_NONNULL(1
#define CLOG_ERROR(clg_ref,...)
#define CLOG_WARN(clg_ref,...)
ID and Library types, which are fundamental for SDNA.
Read Guarded memory(de)allocation.
unsigned long long int uint64_t
const Value * lookup_ptr(const Key &key) const
int64_t size_in_bytes() const
bool add(const Key &key, const Value &value)
Value & lookup_or_add_as(ForwardKey &&key, ForwardValue &&...value)
void remove_contained(const Key &key)
bool remove(const Key &key)
KeyIterator keys() const &
bool contains(const Key &key) const
bool contains(const Key &key) const
constexpr int64_t size() const
constexpr const char * c_str() const
void resize(const int64_t new_size_in_bits, const bool value=false)
void BKE_main_namemap_destroy(UniqueName_Map **r_name_map)
static bool namemap_get_name(Main &bmain, ID &id, std::string &r_name_full, const bool do_unique_in_bmain)
static bool main_namemap_validate_and_fix(Main &bmain, const bool do_fix)
bool BKE_main_namemap_validate_and_fix(Main &bmain)
void BKE_main_namemap_remove_id(Main &bmain, ID &id)
bool BKE_main_namemap_contain_name(Main &bmain, Library *lib, const short id_type, StringRef name)
constexpr int NO_AVAILABLE_NUMBER
static bool id_name_final_build(UniqueName_TypeMap &type_map, const StringRefNull base_name, const int number, std::string &r_name_final)
void BKE_main_namemap_clear(Main &bmain)
bool BKE_main_namemap_get_unique_name(Main &bmain, ID &id, char *r_name)
bool BKE_main_global_namemap_get_unique_name(Main &bmain, ID &id, char *r_name)
static UniqueName_Map * get_namemap_for(Main &bmain, Library *lib, ID *ignore_id, const bool ensure_created)
static UniqueName_Map * get_global_namemap_for(Main &bmain, ID *ignore_id, const bool ensure_created)
bool BKE_main_namemap_validate(Main &bmain)
bool BKE_main_global_namemap_contain_name(Main &bmain, const short id_type, StringRef name)
void max_inplace(T &a, const T &b)
uint64_t get_default_hash(const T &v, const Args &...args)
UniqueName_Map * name_map_global
UniqueName_Map * name_map
std::shared_ptr< blender::VectorSet< Main * > > split_mains
void add_name(UniqueName_TypeMap &type_map, StringRef name_full, StringRef name_base, const int number)
std::array< UniqueName_TypeMap, INDEX_ID_MAX > type_maps
void populate(Main &bmain, Library *library, ID *ignore_id)
UniqueName_Map(const bool is_global)
void remove_full_name(const short id_type, blender::StringRef name_full)
void remove_full_name(UniqueName_TypeMap &type_map, blender::StringRef name_full)
UniqueName_TypeMap & find_by_type(const short id_type)
void add_name(const short id_type, StringRef name_full, StringRef name_base, const int number)
Map< std::string, int > full_names
Map< std::string, std::unique_ptr< UniqueName_Value > > base_name_to_num_suffix
void mark_unused(const int number)
void mark_used(const int number)
std::optional< Map< int, int > > numbers_multi_usages
int get_smallest_unused()
std::optional< int > max_value_in_use
static constexpr int max_exact_tracking
friend bool operator==(const Uniqueness_Key &a, const Uniqueness_Key &b)
static DynamicLibrary lib