Blender V4.5
main_namemap.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BKE_idtype.hh"
10#include "BKE_lib_id.hh"
11#include "BKE_library.hh"
12#include "BKE_main.hh"
13#include "BKE_main_namemap.hh"
14
15#include "BLI_assert.h"
16#include "BLI_bit_span_ops.hh"
17#include "BLI_bit_vector.hh"
18#include "BLI_listbase.h"
19#include "BLI_map.hh"
20#include "BLI_math_base.hh"
21#include "BLI_string.h"
22#include "BLI_string_utf8.h"
23#include "BLI_string_utils.hh"
24
25#include "DNA_ID.h"
26
27#include "MEM_guardedalloc.h"
28
29#include "CLG_log.h"
30
31#include <fmt/format.h>
32
33static CLG_LogRef LOG = {"bke.main_namemap"};
34
35// #define DEBUG_PRINT_MEMORY_USAGE
36
37using namespace blender;
38
39/* Assumes and ensure that the suffix number can never go beyond 1 billion. */
40constexpr int MAX_NUMBER = 999999999;
41/* Value representing that there is no available number. Must be negative value. */
42constexpr int NO_AVAILABLE_NUMBER = -1;
43
44/* Tracking of used numeric suffixes. For each base name:
45 *
46 * - Exactly track which of the lowest 1023 suffixes are in use,
47 * whenever there is a name collision we pick the lowest "unused"
48 * one. This is done with a bit vector.
49 * - Above 1023, do not track them exactly, just track the maximum
50 * suffix value seen so far. Upon collision, assign number that is
51 * one larger.
52 */
54 static constexpr int max_exact_tracking = 1023;
55 std::optional<int> max_value_in_use = {};
57 /* Only created when required. Used to manage cases where the same numeric value is used by
58 * several unique full names ('Foo.1' and 'Foo.001' e.g.).
59 *
60 * The key is the suffix numeric value.
61 * The value is the number of how many different full names using that same base name and numeric
62 * suffix value are currently registered.
63 *
64 * This code will never generate such cases for local maps, but it needs to support users doing
65 * so explicitly.
66 *
67 * For global maps, this is a much more common case, as duplicates of ID names across libraries
68 * are fairly common. */
69 std::optional<Map<int, int>> numbers_multi_usages = std::nullopt;
70
71 void mark_used(const int number)
72 {
73 BLI_assert(number >= 0);
74 if (number >= 0 && number <= max_exact_tracking) {
75 if (this->mask.size() <= number) {
76 this->mask.resize(number + 1);
77 }
78 if (this->mask[number]) {
79 if (!this->numbers_multi_usages.has_value()) {
80 this->numbers_multi_usages.emplace();
81 }
82 int &multi_usages_num = this->numbers_multi_usages->lookup_or_add(number, 1);
83 BLI_assert(multi_usages_num >= 1);
84 multi_usages_num++;
85 }
86 else {
87 this->mask[number].set(true);
88 }
89 }
90 if (number <= MAX_NUMBER) {
91 if (this->max_value_in_use) {
92 math::max_inplace(this->max_value_in_use.value(), number);
93 }
94 else {
95 this->max_value_in_use = number;
96 }
97 }
98 }
99
100 void mark_unused(const int number)
101 {
102 BLI_assert(number >= 0);
103 if (number >= 0 && number <= max_exact_tracking) {
104 BLI_assert_msg(number < this->mask.size(),
105 "Trying to unregister a number suffix higher than current size of the bit "
106 "vector, should never happen.");
107
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);
110 BLI_assert(multi_usages_num > 1);
111 multi_usages_num--;
112 if (multi_usages_num == 1) {
113 this->numbers_multi_usages->remove_contained(number);
114 }
115 /* Do not unset the matching bit, nor handle #max_value_in_use, since there are more IDs
116 * with the same base name and numeric suffix value. */
117 return;
118 }
119
120 this->mask[number].set(false);
121 }
122 if (number == this->max_value_in_use.value_or(NO_AVAILABLE_NUMBER)) {
123 if (number > 0) {
124 this->max_value_in_use.value()--;
125 }
126 else {
127 this->max_value_in_use.reset();
128 }
129 }
130 }
131
139 {
140 if (this->mask.size() < 2) {
141 /* No numbered entry was ever registered for this name yet, so first non-null value is
142 * unused. */
143 return 1;
144 }
145 /* Never pick the `0` value (e.g. if 'Foo.001' is used and another 'Foo.001' is requested,
146 * return 'Foo.002' and not 'Foo'). So only search on mask[1:] range. */
147 BitSpan search_mask(this->mask.data(), IndexRange(1, this->mask.size() - 1));
148 std::optional<int64_t> result = find_first_0_index(search_mask);
149 if (result) {
150 return int(*result + 1);
151 }
152 if (this->mask.size() <= max_exact_tracking) {
153 /* No need to increase size of the mask here, this will be done by calls to #mark_used once
154 * the final name with its final number has been defined. */
155 return int(this->mask.size());
156 }
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;
160 }
161 return NO_AVAILABLE_NUMBER;
162 }
163 return 1;
164 }
165};
166
167/* Tracking of names for a single ID type. */
169 /* Map of full names that are in use, and the amount of times they are used.
170 * For local maps, the number values must all always be `1`.
171 * For global maps, a same name can be used by several IDs from different libraries. */
173 /* For each base name (i.e. without numeric suffix), track the
174 * numeric suffixes that are in use. */
176};
177
179 std::array<UniqueName_TypeMap, INDEX_ID_MAX> type_maps;
180 /* Whether this map covers all IDs in its Main, or only the local (or a specific library) ones.
181 */
183
185
186 UniqueName_TypeMap &find_by_type(const short id_type)
187 {
188 int index = BKE_idtype_idcode_to_index(id_type);
189 return type_maps[size_t(index)];
190 }
191
192 void populate(Main &bmain, Library *library, ID *ignore_id)
193 {
194 for (UniqueName_TypeMap &type_map : this->type_maps) {
195 type_map.full_names.clear();
196 type_map.base_name_to_num_suffix.clear();
197 }
198 ID *id;
199 FOREACH_MAIN_ID_BEGIN (&bmain, id) {
200 if ((id == ignore_id) || (!this->is_global && (id->lib != library))) {
201 continue;
202 }
203 UniqueName_TypeMap &type_map = this->find_by_type(GS(id->name));
204
205 /* Insert the full name into the map. */
206 if (this->is_global) {
207 /* Global name-map is expected to have several IDs using the same name (from different
208 * libraries). */
210 count++;
211 if (count > 1) {
212 /* Name is already used at least once, just increase user-count. */
213 continue;
214 }
215 }
216 else {
217 /* For non-global name-maps, there should only be one usage for each name, adding the
218 * full-name as key should always return `true`. */
219 if (!type_map.full_names.add(BKE_id_name(*id), 1)) {
220 /* Do not assert, this code is also used by #BKE_main_namemap_validate_and_fix, where
221 * duplicates are expected. */
222#if 0
223 BLI_assert_msg(false,
224 "The key (name) already exists in the namemap, should only happen when "
225 "`do_global` is true.");
226#endif
227 continue;
228 }
229 }
230
231 /* Get the name and number parts ("name.number"). */
232 int number = 0;
233 const std::string name_base = BLI_string_split_name_number(BKE_id_name(*id), '.', number);
234
235 /* Get and update the entry for this base name. */
236 std::unique_ptr<UniqueName_Value> &val = type_map.base_name_to_num_suffix.lookup_or_add_as(
237 name_base, std::make_unique<UniqueName_Value>(UniqueName_Value{}));
238 val->mark_used(number);
239 }
241 }
242
243 /* Add the given name to the given #type_map, returns `true` if added, `false` if it was already
244 * in the map. */
246 StringRef name_full,
247 StringRef name_base,
248 const int number)
249 {
250 BLI_assert(name_full.size() < MAX_ID_NAME - 2);
251
252 if (this->is_global) {
253 /* By definition adding to global map is always successful. */
254 int &count = type_map.full_names.lookup_or_add_as(name_full, 0);
255 if (!count) {
256 std::unique_ptr<UniqueName_Value> &val = type_map.base_name_to_num_suffix.lookup_or_add_as(
257 name_base, std::make_unique<UniqueName_Value>(UniqueName_Value{}));
258 val->mark_used(number);
259 }
260 count++;
261 return;
262 }
263
264 if (!type_map.full_names.add(name_full, 1)) {
266 false, "Adding a name to Main namemaps that was already in it, should never happen.");
267 return;
268 }
269
270 std::unique_ptr<UniqueName_Value> &val = type_map.base_name_to_num_suffix.lookup_or_add_as(
271 name_base, std::make_unique<UniqueName_Value>(UniqueName_Value{}));
272 val->mark_used(number);
273 }
274 void add_name(const short id_type, StringRef name_full, StringRef name_base, const int number)
275 {
276 this->add_name(this->find_by_type(id_type), name_full, name_base, number);
277 }
278
279 /* Remove a full name_full from the specified #type_map. Trying to remove an unknown
280 * (unregistered) name_full is an error. */
282 {
283 BLI_assert(name_full.size() < MAX_ID_NAME - 2);
284
285 int number = 0;
286 const std::string name_base = BLI_string_split_name_number(name_full, '.', number);
287 std::unique_ptr<UniqueName_Value> *val = nullptr;
288
289 if (this->is_global) {
290 /* By definition adding to global map is always successful. */
291 int *count = type_map.full_names.lookup_ptr(name_full);
292 if (!count) {
294 false, "Removing a name from Main namemaps that was not in it, should never happen.");
295 return;
296 }
297 if (*count > 1) {
298 (*count)--;
299 /* If there are still one or more usages of this number, do not mark it as unused in the
300 * global map, so do not lookup for the #UniqueName_Value of its basename. */
301 }
302 else {
303 BLI_assert(*count == 1);
304 type_map.full_names.remove_contained(name_full);
305 val = type_map.base_name_to_num_suffix.lookup_ptr(name_base);
306 if (!val) {
308 return;
309 }
310 }
311 }
312 else {
313 if (!type_map.full_names.remove(name_full)) {
315 false, "Removing a name from Main namemaps that was not in it, should never happen.");
316 return;
317 }
318 val = type_map.base_name_to_num_suffix.lookup_ptr(name_base);
319 if (!val) {
321 return;
322 }
323 }
324
325 if (val) {
326 val->get()->mark_unused(number);
327 if (!val->get()->max_value_in_use) {
328 /* This was the only base name usage, remove the whole key. */
329 type_map.base_name_to_num_suffix.remove(name_base);
330 }
331 }
332 }
333 void remove_full_name(const short id_type, blender::StringRef name_full)
334 {
335 this->remove_full_name(this->find_by_type(id_type), name_full);
336 }
337};
338
340{
341 if (*r_name_map == nullptr) {
342 return;
343 }
344#ifdef DEBUG_PRINT_MEMORY_USAGE
345 int64_t size_full_names = 0;
346 int64_t size_base_names = 0;
347 for (const UniqueName_TypeMap &type_map : (*r_name_map)->type_maps) {
348 size_full_names += type_map.full_names.size_in_bytes();
349 size_base_names += type_map.base_name_to_num_suffix.size_in_bytes();
350 }
351 printf("NameMap memory usage: full_names %.1fKB, base_names %.1fKB\n",
352 size_full_names / 1024.0,
353 size_base_names / 1024.0);
354#endif
355 MEM_SAFE_DELETE(*r_name_map);
356}
357
359{
360 for (Main *bmain_iter = &bmain; bmain_iter != nullptr; bmain_iter = bmain_iter->next) {
361 BKE_main_namemap_destroy(&bmain_iter->name_map);
362 BKE_main_namemap_destroy(&bmain_iter->name_map_global);
363 for (Library *lib_iter = static_cast<Library *>(bmain_iter->libraries.first);
364 lib_iter != nullptr;
365 lib_iter = static_cast<Library *>(lib_iter->id.next))
366 {
367 BKE_main_namemap_destroy(&lib_iter->runtime->name_map);
368 }
369 }
370}
371
372/* Get the global (local and linked IDs) name map object used for the given Main/ID.
373 * Lazily creates and populates the contents of the name map, if ensure_created is true.
374 * NOTE: if the contents are populated, the name of the given #ignore_id ID (if any) is not added.
375 */
377 ID *ignore_id,
378 const bool ensure_created)
379{
380 if (ensure_created && bmain.name_map_global == nullptr) {
381 bmain.name_map_global = MEM_new<UniqueName_Map>(__func__, true);
382 bmain.name_map_global->populate(bmain, nullptr, ignore_id);
383 }
384 return bmain.name_map_global;
385}
386
387/* Get the local or library-specific (when #lib is not null) name map object used for the given
388 * Main/ID. Lazily creates and populates the contents of the name map, if ensure_created is true.
389 * NOTE: if the contents are populated, the name of the given #ignore_id ID (if any) is not added.
390 */
392 Library *lib,
393 ID *ignore_id,
394 const bool ensure_created)
395{
396 if (lib != nullptr) {
397 if (ensure_created && lib->runtime->name_map == nullptr) {
398 lib->runtime->name_map = MEM_new<UniqueName_Map>(__func__, false);
399 lib->runtime->name_map->populate(bmain, lib, ignore_id);
400 }
401 return lib->runtime->name_map;
402 }
403 if (ensure_created && bmain.name_map == nullptr) {
404 bmain.name_map = MEM_new<UniqueName_Map>(__func__, false);
405 bmain.name_map->populate(bmain, lib, ignore_id);
406 }
407 return bmain.name_map;
408}
409
410bool BKE_main_global_namemap_contain_name(Main &bmain, const short id_type, StringRef name)
411{
412 UniqueName_Map *name_map = get_global_namemap_for(bmain, nullptr, true);
413 BLI_assert(name_map != nullptr);
414 BLI_assert(name.size() < MAX_ID_NAME - 2);
415 UniqueName_TypeMap &type_map = name_map->find_by_type(id_type);
416
417 return type_map.full_names.contains(name);
418}
419
420bool BKE_main_namemap_contain_name(Main &bmain, Library *lib, const short id_type, StringRef name)
421{
422 UniqueName_Map *name_map = get_namemap_for(bmain, lib, nullptr, true);
423 BLI_assert(name_map != nullptr);
424 BLI_assert(name.size() < MAX_ID_NAME - 2);
425 UniqueName_TypeMap &type_map = name_map->find_by_type(id_type);
426
427 return type_map.full_names.contains(name);
428}
429
443 const StringRefNull base_name,
444 const int number,
445 std::string &r_name_final)
446{
447 /* In case no number value is available, current base name cannot be used to generate a final
448 * full name. */
449 if (number != NO_AVAILABLE_NUMBER) {
450 BLI_assert(number >= 0 && number <= MAX_NUMBER);
451 r_name_final = fmt::format("{}.{:03}", base_name, number);
452 /* Most common case, there is a valid number suffix value and it fits in the #MAX_ID_NAME - 2
453 * length limit.
454 */
455 if (r_name_final.size() < MAX_ID_NAME - 2) {
456 return true;
457 }
458 }
459
460 /* The base name cannot be used as-is, it needs to be modified, and a new number suffix must be
461 * generated for it. */
462 r_name_final = base_name;
463
464 /* If the base name is long enough, shorten it by one (UTF8) char, until a base name with
465 * available number suffixes is found. */
466 while (r_name_final.size() > 8) {
467 char base_name_modified[MAX_ID_NAME - 2];
468
469 BLI_strncpy(base_name_modified, r_name_final.c_str(), r_name_final.size() + 1);
470 base_name_modified[r_name_final.size() - 1] = '\0';
471 /* Raw truncation of an UTF8 string may generate invalid UTF8 char-code at the end.
472 * Ensure we get a valid one now. */
473 BLI_str_utf8_invalid_strip(base_name_modified, r_name_final.size() - 1);
474
475 r_name_final = base_name_modified;
476 std::unique_ptr<UniqueName_Value> *val = type_map.base_name_to_num_suffix.lookup_ptr(
477 r_name_final);
478 if (!val || val->get()->max_value_in_use.value_or(0) < MAX_NUMBER) {
479 return false;
480 }
481 }
482 /* Else, extend the name with an increasing three-or-more-digits number, as it's better to add
483 * gibberish at the end of a short name, rather than shorten it further. */
484 uint64_t suffix = 1;
485 const StringRef new_base_name = r_name_final;
486 r_name_final = fmt::format("{}_{:03}", r_name_final, suffix);
487 while (r_name_final.size() < MAX_ID_NAME - 2 - 12) {
488 std::unique_ptr<UniqueName_Value> *val = type_map.base_name_to_num_suffix.lookup_ptr(
489 r_name_final);
490 if (!val || val->get()->max_value_in_use.value_or(0) < MAX_NUMBER) {
491 return false;
492 }
493 suffix++;
494 r_name_final = fmt::format("{}_{:03}", new_base_name, suffix);
495 }
496
497 /* WARNING: This code is absolute last defense, essentially theoretical case at this point.
498 * It is not expected to ever be reached in practice. It is also virtually impossible to actually
499 * test that case, given the enormous amount of IDs that would need to be created before it is
500 * reached. */
502 "Impossible to find an available name for '%s' base name, even by editing that base "
503 "name. This should never happen in real-life scenarii. Now trying to brute-force "
504 "generate random names until a free one is found.",
505 base_name.c_str());
506 BLI_assert(new_base_name.size() <= 8);
507 while (true) {
508 r_name_final = fmt::format("{}_{}", new_base_name, uint32_t(get_default_hash(r_name_final)));
509 std::unique_ptr<UniqueName_Value> *val = type_map.base_name_to_num_suffix.lookup_ptr(
510 r_name_final);
511 if (!val || val->get()->max_value_in_use.value_or(0) < MAX_NUMBER) {
512 return false;
513 }
514 }
515}
516
517static bool namemap_get_name(Main &bmain,
518 ID &id,
519 std::string &r_name_full,
520 const bool do_unique_in_bmain)
521{
522 UniqueName_Map *name_map = do_unique_in_bmain ? get_global_namemap_for(bmain, &id, true) :
523 get_namemap_for(bmain, id.lib, &id, true);
524 UniqueName_Map *name_map_other = do_unique_in_bmain ?
525 get_namemap_for(bmain, id.lib, &id, false) :
526 get_global_namemap_for(bmain, &id, false);
527 BLI_assert(name_map != nullptr);
528 BLI_assert(r_name_full.size() < MAX_ID_NAME - 2);
529 UniqueName_TypeMap &type_map = name_map->find_by_type(GS(id.name));
530
531 bool is_name_changed = false;
532
533 while (true) {
534 /* Get the name and number parts ("name.number"). */
535 int number = 0;
536 const std::string name_base = BLI_string_split_name_number(r_name_full, '.', number);
537 std::unique_ptr<UniqueName_Value> &val = type_map.base_name_to_num_suffix.lookup_or_add_as(
538 name_base, std::make_unique<UniqueName_Value>(UniqueName_Value{}));
539
540 /* If the full original name is unused, and its number suffix is unused, or is above the max
541 * managed value, the name can be used directly.
542 *
543 * NOTE: The second part of the check is designed to prevent issues with different names with
544 * the same name base, and the same numeric value as suffix, but written differently.
545 * E.g. `Mesh.001` and `Mesh.1` would both "use" the numeric suffix for base name `Mesh`.
546 * Removing `Mesh.1` would then mark `001` suffix as available, which would be incorrect. */
547 if (!type_map.full_names.contains(r_name_full)) {
548 name_map->add_name(type_map, r_name_full, name_base, number);
549 if (name_map_other != nullptr) {
550 name_map_other->add_name(GS(id.name), r_name_full, name_base, number);
551 }
552 return is_name_changed;
553 }
554
555 /* At this point, if this is the first iteration, the initially given name is colliding with an
556 * existing ID name, and has to be modified. If this is a later iteration, the given name has
557 * already been modified one way or another. */
558 is_name_changed = true;
559
560 /* The base name and current number suffix are already used.
561 * Request the lowest available valid number suffix (will return #NO_AVAILABLE_NUMBER if none
562 * are available for the current base name). */
563 const int number_to_use = val->get_smallest_unused();
564
565 /* Try to build final name from the current base name and the number.
566 * Note that this will fail if the suffix number is #NO_AVAILABLE_NUMBER, or if the base name
567 * and suffix number would give a too long name. In such cases, this call will modify
568 * the base name and put it into r_name_full, and a new iteration to find a suitable suffix
569 * number and valid full name is needed. */
570 if (!id_name_final_build(type_map, name_base, number_to_use, r_name_full)) {
571 continue;
572 }
573
574 /* All good, add final name to the set. */
575 name_map->add_name(type_map, r_name_full, name_base, number_to_use);
576 if (name_map_other != nullptr) {
577 name_map_other->add_name(GS(id.name), r_name_full, name_base, number_to_use);
578 }
579 return is_name_changed;
580 }
581}
582
583bool BKE_main_namemap_get_unique_name(Main &bmain, ID &id, char *r_name)
584{
585 std::string r_name_full = r_name;
586 BLI_assert(r_name_full.size() < MAX_ID_NAME - 2);
587 const bool is_name_modified = namemap_get_name(bmain, id, r_name_full, false);
588 BLI_assert(r_name_full.size() < MAX_ID_NAME - 2);
589 BLI_strncpy(r_name, r_name_full.c_str(), MAX_ID_NAME - 2);
590 return is_name_modified;
591}
592bool BKE_main_global_namemap_get_unique_name(Main &bmain, ID &id, char *r_name)
593{
594 std::string r_name_full = r_name;
595 BLI_assert(r_name_full.size() < MAX_ID_NAME - 2);
596 const bool is_name_modified = namemap_get_name(bmain, id, r_name_full, true);
597 BLI_assert(r_name_full.size() < MAX_ID_NAME - 2);
598 BLI_strncpy(r_name, r_name_full.c_str(), MAX_ID_NAME - 2);
599 return is_name_modified;
600}
601
603{
604 StringRef name = BKE_id_name(id);
605 if (name.is_empty()) {
606 /* Name is empty or not initialized yet, nothing to remove. */
607 return;
608 }
609 const short id_code = GS(id.name);
610
611 UniqueName_Map *name_map_local = get_namemap_for(bmain, id.lib, nullptr, false);
612 if (name_map_local) {
613 name_map_local->remove_full_name(id_code, name);
614 }
615
616 UniqueName_Map *name_map_global = get_global_namemap_for(bmain, nullptr, false);
617 if (name_map_global) {
618 name_map_global->remove_full_name(id_code, name);
619 }
620}
621
623 std::string name;
626 {
628 }
629 friend bool operator==(const Uniqueness_Key &a, const Uniqueness_Key &b)
630 {
631 return a.lib == b.lib && a.name == b.name;
632 }
633};
634
635static bool main_namemap_validate_and_fix(Main &bmain, const bool do_fix)
636{
637 Set<Uniqueness_Key> id_names_libs;
638 Set<ID *> id_validated;
639 bool is_valid = true;
640 ListBase *lb_iter;
641 FOREACH_MAIN_LISTBASE_BEGIN (&bmain, lb_iter) {
642 LISTBASE_FOREACH_MUTABLE (ID *, id_iter, lb_iter) {
643 if (id_validated.contains(id_iter)) {
644 /* Do not re-check an already validated ID. */
645 continue;
646 }
647
648 Uniqueness_Key key = {id_iter->name, id_iter->lib};
649 if (!id_names_libs.add(key)) {
650 is_valid = false;
651 if (do_fix) {
652 CLOG_WARN(&LOG,
653 "ID name '%s' (from library '%s') is found more than once",
654 id_iter->name,
655 id_iter->lib != nullptr ? id_iter->lib->filepath : "<None>");
656 /* NOTE: this may imply moving this ID in its listbase. The logic below will add the ID
657 * to the validated set if it can now be added to `id_names_libs`, and will prevent
658 * further checking (which would fail again, since the new ID name/lib key has already
659 * been added to `id_names_libs`). */
661 *which_libbase(&bmain, GS(id_iter->name)),
662 *id_iter,
663 nullptr,
665 true);
666 key.name = id_iter->name;
667 if (!id_names_libs.add(key)) {
668 /* This is a serious error, very likely a bug, keep it as CLOG_ERROR even when doing
669 * fixes. */
671 "\tID has been renamed to '%s', but it still seems to be already in use",
672 id_iter->name);
673 }
674 else {
675 CLOG_WARN(&LOG, "\tID has been renamed to '%s'", id_iter->name);
676 id_validated.add(id_iter);
677 }
678 }
679 else {
681 "ID name '%s' (from library '%s') is found more than once",
682 id_iter->name,
683 id_iter->lib != nullptr ? id_iter->lib->filepath : "<None>");
684 }
685 }
686
687 UniqueName_Map *name_map = get_namemap_for(bmain, id_iter->lib, id_iter, false);
688 if (name_map == nullptr) {
689 continue;
690 }
691 UniqueName_TypeMap &type_map = name_map->find_by_type(GS(id_iter->name));
692
693 /* Remove full name from the set. */
694 const std::string id_name = BKE_id_name(*id_iter);
695 if (!type_map.full_names.contains(id_name)) {
696 is_valid = false;
697 if (do_fix) {
698 CLOG_WARN(
699 &LOG,
700 "ID name '%s' (from library '%s') exists in current Main, but is not listed in "
701 "the namemap",
702 id_iter->name,
703 id_iter->lib != nullptr ? id_iter->lib->filepath : "<None>");
704 }
705 else {
707 &LOG,
708 "ID name '%s' (from library '%s') exists in current Main, but is not listed in "
709 "the namemap",
710 id_iter->name,
711 id_iter->lib != nullptr ? id_iter->lib->filepath : "<None>");
712 }
713 }
714 }
715 }
717
718 Library *lib = nullptr;
719 UniqueName_Map *name_map = bmain.name_map;
720 do {
721 if (name_map) {
722 int i = 0;
723 for (short idcode = BKE_idtype_idcode_iter_step(&i); idcode != 0;
725 {
726 UniqueName_TypeMap &type_map = name_map->find_by_type(idcode);
727 for (const std::string &id_name : type_map.full_names.keys()) {
728 Uniqueness_Key key = {
729 fmt::format("{}{}", StringRef{reinterpret_cast<char *>(&idcode), 2}, id_name), lib};
730 if (!id_names_libs.contains(key)) {
731 is_valid = false;
732 if (do_fix) {
733 CLOG_WARN(&LOG,
734 "ID name '%s' (from library '%s') is listed in the namemap, but does not "
735 "exists in current Main",
736 key.name.c_str(),
737 lib != nullptr ? lib->filepath : "<None>");
738 }
739 else {
741 "ID name '%s' (from library '%s') is listed in the namemap, but does not "
742 "exists in current Main",
743 key.name.c_str(),
744 lib != nullptr ? lib->filepath : "<None>");
745 }
746 }
747 }
748 }
749 }
750
751 lib = static_cast<Library *>((lib == nullptr) ? bmain.libraries.first : lib->id.next);
752 name_map = (lib != nullptr) ? lib->runtime->name_map : nullptr;
753 } while (lib != nullptr);
754
755 if (is_valid || !do_fix) {
756 return is_valid;
757 }
758
759 /* Clear all existing name-maps. */
761
762 return is_valid;
763}
764
766{
767 const bool is_valid = main_namemap_validate_and_fix(bmain, true);
769 return is_valid;
770}
771
773{
774 return main_namemap_validate_and_fix(bmain, false);
775}
int BKE_idtype_idcode_to_index(short idcode)
Definition idtype.cc:229
short BKE_idtype_idcode_iter_step(int *idtype_index)
Definition idtype.cc:376
IDNewNameResult BKE_id_new_name_validate(Main &bmain, ListBase &lb, ID &id, const char *newname, IDNewNameMode mode, bool do_linked_data)
Definition lib_id.cc:1883
const char * BKE_id_name(const ID &id)
#define FOREACH_MAIN_ID_END
Definition BKE_main.hh:563
ListBase * which_libbase(Main *bmain, short type)
Definition main.cc:887
#define FOREACH_MAIN_LISTBASE_END
Definition BKE_main.hh:544
#define FOREACH_MAIN_LISTBASE_BEGIN(_bmain, _lb)
Definition BKE_main.hh:537
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition BKE_main.hh:557
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#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)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:182
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:181
ID and Library types, which are fundamental for SDNA.
Read Guarded memory(de)allocation.
blender::StringRef BLI_string_split_name_number(const blender::StringRef name_full, const char delim, int &r_number)
long long int int64_t
unsigned long long int uint64_t
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
int64_t size_in_bytes() const
Definition BLI_map.hh:1019
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
Value & lookup_or_add_as(ForwardKey &&key, ForwardValue &&...value)
Definition BLI_map.hh:605
void remove_contained(const Key &key)
Definition BLI_map.hh:387
bool remove(const Key &key)
Definition BLI_map.hh:368
KeyIterator keys() const &
Definition BLI_map.hh:875
bool contains(const Key &key) const
Definition BLI_map.hh:353
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr bool is_empty() 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)
std::string id_name(void *id)
#define this
#define printf(...)
#define MAX_ID_NAME
#define GS(a)
int count
#define LOG(severity)
Definition log.h:32
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)
constexpr int MAX_NUMBER
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)
Definition BLI_hash.hh:233
Definition DNA_ID.h:404
struct Library * lib
Definition DNA_ID.h:410
char name[66]
Definition DNA_ID.h:415
void * first
UniqueName_Map * name_map_global
Definition BKE_main.hh:310
UniqueName_Map * name_map
Definition BKE_main.hh:304
ListBase libraries
Definition BKE_main.hh:246
Main * next
Definition BKE_main.hh:142
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
std::optional< int > max_value_in_use
static constexpr int max_exact_tracking
uint64_t hash() const
std::string name
friend bool operator==(const Uniqueness_Key &a, const Uniqueness_Key &b)
i
Definition text_draw.cc:230
static DynamicLibrary lib