Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2020-09-29 13:29:01 +0300
committerJacques Lucke <jacques@blender.org>2020-09-29 13:29:01 +0300
commit6374644fd11797806ab2e92341e5e7d32e94067b (patch)
tree5126cb4de4e7ffd23632b57793751e15f1b89e32 /source/blender/makesdna/intern
parentb41fb5542a72d8277342fead39bc0249e7306fb1 (diff)
DNA: optimize struct reconstruction by doing some preprocessing
When loading large files that are more than a couple weeks old (such that DNA has changed in that time), a significant amount of time is spent in `DNA_struct_reconstruct`. This function takes a struct in the old layout and creates a struct in the new layout from it. This was slow because it was computing the diff between the struct layouts every time a struct is updated. Now the steps for the struct reconstruction is computed only once per struct. This information is then used to actually reconstruct all structs that changed. I measured about 10-20% speedup when loading Spring files. E.g. `10.6s -> 8.7s` for `06_055_A.anim.blend` in BKE_blendfile_read`. This percentage varies a lot based on the number of blocks that have to be reconstructed and how much DNA has changed since they have been written. In none of my tests was the new code slower than the old code. Reviewers: campbellbarton Differential Revision: https://developer.blender.org/D8959
Diffstat (limited to 'source/blender/makesdna/intern')
-rw-r--r--source/blender/makesdna/intern/dna_genfile.c1105
1 files changed, 658 insertions, 447 deletions
diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c
index b140a847188..74f587fe032 100644
--- a/source/blender/makesdna/intern/dna_genfile.c
+++ b/source/blender/makesdna/intern/dna_genfile.c
@@ -611,34 +611,89 @@ void DNA_sdna_current_free(void)
/* ******************* HANDLE DNA ***************** */
/**
- * Used by #DNA_struct_get_compareflags (below) to recursively mark all structs
- * containing a field of type structnr as changed between old and current SDNAs.
+ * This function changes compare_flags[old_struct_index] from SDNA_CMP_UNKNOWN to something else.
+ * It might call itself recursively.
*/
-static void recurs_test_compflags(const SDNA *sdna, char *compflags, int structnr)
+static void set_compare_flags_for_struct(const SDNA *oldsdna,
+ const SDNA *newsdna,
+ char *compare_flags,
+ const int old_struct_index)
{
- /* check all structs, test if it's inside another struct */
- const int typenr = sdna->structs[structnr]->type;
-
- for (int a = 0; a < sdna->structs_len; a++) {
- if (a != structnr && compflags[a] == SDNA_CMP_EQUAL) {
- SDNA_Struct *struct_info = sdna->structs[a];
- for (int b = 0; b < struct_info->members_len; b++) {
- SDNA_StructMember *member = &struct_info->members[b];
- if (member->type == typenr) {
- const char *member_name = sdna->names[member->name];
- if (!ispointer(member_name)) {
- compflags[a] = SDNA_CMP_NOT_EQUAL;
- recurs_test_compflags(sdna, compflags, a);
- }
+ if (compare_flags[old_struct_index] != SDNA_CMP_UNKNOWN) {
+ /* This flag has been initialized already. */
+ return;
+ }
+
+ SDNA_Struct *old_struct = oldsdna->structs[old_struct_index];
+ const char *struct_name = oldsdna->types[old_struct->type];
+
+ const int new_struct_index = DNA_struct_find_nr(newsdna, struct_name);
+ if (new_struct_index == -1) {
+ /* Didn't find a matching new struct, so it has been removed. */
+ compare_flags[old_struct_index] = SDNA_CMP_REMOVED;
+ return;
+ }
+
+ SDNA_Struct *new_struct = newsdna->structs[new_struct_index];
+ if (old_struct->members_len != new_struct->members_len) {
+ /* Structs with a different amount of members are not equal. */
+ compare_flags[old_struct_index] = SDNA_CMP_NOT_EQUAL;
+ return;
+ }
+ if (oldsdna->types_size[old_struct->type] != newsdna->types_size[new_struct->type]) {
+ /* Structs that don't have the same size are not equal. */
+ compare_flags[old_struct_index] = SDNA_CMP_NOT_EQUAL;
+ return;
+ }
+
+ /* Compare each member individually. */
+ for (int member_index = 0; member_index < old_struct->members_len; member_index++) {
+ SDNA_StructMember *old_member = &old_struct->members[member_index];
+ SDNA_StructMember *new_member = &new_struct->members[member_index];
+
+ const char *old_type_name = oldsdna->types[old_member->type];
+ const char *new_type_name = newsdna->types[new_member->type];
+ if (!STREQ(old_type_name, new_type_name)) {
+ /* If two members have a different type in the same place, the structs are not equal. */
+ compare_flags[old_struct_index] = SDNA_CMP_NOT_EQUAL;
+ return;
+ }
+
+ const char *old_member_name = oldsdna->names[old_member->name];
+ const char *new_member_name = newsdna->names[new_member->name];
+ if (!STREQ(old_member_name, new_member_name)) {
+ /* If two members have a different name in the same place, the structs are not equal. */
+ compare_flags[old_struct_index] = SDNA_CMP_NOT_EQUAL;
+ return;
+ }
+
+ if (ispointer(old_member_name)) {
+ if (oldsdna->pointer_size != newsdna->pointer_size) {
+ /* When the struct contains a pointer, and the pointer sizes differ, the structs are not
+ * equal. */
+ compare_flags[old_struct_index] = SDNA_CMP_NOT_EQUAL;
+ return;
+ }
+ }
+ else {
+ const int old_member_struct_index = DNA_struct_find_nr(oldsdna, old_type_name);
+ if (old_member_struct_index >= 0) {
+ set_compare_flags_for_struct(oldsdna, newsdna, compare_flags, old_member_struct_index);
+ if (compare_flags[old_member_struct_index] != SDNA_CMP_EQUAL) {
+ /* If an embedded struct is not equal, the parent struct cannot be equal either. */
+ compare_flags[old_struct_index] = SDNA_CMP_NOT_EQUAL;
+ return;
}
}
}
}
+
+ compare_flags[old_struct_index] = SDNA_CMP_EQUAL;
}
/**
* Constructs and returns an array of byte flags with one element for each struct in oldsdna,
- * indicating how it compares to newsdna:
+ * indicating how it compares to newsdna.
*/
const char *DNA_struct_get_compareflags(const SDNA *oldsdna, const SDNA *newsdna)
{
@@ -647,130 +702,31 @@ const char *DNA_struct_get_compareflags(const SDNA *oldsdna, const SDNA *newsdna
return NULL;
}
- char *compflags = MEM_callocN(oldsdna->structs_len, "compflags");
-
- /* we check all structs in 'oldsdna' and compare them with
- * the structs in 'newsdna'
- */
- unsigned int newsdna_index_last = 0;
+ char *compare_flags = MEM_mallocN(oldsdna->structs_len, "compare flags");
+ memset(compare_flags, SDNA_CMP_UNKNOWN, oldsdna->structs_len);
+ /* Set correct flag for every struct. */
for (int a = 0; a < oldsdna->structs_len; a++) {
- SDNA_Struct *struct_old = oldsdna->structs[a];
-
- /* search for type in cur */
- int sp_new_index = DNA_struct_find_nr_ex(
- newsdna, oldsdna->types[struct_old->type], &newsdna_index_last);
-
- /* The next indices will almost always match */
- newsdna_index_last++;
-
- if (sp_new_index != -1) {
- SDNA_Struct *struct_new = newsdna->structs[sp_new_index];
- /* initial assumption */
- compflags[a] = SDNA_CMP_NOT_EQUAL;
-
- /* compare length and amount of elems */
- if (struct_new->members_len == struct_old->members_len) {
- if (newsdna->types_size[struct_new->type] == oldsdna->types_size[struct_old->type]) {
-
- /* Both structs have the same size and number of members. Now check the individual
- * members. */
- bool all_members_equal = true;
- for (int b = 0; b < struct_old->members_len; b++) {
- SDNA_StructMember *member_old = &struct_old->members[b];
- SDNA_StructMember *member_new = &struct_new->members[b];
-
- const char *type_name_old = oldsdna->types[member_old->type];
- const char *type_name_new = newsdna->types[member_new->type];
- if (!STREQ(type_name_old, type_name_new)) {
- all_members_equal = false;
- break;
- }
-
- const char *member_name_old = oldsdna->names[member_old->name];
- const char *member_name_new = newsdna->names[member_new->name];
- if (!STREQ(member_name_old, member_name_new)) {
- all_members_equal = false;
- break;
- }
-
- if (ispointer(member_name_new)) {
- if (oldsdna->pointer_size != newsdna->pointer_size) {
- all_members_equal = false;
- break;
- }
- }
- }
- if (all_members_equal) {
- /* no differences found */
- compflags[a] = SDNA_CMP_EQUAL;
- }
- }
- }
- }
+ set_compare_flags_for_struct(oldsdna, newsdna, compare_flags, a);
+ BLI_assert(compare_flags[a] != SDNA_CMP_UNKNOWN);
}
- /* first struct in util.h is struct Link, this is skipped in compflags (als # 0).
+ /* first struct in util.h is struct Link, this is skipped in compare_flags (als # 0).
* was a bug, and this way dirty patched! Solve this later....
*/
- compflags[0] = SDNA_CMP_EQUAL;
-
- /* Because structs can be inside structs, we recursively
- * set flags when a struct is altered
- */
- for (int a = 0; a < oldsdna->structs_len; a++) {
- if (compflags[a] == SDNA_CMP_NOT_EQUAL) {
- recurs_test_compflags(oldsdna, compflags, a);
- }
- }
+ compare_flags[0] = SDNA_CMP_EQUAL;
+/* This code can be enabled to see which structs have changed. */
#if 0
for (int a = 0; a < oldsdna->structs_len; a++) {
- if (compflags[a] == SDNA_CMP_NOT_EQUAL) {
+ if (compare_flags[a] == SDNA_CMP_NOT_EQUAL) {
SDNA_Struct *struct_info = oldsdna->structs[a];
printf("changed: %s\n", oldsdna->types[struct_info->type]);
}
}
#endif
- return compflags;
-}
-
-/**
- * Converts the name of a primitive type to its enumeration code.
- */
-static eSDNA_Type sdna_type_nr(const char *dna_type)
-{
- if (STR_ELEM(dna_type, "char", "const char")) {
- return SDNA_TYPE_CHAR;
- }
- if (STR_ELEM(dna_type, "uchar", "unsigned char")) {
- return SDNA_TYPE_UCHAR;
- }
- if (STR_ELEM(dna_type, "short")) {
- return SDNA_TYPE_SHORT;
- }
- if (STR_ELEM(dna_type, "ushort", "unsigned short")) {
- return SDNA_TYPE_USHORT;
- }
- if (STR_ELEM(dna_type, "int")) {
- return SDNA_TYPE_INT;
- }
- if (STR_ELEM(dna_type, "float")) {
- return SDNA_TYPE_FLOAT;
- }
- if (STR_ELEM(dna_type, "double")) {
- return SDNA_TYPE_DOUBLE;
- }
- if (STR_ELEM(dna_type, "int64_t")) {
- return SDNA_TYPE_INT64;
- }
- if (STR_ELEM(dna_type, "uint64_t")) {
- return SDNA_TYPE_UINT64;
- }
- /* invalid! */
-
- return -1;
+ return compare_flags;
}
/**
@@ -778,147 +734,124 @@ static eSDNA_Type sdna_type_nr(const char *dna_type)
* Note there is no optimization for the case where otype and ctype are the same:
* assumption is that caller will handle this case.
*
- * \param ctype: Name of type to convert to
- * \param otype: Name of type to convert from
- * \param name_array_len: Result of #DNA_elem_array_size for this element.
- * \param curdata: Where to put converted data
- * \param olddata: Data of type otype to convert
+ * \param old_type: Type to convert from.
+ * \param new_type: Type to convert to.
+ * \param array_len: Number of elements to convert.
+ * \param old_data: Buffer containing the old values.
+ * \param new_data: Buffer the converted values will be written to.
*/
-static void cast_elem(
- const char *ctype, const char *otype, int name_array_len, char *curdata, const char *olddata)
+static void cast_primitive_type(const eSDNA_Type old_type,
+ const eSDNA_Type new_type,
+ const int array_len,
+ const char *old_data,
+ char *new_data)
{
- eSDNA_Type ctypenr, otypenr;
- if ((otypenr = sdna_type_nr(otype)) == -1 || (ctypenr = sdna_type_nr(ctype)) == -1) {
- return;
- }
-
/* define lengths */
- const int oldlen = DNA_elem_type_size(otypenr);
- const int curlen = DNA_elem_type_size(ctypenr);
+ const int oldlen = DNA_elem_type_size(old_type);
+ const int curlen = DNA_elem_type_size(new_type);
double old_value_f = 0.0;
uint64_t old_value_i = 0;
- while (name_array_len > 0) {
- switch (otypenr) {
+ for (int a = 0; a < array_len; a++) {
+ switch (old_type) {
case SDNA_TYPE_CHAR:
- old_value_i = *olddata;
+ old_value_i = *old_data;
old_value_f = (double)old_value_i;
break;
case SDNA_TYPE_UCHAR:
- old_value_i = *((unsigned char *)olddata);
+ old_value_i = *((unsigned char *)old_data);
old_value_f = (double)old_value_i;
break;
case SDNA_TYPE_SHORT:
- old_value_i = *((short *)olddata);
+ old_value_i = *((short *)old_data);
old_value_f = (double)old_value_i;
break;
case SDNA_TYPE_USHORT:
- old_value_i = *((unsigned short *)olddata);
+ old_value_i = *((unsigned short *)old_data);
old_value_f = (double)old_value_i;
break;
case SDNA_TYPE_INT:
- old_value_i = *((int *)olddata);
+ old_value_i = *((int *)old_data);
old_value_f = (double)old_value_i;
break;
case SDNA_TYPE_FLOAT:
- old_value_f = *((float *)olddata);
+ old_value_f = *((float *)old_data);
old_value_i = (uint64_t)(int64_t)old_value_f;
break;
case SDNA_TYPE_DOUBLE:
- old_value_f = *((double *)olddata);
+ old_value_f = *((double *)old_data);
old_value_i = (uint64_t)(int64_t)old_value_f;
break;
case SDNA_TYPE_INT64:
- old_value_i = (uint64_t) * ((int64_t *)olddata);
+ old_value_i = (uint64_t) * ((int64_t *)old_data);
old_value_f = (double)old_value_i;
break;
case SDNA_TYPE_UINT64:
- old_value_i = *((uint64_t *)olddata);
+ old_value_i = *((uint64_t *)old_data);
old_value_f = (double)old_value_i;
break;
}
- switch (ctypenr) {
+ switch (new_type) {
case SDNA_TYPE_CHAR:
- *curdata = (char)old_value_i;
+ *new_data = (char)old_value_i;
break;
case SDNA_TYPE_UCHAR:
- *((unsigned char *)curdata) = (unsigned char)old_value_i;
+ *((unsigned char *)new_data) = (unsigned char)old_value_i;
break;
case SDNA_TYPE_SHORT:
- *((short *)curdata) = (short)old_value_i;
+ *((short *)new_data) = (short)old_value_i;
break;
case SDNA_TYPE_USHORT:
- *((unsigned short *)curdata) = (unsigned short)old_value_i;
+ *((unsigned short *)new_data) = (unsigned short)old_value_i;
break;
case SDNA_TYPE_INT:
- *((int *)curdata) = (int)old_value_i;
+ *((int *)new_data) = (int)old_value_i;
break;
case SDNA_TYPE_FLOAT:
- if (otypenr < 2) {
+ if (old_type < 2) {
old_value_f /= 255.0;
}
- *((float *)curdata) = old_value_f;
+ *((float *)new_data) = old_value_f;
break;
case SDNA_TYPE_DOUBLE:
- if (otypenr < 2) {
+ if (old_type < 2) {
old_value_f /= 255.0;
}
- *((double *)curdata) = old_value_f;
+ *((double *)new_data) = old_value_f;
break;
case SDNA_TYPE_INT64:
- *((int64_t *)curdata) = (int64_t)old_value_i;
+ *((int64_t *)new_data) = (int64_t)old_value_i;
break;
case SDNA_TYPE_UINT64:
- *((uint64_t *)curdata) = old_value_i;
+ *((uint64_t *)new_data) = old_value_i;
break;
}
- olddata += oldlen;
- curdata += curlen;
- name_array_len--;
+ old_data += oldlen;
+ new_data += curlen;
}
}
-/**
- * Converts pointer values between different sizes. These are only used
- * as lookup keys to identify data blocks in the saved .blend file, not
- * as actual in-memory pointers.
- *
- * \param curlen: Pointer length to convert to
- * \param oldlen: Length of pointers in olddata
- * \param name_array_len: Result of #DNA_elem_array_size for this element.
- * \param curdata: Where to put converted data
- * \param olddata: Data to convert
- */
-static void cast_pointer(
- int curlen, int oldlen, int name_array_len, char *curdata, const char *olddata)
+static void cast_pointer_32_to_64(const int array_len,
+ const uint32_t *old_data,
+ uint64_t *new_data)
{
- while (name_array_len > 0) {
-
- if (curlen == oldlen) {
- memcpy(curdata, olddata, curlen);
- }
- else if (curlen == 4 && oldlen == 8) {
- int64_t lval = *((int64_t *)olddata);
-
- /* WARNING: 32-bit Blender trying to load file saved by 64-bit Blender,
- * pointers may lose uniqueness on truncation! (Hopefully this wont
- * happen unless/until we ever get to multi-gigabyte .blend files...) */
- *((int *)curdata) = lval >> 3;
- }
- else if (curlen == 8 && oldlen == 4) {
- *((int64_t *)curdata) = *((int *)olddata);
- }
- else {
- /* for debug */
- printf("errpr: illegal pointersize!\n");
- }
+ for (int a = 0; a < array_len; a++) {
+ new_data[a] = old_data[a];
+ }
+}
- olddata += oldlen;
- curdata += curlen;
- name_array_len--;
+static void cast_pointer_64_to_32(const int array_len,
+ const uint64_t *old_data,
+ uint32_t *new_data)
+{
+ /* WARNING: 32-bit Blender trying to load file saved by 64-bit Blender,
+ * pointers may lose uniqueness on truncation! (Hopefully this wont
+ * happen unless/until we ever get to multi-gigabyte .blend files...) */
+ for (int a = 0; a < array_len; a++) {
+ new_data[a] = old_data[a] >> 3;
}
}
@@ -1060,216 +993,6 @@ static const char *find_elem(const SDNA *sdna,
}
/**
- * Converts the contents of a single field of a struct, of a non-struct type,
- * from \a oldsdna to \a newsdna format.
- *
- * \param newsdna: SDNA of current Blender
- * \param oldsdna: SDNA of Blender that saved file
- * \param type: current field type name
- * \param new_name_nr: current field name number.
- * \param curdata: put field data converted to newsdna here
- * \param old: pointer to struct info in oldsdna
- * \param olddata: struct contents laid out according to oldsdna
- */
-static void reconstruct_elem(const SDNA *newsdna,
- const SDNA *oldsdna,
- const char *type,
- const int new_name_nr,
- char *curdata,
- const SDNA_Struct *old,
- const char *olddata)
-{
- /* rules: test for NAME:
- * - name equal:
- * - cast type
- * - name partially equal (array differs)
- * - type equal: memcpy
- * - type cast (per element).
- * (nzc 2-4-2001 I want the 'unsigned' bit to be parsed as well. Where
- * can I force this?)
- */
-
- /* is 'name' an array? */
- const char *name = newsdna->names[new_name_nr];
- const char *cp = name;
- int countpos = 0;
- while (*cp && *cp != '[') {
- cp++;
- countpos++;
- }
- if (*cp != '[') {
- countpos = 0;
- }
-
- /* in old is the old struct */
- for (int a = 0; a < old->members_len; a++) {
- const SDNA_StructMember *old_member = &old->members[a];
- const int old_name_nr = old_member->name;
- const char *otype = oldsdna->types[old_member->type];
- const char *oname = oldsdna->names[old_member->name];
- const int len = DNA_elem_size_nr(oldsdna, old_member->type, old_member->name);
-
- if (STREQ(name, oname)) { /* name equal */
-
- if (ispointer(name)) { /* pointer of functionpointer afhandelen */
- cast_pointer(newsdna->pointer_size,
- oldsdna->pointer_size,
- newsdna->names_array_len[new_name_nr],
- curdata,
- olddata);
- }
- else if (STREQ(type, otype)) { /* type equal */
- memcpy(curdata, olddata, len);
- }
- else {
- cast_elem(type, otype, newsdna->names_array_len[new_name_nr], curdata, olddata);
- }
-
- return;
- }
- if (countpos != 0) { /* name is an array */
-
- if (oname[countpos] == '[' && strncmp(name, oname, countpos) == 0) { /* basis equal */
- const int new_name_array_len = newsdna->names_array_len[new_name_nr];
- const int old_name_array_len = oldsdna->names_array_len[old_name_nr];
- const int min_name_array_len = MIN2(new_name_array_len, old_name_array_len);
-
- if (ispointer(name)) { /* handle pointer or functionpointer */
- cast_pointer(
- newsdna->pointer_size, oldsdna->pointer_size, min_name_array_len, curdata, olddata);
- }
- else if (STREQ(type, otype)) { /* type equal */
- /* size of single old array element */
- int mul = len / old_name_array_len;
- /* smaller of sizes of old and new arrays */
- mul *= min_name_array_len;
-
- memcpy(curdata, olddata, mul);
-
- if (old_name_array_len > new_name_array_len && STREQ(type, "char")) {
- /* string had to be truncated, ensure it's still null-terminated */
- curdata[mul - 1] = '\0';
- }
- }
- else {
- cast_elem(type, otype, min_name_array_len, curdata, olddata);
- }
- return;
- }
- }
- olddata += len;
- }
-}
-
-/**
- * Converts the contents of an entire struct from oldsdna to newsdna format.
- *
- * \param newsdna: SDNA of current Blender
- * \param oldsdna: SDNA of Blender that saved file
- * \param compflags:
- *
- * Result from DNA_struct_get_compareflags to avoid needless conversions.
- * \param oldSDNAnr: Index of old struct definition in oldsdna
- * \param data: Struct contents laid out according to oldsdna
- * \param curSDNAnr: Index of current struct definition in newsdna
- * \param cur: Where to put converted struct contents
- */
-static void reconstruct_struct(const SDNA *newsdna,
- const SDNA *oldsdna,
- const char *compflags,
-
- int oldSDNAnr,
- const char *data,
- int curSDNAnr,
- char *cur)
-{
- /* Recursive!
- * Per element from cur_struct, read data from old_struct.
- * If element is a struct, call recursive.
- */
-
- if (oldSDNAnr == -1) {
- return;
- }
- if (curSDNAnr == -1) {
- return;
- }
-
- if (compflags[oldSDNAnr] == SDNA_CMP_EQUAL) {
- /* if recursive: test for equal */
- const SDNA_Struct *struct_old = oldsdna->structs[oldSDNAnr];
- const int elen = oldsdna->types_size[struct_old->type];
- memcpy(cur, data, elen);
-
- return;
- }
-
- const int firststructtypenr = newsdna->structs[0]->type;
-
- const SDNA_Struct *struct_old = oldsdna->structs[oldSDNAnr];
- const SDNA_Struct *struct_new = newsdna->structs[curSDNAnr];
-
- char *cpc = cur;
- for (int a = 0; a < struct_new->members_len; a++) { /* convert each field */
- const SDNA_StructMember *member_new = &struct_new->members[a];
- const char *type = newsdna->types[member_new->type];
- const char *name = newsdna->names[member_new->name];
-
- int elen = DNA_elem_size_nr(newsdna, member_new->type, member_new->name);
-
- /* Skip pad bytes which must start with '_pad', see makesdna.c 'is_name_legal'.
- * for exact rules. Note that if we fail to skip a pad byte it's harmless,
- * this just avoids unnecessary reconstruction. */
- if (name[0] == '_' || (name[0] == '*' && name[1] == '_')) {
- cpc += elen;
- }
- else if (member_new->type >= firststructtypenr && !ispointer(name)) {
- /* struct field type */
-
- /* where does the old struct data start (and is there an old one?) */
- const SDNA_StructMember *member_old;
- const char *cpo = find_elem(oldsdna, type, name, struct_old, data, &member_old);
-
- if (cpo) {
- unsigned int oldsdna_index_last = UINT_MAX;
- unsigned int cursdna_index_last = UINT_MAX;
- oldSDNAnr = DNA_struct_find_nr_ex(oldsdna, type, &oldsdna_index_last);
- curSDNAnr = DNA_struct_find_nr_ex(newsdna, type, &cursdna_index_last);
-
- /* array! */
- int mul = newsdna->names_array_len[member_new->name];
- int mulo = oldsdna->names_array_len[member_old->name];
-
- int eleno = DNA_elem_size_nr(oldsdna, member_old->type, member_old->name);
-
- elen /= mul;
- eleno /= mulo;
-
- while (mul--) {
- reconstruct_struct(newsdna, oldsdna, compflags, oldSDNAnr, cpo, curSDNAnr, cpc);
- cpo += eleno;
- cpc += elen;
-
- /* new struct array larger than old */
- mulo--;
- if (mulo <= 0) {
- break;
- }
- }
- }
- else {
- cpc += elen; /* skip field no longer present */
- }
- }
- else {
- /* non-struct field type */
- reconstruct_elem(newsdna, oldsdna, type, member_new->name, cpc, struct_old, data);
- cpc += elen;
- }
- }
-}
-
-/**
* Does endian swapping on the fields of a struct value.
*
* \param oldsdna: SDNA of Blender that saved file
@@ -1352,50 +1075,538 @@ void DNA_struct_switch_endian(const SDNA *oldsdna, int oldSDNAnr, char *data)
}
}
+typedef enum eReconstructStepType {
+ RECONSTRUCT_STEP_MEMCPY,
+ RECONSTRUCT_STEP_CAST_PRIMITIVE,
+ RECONSTRUCT_STEP_CAST_POINTER_TO_32,
+ RECONSTRUCT_STEP_CAST_POINTER_TO_64,
+ RECONSTRUCT_STEP_SUBSTRUCT,
+ RECONSTRUCT_STEP_INIT_ZERO,
+} eReconstructStepType;
+
+typedef struct ReconstructStep {
+ eReconstructStepType type;
+ union {
+ struct {
+ int old_offset;
+ int new_offset;
+ int size;
+ } memcpy;
+ struct {
+ int old_offset;
+ int new_offset;
+ int array_len;
+ eSDNA_Type old_type;
+ eSDNA_Type new_type;
+ } cast_primitive;
+ struct {
+ int old_offset;
+ int new_offset;
+ int array_len;
+ } cast_pointer;
+ struct {
+ int old_offset;
+ int new_offset;
+ int array_len;
+ short old_struct_nr;
+ short new_struct_nr;
+ } substruct;
+ } data;
+} ReconstructStep;
+
+typedef struct DNA_ReconstructInfo {
+ const SDNA *oldsdna;
+ const SDNA *newsdna;
+ const char *compare_flags;
+
+ int *step_counts;
+ ReconstructStep **steps;
+} DNA_ReconstructInfo;
+
+static void reconstruct_structs(const DNA_ReconstructInfo *reconstruct_info,
+ const int blocks,
+ const int old_struct_nr,
+ const int new_struct_nr,
+ const char *old_blocks,
+ char *new_blocks);
+
/**
- * \param newsdna: SDNA of current Blender
- * \param oldsdna: SDNA of Blender that saved file
- * \param compflags:
+ * Converts the contents of an entire struct from oldsdna to newsdna format.
*
- * Result from DNA_struct_get_compareflags to avoid needless conversions
- * \param oldSDNAnr: Index of struct info within oldsdna
- * \param blocks: The number of array elements
- * \param data: Array of struct data
- * \return An allocated reconstructed struct
+ * \param reconstruct_info: Preprocessed reconstruct information generated by
+ * #DNA_reconstruct_info_create.
+ * \param new_struct_nr: Index in newsdna->structs of the struct that is being reconstructed.
+ * \param old_block: Memory buffer containing the old struct.
+ * \param new_block: Where to put converted struct contents.
*/
-void *DNA_struct_reconstruct(const SDNA *newsdna,
- const SDNA *oldsdna,
- const char *compflags,
- int oldSDNAnr,
+static void reconstruct_struct(const DNA_ReconstructInfo *reconstruct_info,
+ const int new_struct_nr,
+ const char *old_block,
+ char *new_block)
+{
+ const ReconstructStep *steps = reconstruct_info->steps[new_struct_nr];
+ const int step_count = reconstruct_info->step_counts[new_struct_nr];
+
+ /* Execute all preprocessed steps. */
+ for (int a = 0; a < step_count; a++) {
+ const ReconstructStep *step = &steps[a];
+ switch (step->type) {
+ case RECONSTRUCT_STEP_MEMCPY:
+ memcpy(new_block + step->data.memcpy.new_offset,
+ old_block + step->data.memcpy.old_offset,
+ step->data.memcpy.size);
+ break;
+ case RECONSTRUCT_STEP_CAST_PRIMITIVE:
+ cast_primitive_type(step->data.cast_primitive.old_type,
+ step->data.cast_primitive.new_type,
+ step->data.cast_primitive.array_len,
+ old_block + step->data.cast_primitive.old_offset,
+ new_block + step->data.cast_primitive.new_offset);
+ break;
+ case RECONSTRUCT_STEP_CAST_POINTER_TO_32:
+ cast_pointer_64_to_32(step->data.cast_pointer.array_len,
+ (const uint64_t *)(old_block + step->data.cast_pointer.old_offset),
+ (uint32_t *)(new_block + step->data.cast_pointer.new_offset));
+ break;
+ case RECONSTRUCT_STEP_CAST_POINTER_TO_64:
+ cast_pointer_32_to_64(step->data.cast_pointer.array_len,
+ (const uint32_t *)(old_block + step->data.cast_pointer.old_offset),
+ (uint64_t *)(new_block + step->data.cast_pointer.new_offset));
+ break;
+ case RECONSTRUCT_STEP_SUBSTRUCT:
+ reconstruct_structs(reconstruct_info,
+ step->data.substruct.array_len,
+ step->data.substruct.old_struct_nr,
+ step->data.substruct.new_struct_nr,
+ old_block + step->data.substruct.old_offset,
+ new_block + step->data.substruct.new_offset);
+ break;
+ case RECONSTRUCT_STEP_INIT_ZERO:
+ /* Do nothing, because the memory block has been calloced. */
+ break;
+ }
+ }
+}
+
+/** Reconstructs an array of structs. */
+static void reconstruct_structs(const DNA_ReconstructInfo *reconstruct_info,
+ const int blocks,
+ const int old_struct_nr,
+ const int new_struct_nr,
+ const char *old_blocks,
+ char *new_blocks)
+{
+ const SDNA_Struct *old_struct = reconstruct_info->oldsdna->structs[old_struct_nr];
+ const SDNA_Struct *new_struct = reconstruct_info->newsdna->structs[new_struct_nr];
+
+ const int old_block_size = reconstruct_info->oldsdna->types_size[old_struct->type];
+ const int new_block_size = reconstruct_info->newsdna->types_size[new_struct->type];
+
+ for (int a = 0; a < blocks; a++) {
+ const char *old_block = old_blocks + a * old_block_size;
+ char *new_block = new_blocks + a * new_block_size;
+ reconstruct_struct(reconstruct_info, new_struct_nr, old_block, new_block);
+ }
+}
+
+/**
+ * \param reconstruct_info: Information preprocessed by #DNA_reconstruct_info_create.
+ * \param old_struct_nr: Index of struct info within oldsdna.
+ * \param blocks: The number of array elements.
+ * \param old_blocks: Array of struct data.
+ * \return An allocated reconstructed struct.
+ */
+void *DNA_struct_reconstruct(const DNA_ReconstructInfo *reconstruct_info,
+ int old_struct_nr,
int blocks,
- const void *data)
+ const void *old_blocks)
{
- /* oldSDNAnr == structnr, we're looking for the corresponding 'cur' number */
- const SDNA_Struct *struct_old = oldsdna->structs[oldSDNAnr];
- const char *type = oldsdna->types[struct_old->type];
- const int oldlen = oldsdna->types_size[struct_old->type];
- const int curSDNAnr = DNA_struct_find_nr(newsdna, type);
-
- /* init data and alloc */
- int curlen = 0;
- if (curSDNAnr != -1) {
- const SDNA_Struct *struct_new = newsdna->structs[curSDNAnr];
- curlen = newsdna->types_size[struct_new->type];
- }
- if (curlen == 0) {
+ const SDNA *oldsdna = reconstruct_info->oldsdna;
+ const SDNA *newsdna = reconstruct_info->newsdna;
+
+ const SDNA_Struct *old_struct = oldsdna->structs[old_struct_nr];
+ const char *type_name = oldsdna->types[old_struct->type];
+ const int new_struct_nr = DNA_struct_find_nr(newsdna, type_name);
+
+ if (new_struct_nr == -1) {
return NULL;
}
- char *cur = MEM_callocN(blocks * curlen, "reconstruct");
- char *cpc = cur;
- const char *cpo = data;
- for (int a = 0; a < blocks; a++) {
- reconstruct_struct(newsdna, oldsdna, compflags, oldSDNAnr, cpo, curSDNAnr, cpc);
- cpc += curlen;
- cpo += oldlen;
+ const SDNA_Struct *new_struct = newsdna->structs[new_struct_nr];
+ const int new_block_size = newsdna->types_size[new_struct->type];
+
+ char *new_blocks = MEM_callocN(blocks * new_block_size, "reconstruct");
+ reconstruct_structs(
+ reconstruct_info, blocks, old_struct_nr, new_struct_nr, old_blocks, new_blocks);
+ return new_blocks;
+}
+
+/* Each struct member belongs to one of the categories below. */
+typedef enum eStructMemberCategory {
+ STRUCT_MEMBER_CATEGORY_STRUCT,
+ STRUCT_MEMBER_CATEGORY_PRIMITIVE,
+ STRUCT_MEMBER_CATEGORY_POINTER,
+} eStructMemberCategory;
+
+static eStructMemberCategory get_struct_member_category(const SDNA *sdna,
+ const SDNA_StructMember *member)
+{
+ const char *member_name = sdna->names[member->name];
+ if (ispointer(member_name)) {
+ return STRUCT_MEMBER_CATEGORY_POINTER;
+ }
+ const char *member_type_name = sdna->types[member->type];
+ if (DNA_struct_find(sdna, member_type_name)) {
+ return STRUCT_MEMBER_CATEGORY_STRUCT;
+ }
+ return STRUCT_MEMBER_CATEGORY_PRIMITIVE;
+}
+
+static int get_member_size_in_bytes(const SDNA *sdna, const SDNA_StructMember *member)
+{
+ const char *name = sdna->names[member->name];
+ const int array_length = sdna->names_array_len[member->name];
+ if (ispointer(name)) {
+ return sdna->pointer_size * array_length;
+ }
+ const int type_size = sdna->types_size[member->type];
+ return type_size * array_length;
+}
+
+/** Finds a member in the given struct with the given name. */
+static const SDNA_StructMember *find_member_with_matching_name(const SDNA *sdna,
+ const SDNA_Struct *struct_info,
+ const char *name,
+ int *r_offset)
+{
+ int offset = 0;
+ for (int a = 0; a < struct_info->members_len; a++) {
+ const SDNA_StructMember *member = &struct_info->members[a];
+ const char *member_name = sdna->names[member->name];
+ if (elem_streq(name, member_name)) {
+ *r_offset = offset;
+ return member;
+ }
+ offset += get_member_size_in_bytes(sdna, member);
+ }
+ return NULL;
+}
+
+/** Initializes a single reconstruct step for a member in the new struct. */
+static void init_reconstruct_step_for_member(const SDNA *oldsdna,
+ const SDNA *newsdna,
+ const char *compare_flags,
+ const SDNA_Struct *old_struct,
+ const SDNA_StructMember *new_member,
+ const int new_member_offset,
+ ReconstructStep *r_step)
+{
+
+ /* Find the matching old member. */
+ int old_member_offset;
+ const char *new_name = newsdna->names[new_member->name];
+ const SDNA_StructMember *old_member = find_member_with_matching_name(
+ oldsdna, old_struct, new_name, &old_member_offset);
+
+ if (old_member == NULL) {
+ /* No matching member has been found in the old struct. */
+ r_step->type = RECONSTRUCT_STEP_INIT_ZERO;
+ return;
+ }
+
+ /* Determine the member category of the old an new members. */
+ const eStructMemberCategory new_category = get_struct_member_category(newsdna, new_member);
+ const eStructMemberCategory old_category = get_struct_member_category(oldsdna, old_member);
+
+ if (new_category != old_category) {
+ /* Can only reconstruct the new member based on the old member, when the belong to the same
+ * category. */
+ r_step->type = RECONSTRUCT_STEP_INIT_ZERO;
+ return;
}
- return cur;
+ const int new_array_length = newsdna->names_array_len[new_member->name];
+ const int old_array_length = oldsdna->names_array_len[old_member->name];
+ const int shared_array_length = MIN2(new_array_length, old_array_length);
+
+ const char *new_type_name = newsdna->types[new_member->type];
+ const char *old_type_name = oldsdna->types[old_member->type];
+
+ switch (new_category) {
+ case STRUCT_MEMBER_CATEGORY_STRUCT: {
+ if (STREQ(new_type_name, old_type_name)) {
+ const int old_struct_nr = DNA_struct_find_nr(oldsdna, old_type_name);
+ BLI_assert(old_struct_nr != -1);
+ enum eSDNA_StructCompare compare_flag = compare_flags[old_struct_nr];
+ BLI_assert(compare_flag != SDNA_CMP_REMOVED);
+ if (compare_flag == SDNA_CMP_EQUAL) {
+ /* The old and new members are identical, just do a memcpy. */
+ r_step->type = RECONSTRUCT_STEP_MEMCPY;
+ r_step->data.memcpy.new_offset = new_member_offset;
+ r_step->data.memcpy.old_offset = old_member_offset;
+ r_step->data.memcpy.size = newsdna->types_size[new_member->type] * shared_array_length;
+ }
+ else {
+ const int new_struct_nr = DNA_struct_find_nr(newsdna, new_type_name);
+ BLI_assert(new_struct_nr != -1);
+
+ /* The old and new members are different, use recursion to reconstruct the
+ * nested struct. */
+ BLI_assert(compare_flag == SDNA_CMP_NOT_EQUAL);
+ r_step->type = RECONSTRUCT_STEP_SUBSTRUCT;
+ r_step->data.substruct.new_offset = new_member_offset;
+ r_step->data.substruct.old_offset = old_member_offset;
+ r_step->data.substruct.array_len = shared_array_length;
+ r_step->data.substruct.new_struct_nr = new_struct_nr;
+ r_step->data.substruct.old_struct_nr = old_struct_nr;
+ }
+ }
+ else {
+ /* Cannot match structs that have different names. */
+ r_step->type = RECONSTRUCT_STEP_INIT_ZERO;
+ }
+ break;
+ }
+ case STRUCT_MEMBER_CATEGORY_PRIMITIVE: {
+ if (STREQ(new_type_name, old_type_name)) {
+ /* Primitives with the same name cannot be different, so just do a memcpy. */
+ r_step->type = RECONSTRUCT_STEP_MEMCPY;
+ r_step->data.memcpy.new_offset = new_member_offset;
+ r_step->data.memcpy.old_offset = old_member_offset;
+ r_step->data.memcpy.size = newsdna->types_size[new_member->type] * shared_array_length;
+ }
+ else {
+ /* The old and new primitive types are different, cast from the old to new type. */
+ r_step->type = RECONSTRUCT_STEP_CAST_PRIMITIVE;
+ r_step->data.cast_primitive.array_len = shared_array_length;
+ r_step->data.cast_primitive.new_offset = new_member_offset;
+ r_step->data.cast_primitive.old_offset = old_member_offset;
+ r_step->data.cast_primitive.new_type = new_member->type;
+ r_step->data.cast_primitive.old_type = old_member->type;
+ }
+ break;
+ }
+ case STRUCT_MEMBER_CATEGORY_POINTER: {
+ if (newsdna->pointer_size == oldsdna->pointer_size) {
+ /* The pointer size is the same, so just do a memcpy. */
+ r_step->type = RECONSTRUCT_STEP_MEMCPY;
+ r_step->data.memcpy.new_offset = new_member_offset;
+ r_step->data.memcpy.old_offset = old_member_offset;
+ r_step->data.memcpy.size = newsdna->pointer_size * shared_array_length;
+ }
+ else if (newsdna->pointer_size == 8 && oldsdna->pointer_size == 4) {
+ /* Need to convert from 32 bit to 64 bit pointers. */
+ r_step->type = RECONSTRUCT_STEP_CAST_POINTER_TO_64;
+ r_step->data.cast_pointer.new_offset = new_member_offset;
+ r_step->data.cast_pointer.old_offset = old_member_offset;
+ r_step->data.cast_pointer.array_len = shared_array_length;
+ }
+ else if (newsdna->pointer_size == 4 && oldsdna->pointer_size == 8) {
+ /* Need to convert from 64 bit to 32 bit pointers. */
+ r_step->type = RECONSTRUCT_STEP_CAST_POINTER_TO_32;
+ r_step->data.cast_pointer.new_offset = new_member_offset;
+ r_step->data.cast_pointer.old_offset = old_member_offset;
+ r_step->data.cast_pointer.array_len = shared_array_length;
+ }
+ else {
+ BLI_assert(!"invalid pointer size");
+ r_step->type = RECONSTRUCT_STEP_INIT_ZERO;
+ }
+ break;
+ }
+ }
+}
+
+/** Useful function when debugging the reconstruct steps. */
+static void print_reconstruct_step(ReconstructStep *step, const SDNA *oldsdna, const SDNA *newsdna)
+{
+ switch (step->type) {
+ case RECONSTRUCT_STEP_INIT_ZERO: {
+ printf("init zero");
+ break;
+ }
+ case RECONSTRUCT_STEP_MEMCPY: {
+ printf("memcpy, size: %d, old offset: %d, new offset: %d",
+ step->data.memcpy.size,
+ step->data.memcpy.old_offset,
+ step->data.memcpy.new_offset);
+ break;
+ }
+ case RECONSTRUCT_STEP_CAST_PRIMITIVE: {
+ printf(
+ "cast element, old type: %d ('%s'), new type: %d ('%s'), old offset: %d, new offset: "
+ "%d, length: %d",
+ (int)step->data.cast_primitive.old_type,
+ oldsdna->types[step->data.cast_primitive.old_type],
+ (int)step->data.cast_primitive.new_type,
+ newsdna->types[step->data.cast_primitive.new_type],
+ step->data.cast_primitive.old_offset,
+ step->data.cast_primitive.new_offset,
+ step->data.cast_primitive.array_len);
+ break;
+ }
+ case RECONSTRUCT_STEP_CAST_POINTER_TO_32: {
+ printf("pointer to 32, old offset: %d, new offset: %d, length: %d",
+ step->data.cast_pointer.old_offset,
+ step->data.cast_pointer.new_offset,
+ step->data.cast_pointer.array_len);
+ break;
+ }
+ case RECONSTRUCT_STEP_CAST_POINTER_TO_64: {
+ printf("pointer to 64, old offset: %d, new offset: %d, length: %d",
+ step->data.cast_pointer.old_offset,
+ step->data.cast_pointer.new_offset,
+ step->data.cast_pointer.array_len);
+ break;
+ }
+ case RECONSTRUCT_STEP_SUBSTRUCT: {
+ printf(
+ "substruct, old offset: %d, new offset: %d, new struct: %d ('%s', size per struct: %d), "
+ "length: %d",
+ step->data.substruct.old_offset,
+ step->data.substruct.new_offset,
+ step->data.substruct.new_struct_nr,
+ newsdna->types[newsdna->structs[step->data.substruct.new_struct_nr]->type],
+ newsdna->types_size[newsdna->structs[step->data.substruct.new_struct_nr]->type],
+ step->data.substruct.array_len);
+ break;
+ }
+ }
+}
+
+/**
+ * Generate an array of reconstruct steps for the given #new_struct. There will be one
+ * reconstruct step for every member.
+ */
+static ReconstructStep *create_reconstruct_steps_for_struct(const SDNA *oldsdna,
+ const SDNA *newsdna,
+ const char *compare_flags,
+ const SDNA_Struct *old_struct,
+ const SDNA_Struct *new_struct)
+{
+ ReconstructStep *steps = MEM_calloc_arrayN(
+ new_struct->members_len, sizeof(ReconstructStep), __func__);
+
+ int new_member_offset = 0;
+ for (int new_member_index = 0; new_member_index < new_struct->members_len; new_member_index++) {
+ const SDNA_StructMember *new_member = &new_struct->members[new_member_index];
+ init_reconstruct_step_for_member(oldsdna,
+ newsdna,
+ compare_flags,
+ old_struct,
+ new_member,
+ new_member_offset,
+ &steps[new_member_index]);
+ new_member_offset += get_member_size_in_bytes(newsdna, new_member);
+ }
+
+ return steps;
+}
+
+/** Compresses an array of reconstruct steps in-place and returns the new step count. */
+static int compress_reconstruct_steps(ReconstructStep *steps, const int old_step_count)
+{
+ int new_step_count = 0;
+ for (int a = 0; a < old_step_count; a++) {
+ ReconstructStep *step = &steps[a];
+ switch (step->type) {
+ case RECONSTRUCT_STEP_INIT_ZERO:
+ /* These steps are simply removed. */
+ break;
+ case RECONSTRUCT_STEP_MEMCPY:
+ if (new_step_count > 0) {
+ /* Try to merge this memcpy step with the previous one. */
+ ReconstructStep *prev_step = &steps[new_step_count - 1];
+ if (prev_step->type == RECONSTRUCT_STEP_MEMCPY) {
+ /* Check if there are no bytes between the blocks to copy. */
+ if (prev_step->data.memcpy.old_offset + prev_step->data.memcpy.size ==
+ step->data.memcpy.old_offset &&
+ prev_step->data.memcpy.new_offset + prev_step->data.memcpy.size ==
+ step->data.memcpy.new_offset) {
+ prev_step->data.memcpy.size += step->data.memcpy.size;
+ break;
+ }
+ }
+ }
+ steps[new_step_count] = *step;
+ new_step_count++;
+ break;
+ case RECONSTRUCT_STEP_CAST_PRIMITIVE:
+ case RECONSTRUCT_STEP_CAST_POINTER_TO_32:
+ case RECONSTRUCT_STEP_CAST_POINTER_TO_64:
+ case RECONSTRUCT_STEP_SUBSTRUCT:
+ /* These steps are not changed at all for now. It should be possible to merge consecutive
+ * steps of the same type, but it is not really worth it. */
+ steps[new_step_count] = *step;
+ new_step_count++;
+ break;
+ }
+ }
+ return new_step_count;
+}
+
+/**
+ * Preprocess information about how structs in newsdna can be reconstructed from structs in
+ * oldsdna. This information is then used to speedup #DNA_struct_reconstruct.
+ */
+DNA_ReconstructInfo *DNA_reconstruct_info_create(const SDNA *oldsdna,
+ const SDNA *newsdna,
+ const char *compare_flags)
+{
+ DNA_ReconstructInfo *reconstruct_info = MEM_callocN(sizeof(DNA_ReconstructInfo), __func__);
+ reconstruct_info->oldsdna = oldsdna;
+ reconstruct_info->newsdna = newsdna;
+ reconstruct_info->compare_flags = compare_flags;
+ reconstruct_info->step_counts = MEM_malloc_arrayN(sizeof(int), newsdna->structs_len, __func__);
+ reconstruct_info->steps = MEM_malloc_arrayN(
+ sizeof(ReconstructStep *), newsdna->structs_len, __func__);
+
+ /* Generate reconstruct steps for all structs. */
+ for (int new_struct_nr = 0; new_struct_nr < newsdna->structs_len; new_struct_nr++) {
+ const SDNA_Struct *new_struct = newsdna->structs[new_struct_nr];
+ const char *new_struct_name = newsdna->types[new_struct->type];
+ const int old_struct_nr = DNA_struct_find_nr(oldsdna, new_struct_name);
+ if (old_struct_nr < 0) {
+ reconstruct_info->steps[new_struct_nr] = NULL;
+ reconstruct_info->step_counts[new_struct_nr] = 0;
+ continue;
+ }
+ const SDNA_Struct *old_struct = oldsdna->structs[old_struct_nr];
+ ReconstructStep *steps = create_reconstruct_steps_for_struct(
+ oldsdna, newsdna, compare_flags, old_struct, new_struct);
+
+ int steps_len = new_struct->members_len;
+ /* Comment the line below to skip the compression for debugging purposes. */
+ steps_len = compress_reconstruct_steps(steps, new_struct->members_len);
+
+ reconstruct_info->steps[new_struct_nr] = steps;
+ reconstruct_info->step_counts[new_struct_nr] = steps_len;
+
+/* This is useful when debugging the reconstruct steps. */
+#if 0
+ printf("%s: \n", new_struct_name);
+ for (int a = 0; a < steps_len; a++) {
+ printf(" ");
+ print_reconstruct_step(&steps[a], oldsdna, newsdna);
+ printf("\n");
+ }
+#endif
+ UNUSED_VARS(print_reconstruct_step);
+ }
+
+ return reconstruct_info;
+}
+
+void DNA_reconstruct_info_free(DNA_ReconstructInfo *reconstruct_info)
+{
+ for (int a = 0; a < reconstruct_info->newsdna->structs_len; a++) {
+ if (reconstruct_info->steps[a] != NULL) {
+ MEM_freeN(reconstruct_info->steps[a]);
+ }
+ }
+ MEM_freeN(reconstruct_info->steps);
+ MEM_freeN(reconstruct_info->step_counts);
+ MEM_freeN(reconstruct_info);
}
/**