Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/lib_id.c
| Show First 20 Lines • Show All 1,160 Lines • ▼ Show 20 Lines | void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int orig_flag) | ||||
| ID *new_id = *r_newid; | ID *new_id = *r_newid; | ||||
| int flag = orig_flag; | int flag = orig_flag; | ||||
| const bool is_private_id_data = (id->flag & LIB_EMBEDDED_DATA) != 0; | const bool is_private_id_data = (id->flag & LIB_EMBEDDED_DATA) != 0; | ||||
| BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || bmain != NULL); | BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || bmain != NULL); | ||||
| BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || (flag & LIB_ID_CREATE_NO_ALLOCATE) == 0); | BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || (flag & LIB_ID_CREATE_NO_ALLOCATE) == 0); | ||||
| if (!is_private_id_data) { | if (!is_private_id_data) { | ||||
| /* When we are handling private ID data, we might still want to manage usercounts, even though | /* When we are handling private ID data, we might still want to manage usercounts, even | ||||
| * that ID data-block is actually outside of Main... */ | * though that ID data-block is actually outside of Main... */ | ||||
| BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 || | BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 || | ||||
| (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) != 0); | (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) != 0); | ||||
| } | } | ||||
| /* Never implicitly copy shapekeys when generating temp data outside of Main database. */ | /* Never implicitly copy shapekeys when generating temp data outside of Main database. */ | ||||
| BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 || (flag & LIB_ID_COPY_SHAPEKEY) == 0); | BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 || (flag & LIB_ID_COPY_SHAPEKEY) == 0); | ||||
| /* 'Private ID' data handling. */ | /* 'Private ID' data handling. */ | ||||
| if ((bmain != NULL) && is_private_id_data) { | if ((bmain != NULL) && is_private_id_data) { | ||||
| ▲ Show 20 Lines • Show All 194 Lines • ▼ Show 20 Lines | |||||
| /* The maximum value up to which we search for the actual smallest unused number. Beyond that | /* The maximum value up to which we search for the actual smallest unused number. Beyond that | ||||
| * value, we will only use the first biggest unused number, without trying to 'fill the gaps' | * value, we will only use the first biggest unused number, without trying to 'fill the gaps' | ||||
| * in-between already used numbers... */ | * in-between already used numbers... */ | ||||
| #define MAX_NUMBERS_IN_USE 1024 | #define MAX_NUMBERS_IN_USE 1024 | ||||
| /** | /** | ||||
| * Helper building final ID name from given base_name and number. | * Helper building final ID name from given base_name and number. | ||||
| * | * | ||||
| * If everything goes well and we do generate a valid final ID name in given name, we return true. | * If everything goes well and we do generate a valid final ID name in given name, we return | ||||
| * In case the final name would overflow the allowed ID name length, or given number is bigger than | * true. In case the final name would overflow the allowed ID name length, or given number is | ||||
| * maximum allowed value, we truncate further the base_name (and given name, which is assumed to | * bigger than maximum allowed value, we truncate further the base_name (and given name, which is | ||||
| * have the same 'base_name' part), and return false. | * assumed to have the same 'base_name' part), and return false. | ||||
| */ | */ | ||||
| static bool id_name_final_build(char *name, char *base_name, size_t base_name_len, int number) | static bool id_name_final_build(char *name, char *base_name, size_t base_name_len, int number) | ||||
| { | { | ||||
| char number_str[11]; /* Dot + nine digits + NULL terminator. */ | char number_str[11]; /* Dot + nine digits + NULL terminator. */ | ||||
| size_t number_str_len = BLI_snprintf_rlen(number_str, ARRAY_SIZE(number_str), ".%.3d", number); | size_t number_str_len = BLI_snprintf_rlen(number_str, ARRAY_SIZE(number_str), ".%.3d", number); | ||||
| /* If the number would lead to an overflow of the maximum ID name length, we need to truncate | /* If the number would lead to an overflow of the maximum ID name length, we need to truncate | ||||
| * the base name part and do all the number checks again. */ | * the base name part and do all the number checks again. */ | ||||
| ▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_hint) | ||||
| /* Static storage of previous handled ID/name info, used to perform a quicker test and optimize | /* Static storage of previous handled ID/name info, used to perform a quicker test and optimize | ||||
| * creation of huge number of IDs using the same given base name. */ | * creation of huge number of IDs using the same given base name. */ | ||||
| static char prev_orig_base_name[MAX_ID_NAME - 2] = {0}; | static char prev_orig_base_name[MAX_ID_NAME - 2] = {0}; | ||||
| static char prev_final_base_name[MAX_ID_NAME - 2] = {0}; | static char prev_final_base_name[MAX_ID_NAME - 2] = {0}; | ||||
| static short prev_id_type = ID_LINK_PLACEHOLDER; /* Should never exist in actual ID list. */ | static short prev_id_type = ID_LINK_PLACEHOLDER; /* Should never exist in actual ID list. */ | ||||
| static int prev_number = MIN_NUMBER - 1; | static int prev_number = MIN_NUMBER - 1; | ||||
| /* Initial test to check whether we can 'shortcut' the more complex loop of the main code below. | /* Initial test to check whether we can 'shortcut' the more complex loop of the main code | ||||
| * Note that we do not do that for low numbers, as that would prevent using actual smallest | * below. Note that we do not do that for low numbers, as that would prevent using actual | ||||
| * available number in some cases, and benefits of this special case handling mostly show up with | * smallest available number in some cases, and benefits of this special case handling mostly | ||||
| * high numbers anyway. */ | * show up with high numbers anyway. */ | ||||
| if (id_type == prev_id_type && prev_number >= MAX_NUMBERS_IN_USE && | if (id_type == prev_id_type && prev_number >= MAX_NUMBERS_IN_USE && | ||||
| prev_number < MAX_NUMBER - 1 && name[0] == prev_final_base_name[0]) { | prev_number < MAX_NUMBER - 1 && name[0] == prev_final_base_name[0]) { | ||||
| /* Get the name and number parts ("name.number"). */ | /* Get the name and number parts ("name.number"). */ | ||||
| char base_name[MAX_ID_NAME - 2]; | char base_name[MAX_ID_NAME - 2]; | ||||
| int number = MIN_NUMBER; | int number = MIN_NUMBER; | ||||
| size_t base_name_len = BLI_split_name_num(base_name, &number, name, '.'); | size_t base_name_len = BLI_split_name_num(base_name, &number, name, '.'); | ||||
| size_t prev_final_base_name_len = strlen(prev_final_base_name); | size_t prev_final_base_name_len = strlen(prev_final_base_name); | ||||
| size_t prev_orig_base_name_len = strlen(prev_orig_base_name); | size_t prev_orig_base_name_len = strlen(prev_orig_base_name); | ||||
| if (base_name_len == prev_orig_base_name_len && | if (base_name_len == prev_orig_base_name_len && | ||||
| STREQLEN(base_name, prev_orig_base_name, prev_orig_base_name_len)) { | STREQLEN(base_name, prev_orig_base_name, prev_orig_base_name_len)) { | ||||
| /* Once we have ensured given base_name and original previous one are the same, we can check | /* Once we have ensured given base_name and original previous one are the same, we can | ||||
| * that previously used number is actually used, and that next one is free. */ | * check that previously used number is actually used, and that next one is free. */ | ||||
| /* Note that from now on, we only used previous final base name, as it might have been | /* Note that from now on, we only used previous final base name, as it might have been | ||||
| * truncated from original one due to number suffix length. */ | * truncated from original one due to number suffix length. */ | ||||
| char final_name[MAX_ID_NAME - 2]; | char final_name[MAX_ID_NAME - 2]; | ||||
| char prev_final_name[MAX_ID_NAME - 2]; | char prev_final_name[MAX_ID_NAME - 2]; | ||||
| BLI_strncpy(final_name, prev_final_base_name, prev_final_base_name_len + 1); | BLI_strncpy(final_name, prev_final_base_name, prev_final_base_name_len + 1); | ||||
| BLI_strncpy(prev_final_name, prev_final_base_name, prev_final_base_name_len + 1); | BLI_strncpy(prev_final_name, prev_final_base_name, prev_final_base_name_len + 1); | ||||
| if (id_name_final_build(final_name, base_name, prev_final_base_name_len, prev_number + 1) && | if (id_name_final_build(final_name, base_name, prev_final_base_name_len, prev_number + 1) && | ||||
| ▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | while (true) { | ||||
| for (id_test = lb->first; id_test; id_test = id_test->next) { | for (id_test = lb->first; id_test; id_test = id_test->next) { | ||||
| char base_name_test[MAX_ID_NAME - 2]; | char base_name_test[MAX_ID_NAME - 2]; | ||||
| int number_test; | int number_test; | ||||
| if ((id != id_test) && !ID_IS_LINKED(id_test) && (name[0] == id_test->name[2]) && | if ((id != id_test) && !ID_IS_LINKED(id_test) && (name[0] == id_test->name[2]) && | ||||
| (id_test->name[base_name_len + 2] == '.' || id_test->name[base_name_len + 2] == '\0') && | (id_test->name[base_name_len + 2] == '.' || id_test->name[base_name_len + 2] == '\0') && | ||||
| STREQLEN(name, id_test->name + 2, base_name_len) && | STREQLEN(name, id_test->name + 2, base_name_len) && | ||||
| (BLI_split_name_num(base_name_test, &number_test, id_test->name + 2, '.') == | (BLI_split_name_num(base_name_test, &number_test, id_test->name + 2, '.') == | ||||
| base_name_len)) { | base_name_len)) { | ||||
| /* If we did not yet encounter exact same name as the given one, check the remaining parts | /* If we did not yet encounter exact same name as the given one, check the remaining | ||||
| * of the strings. */ | * parts of the strings. */ | ||||
| if (!is_orig_name_used) { | if (!is_orig_name_used) { | ||||
| is_orig_name_used = STREQ(name + base_name_len, id_test->name + 2 + base_name_len); | is_orig_name_used = STREQ(name + base_name_len, id_test->name + 2 + base_name_len); | ||||
| } | } | ||||
| /* Mark number of current id_test name as used, if possible. */ | /* Mark number of current id_test name as used, if possible. */ | ||||
| if (number_test < MAX_NUMBERS_IN_USE) { | if (number_test < MAX_NUMBERS_IN_USE) { | ||||
| ids_in_use[number_test] = id_test; | ids_in_use[number_test] = id_test; | ||||
| } | } | ||||
| /* Keep track of first largest unused number. */ | /* Keep track of first largest unused number. */ | ||||
| if (number <= number_test) { | if (number <= number_test) { | ||||
| *r_id_sorting_hint = id_test; | *r_id_sorting_hint = id_test; | ||||
| number = number_test + 1; | number = number_test + 1; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* If there is no double, we are done. | /* If there is no double, we are done. | ||||
| * Note however that name might have been changed (truncated) in a previous iteration already. | * Note however that name might have been changed (truncated) in a previous iteration | ||||
| * already. | |||||
| */ | */ | ||||
| if (!is_orig_name_used) { | if (!is_orig_name_used) { | ||||
| /* Don't bother updating prev_ static variables here, this case is not supposed to happen | /* Don't bother updating prev_ static variables here, this case is not supposed to happen | ||||
| * that often, and is not straight-forward here, so just ignore and reset them to default. */ | * that often, and is not straight-forward here, so just ignore and reset them to default. | ||||
| */ | |||||
| prev_id_type = ID_LINK_PLACEHOLDER; | prev_id_type = ID_LINK_PLACEHOLDER; | ||||
| prev_final_base_name[0] = '\0'; | prev_final_base_name[0] = '\0'; | ||||
| prev_number = MIN_NUMBER - 1; | prev_number = MIN_NUMBER - 1; | ||||
| /* Value set previously is meaningless in that case. */ | /* Value set previously is meaningless in that case. */ | ||||
| *r_id_sorting_hint = NULL; | *r_id_sorting_hint = NULL; | ||||
| return is_name_changed; | return is_name_changed; | ||||
| } | } | ||||
| /* Decide which value of number to use, either the smallest unused one if possible, or default | /* Decide which value of number to use, either the smallest unused one if possible, or | ||||
| * to the first largest unused one we got from previous loop. */ | * default to the first largest unused one we got from previous loop. */ | ||||
| for (int i = MIN_NUMBER; i < MAX_NUMBERS_IN_USE; i++) { | for (int i = MIN_NUMBER; i < MAX_NUMBERS_IN_USE; i++) { | ||||
| if (ids_in_use[i] == NULL) { | if (ids_in_use[i] == NULL) { | ||||
| number = i; | number = i; | ||||
| if (i > 0) { | if (i > 0) { | ||||
| *r_id_sorting_hint = ids_in_use[i - 1]; | *r_id_sorting_hint = ids_in_use[i - 1]; | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| /* At this point, number is either the lowest unused number within | /* At this point, number is either the lowest unused number within | ||||
| * [MIN_NUMBER .. MAX_NUMBERS_IN_USE - 1], or 1 greater than the largest used number if all | * [MIN_NUMBER .. MAX_NUMBERS_IN_USE - 1], or 1 greater than the largest used number if all | ||||
| * those low ones are taken. | * those low ones are taken. | ||||
| * We can't be bothered to look for the lowest unused number beyond | * We can't be bothered to look for the lowest unused number beyond | ||||
| * (MAX_NUMBERS_IN_USE - 1). | * (MAX_NUMBERS_IN_USE - 1). | ||||
| */ | */ | ||||
| /* We know for wure that name will be changed. */ | /* We know for wure that name will be changed. */ | ||||
| is_name_changed = true; | is_name_changed = true; | ||||
| /* If id_name_final_build helper returns false, it had to truncate further given name, hence we | /* If id_name_final_build helper returns false, it had to truncate further given name, hence | ||||
| * have to go over the whole check again. */ | * we have to go over the whole check again. */ | ||||
| if (!id_name_final_build(name, base_name, base_name_len, number)) { | if (!id_name_final_build(name, base_name, base_name_len, number)) { | ||||
| /* We have to clear our list of small used numbers before we do the whole check again. */ | /* We have to clear our list of small used numbers before we do the whole check again. */ | ||||
| memset(ids_in_use, 0, sizeof(ids_in_use)); | memset(ids_in_use, 0, sizeof(ids_in_use)); | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* Update prev_ static variables, in case next call is for the same type of IDs and with the | /* Update prev_ static variables, in case next call is for the same type of IDs and with the | ||||
| ▲ Show 20 Lines • Show All 703 Lines • Show Last 20 Lines | |||||