Cleanup: rename IDP_FreeProperty_ex to IDP_FreePropertyContent_ex
[blender.git] / source / blender / blenkernel / intern / idprop.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup bke
22  */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stddef.h>
27 #include <string.h>
28
29 #include "BLI_utildefines.h"
30 #include "BLI_string.h"
31 #include "BLI_listbase.h"
32 #include "BLI_math.h"
33
34 #include "BKE_idprop.h"
35 #include "BKE_library.h"
36
37 #include "CLG_log.h"
38
39 #include "MEM_guardedalloc.h"
40
41 #include "BLI_strict_flags.h"
42
43 /* IDPropertyTemplate is a union in DNA_ID.h */
44
45 /**
46  * if the new is 'IDP_ARRAY_REALLOC_LIMIT' items less,
47  * than #IDProperty.totallen, reallocate anyway.
48  */
49 #define IDP_ARRAY_REALLOC_LIMIT 200
50
51 static CLG_LogRef LOG = {"bke.idprop"};
52
53 /*local size table.*/
54 static size_t idp_size_table[] = {
55     1, /*strings*/
56     sizeof(int),
57     sizeof(float),
58     sizeof(float) * 3,  /*Vector type, deprecated*/
59     sizeof(float) * 16, /*Matrix type, deprecated*/
60     0,                  /*arrays don't have a fixed size*/
61     sizeof(ListBase),   /*Group type*/
62     sizeof(void *),
63     sizeof(double),
64 };
65
66 /* -------------------------------------------------------------------- */
67 /* Array Functions */
68
69 /** \name IDP Array API
70  * \{ */
71
72 #define GETPROP(prop, i) &(IDP_IDPArray(prop)[i])
73
74 /* --------- property array type -------------*/
75
76 /**
77  * \note as a start to move away from the stupid IDP_New function, this type
78  * has it's own allocation function.
79  */
80 IDProperty *IDP_NewIDPArray(const char *name)
81 {
82   IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty prop array");
83   prop->type = IDP_IDPARRAY;
84   prop->len = 0;
85   BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
86
87   return prop;
88 }
89
90 IDProperty *IDP_CopyIDPArray(const IDProperty *array, const int flag)
91 {
92   /* don't use MEM_dupallocN because this may be part of an array */
93   IDProperty *narray, *tmp;
94   int i;
95
96   BLI_assert(array->type == IDP_IDPARRAY);
97
98   narray = MEM_mallocN(sizeof(IDProperty), __func__);
99   *narray = *array;
100
101   narray->data.pointer = MEM_dupallocN(array->data.pointer);
102   for (i = 0; i < narray->len; i++) {
103     /* ok, the copy functions always allocate a new structure,
104      * which doesn't work here.  instead, simply copy the
105      * contents of the new structure into the array cell,
106      * then free it.  this makes for more maintainable
107      * code than simply reimplementing the copy functions
108      * in this loop.*/
109     tmp = IDP_CopyProperty_ex(GETPROP(narray, i), flag);
110     memcpy(GETPROP(narray, i), tmp, sizeof(IDProperty));
111     MEM_freeN(tmp);
112   }
113
114   return narray;
115 }
116
117 static void IDP_FreeIDPArray(IDProperty *prop, const bool do_id_user)
118 {
119   int i;
120
121   BLI_assert(prop->type == IDP_IDPARRAY);
122
123   for (i = 0; i < prop->len; i++) {
124     IDP_FreePropertyContent_ex(GETPROP(prop, i), do_id_user);
125   }
126
127   if (prop->data.pointer) {
128     MEM_freeN(prop->data.pointer);
129   }
130 }
131
132 /* shallow copies item */
133 void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item)
134 {
135   IDProperty *old;
136
137   BLI_assert(prop->type == IDP_IDPARRAY);
138
139   if (index >= prop->len || index < 0) {
140     return;
141   }
142
143   old = GETPROP(prop, index);
144   if (item != old) {
145     IDP_FreePropertyContent(old);
146
147     memcpy(old, item, sizeof(IDProperty));
148   }
149 }
150
151 IDProperty *IDP_GetIndexArray(IDProperty *prop, int index)
152 {
153   BLI_assert(prop->type == IDP_IDPARRAY);
154
155   return GETPROP(prop, index);
156 }
157
158 void IDP_AppendArray(IDProperty *prop, IDProperty *item)
159 {
160   BLI_assert(prop->type == IDP_IDPARRAY);
161
162   IDP_ResizeIDPArray(prop, prop->len + 1);
163   IDP_SetIndexArray(prop, prop->len - 1, item);
164 }
165
166 void IDP_ResizeIDPArray(IDProperty *prop, int newlen)
167 {
168   int newsize;
169
170   BLI_assert(prop->type == IDP_IDPARRAY);
171
172   /* first check if the array buffer size has room */
173   if (newlen <= prop->totallen) {
174     if (newlen < prop->len && prop->totallen - newlen < IDP_ARRAY_REALLOC_LIMIT) {
175       int i;
176
177       for (i = newlen; i < prop->len; i++) {
178         IDP_FreePropertyContent(GETPROP(prop, i));
179       }
180
181       prop->len = newlen;
182       return;
183     }
184     else if (newlen >= prop->len) {
185       prop->len = newlen;
186       return;
187     }
188   }
189
190   /* free trailing items */
191   if (newlen < prop->len) {
192     /* newlen is smaller */
193     int i;
194     for (i = newlen; i < prop->len; i++) {
195       IDP_FreePropertyContent(GETPROP(prop, i));
196     }
197   }
198
199   /* - Note: This code comes from python, here's the corresponding comment. - */
200   /* This over-allocates proportional to the list size, making room
201    * for additional growth.  The over-allocation is mild, but is
202    * enough to give linear-time amortized behavior over a long
203    * sequence of appends() in the presence of a poorly-performing
204    * system realloc().
205    * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
206    */
207   newsize = newlen;
208   newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
209   prop->data.pointer = MEM_recallocN(prop->data.pointer, sizeof(IDProperty) * (size_t)newsize);
210   prop->len = newlen;
211   prop->totallen = newsize;
212 }
213
214 /* ----------- Numerical Array Type ----------- */
215 static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr)
216 {
217   if (prop->subtype != IDP_GROUP) {
218     return;
219   }
220
221   if (newlen >= prop->len) {
222     /* bigger */
223     IDProperty **array = newarr;
224     IDPropertyTemplate val;
225     int a;
226
227     for (a = prop->len; a < newlen; a++) {
228       val.i = 0; /* silence MSVC warning about uninitialized var when debugging */
229       array[a] = IDP_New(IDP_GROUP, &val, "IDP_ResizeArray group");
230     }
231   }
232   else {
233     /* smaller */
234     IDProperty **array = prop->data.pointer;
235     int a;
236
237     for (a = newlen; a < prop->len; a++) {
238       IDP_FreeProperty(array[a]);
239     }
240   }
241 }
242
243 /*this function works for strings too!*/
244 void IDP_ResizeArray(IDProperty *prop, int newlen)
245 {
246   int newsize;
247   const bool is_grow = newlen >= prop->len;
248
249   /* first check if the array buffer size has room */
250   if (newlen <= prop->totallen && prop->totallen - newlen < IDP_ARRAY_REALLOC_LIMIT) {
251     idp_resize_group_array(prop, newlen, prop->data.pointer);
252     prop->len = newlen;
253     return;
254   }
255
256   /* - Note: This code comes from python, here's the corresponding comment. - */
257   /* This over-allocates proportional to the list size, making room
258    * for additional growth.  The over-allocation is mild, but is
259    * enough to give linear-time amortized behavior over a long
260    * sequence of appends() in the presence of a poorly-performing
261    * system realloc().
262    * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
263    */
264   newsize = newlen;
265   newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
266
267   if (is_grow == false) {
268     idp_resize_group_array(prop, newlen, prop->data.pointer);
269   }
270
271   prop->data.pointer = MEM_recallocN(prop->data.pointer,
272                                      idp_size_table[(int)prop->subtype] * (size_t)newsize);
273
274   if (is_grow == true) {
275     idp_resize_group_array(prop, newlen, prop->data.pointer);
276   }
277
278   prop->len = newlen;
279   prop->totallen = newsize;
280 }
281
282 void IDP_FreeArray(IDProperty *prop)
283 {
284   if (prop->data.pointer) {
285     idp_resize_group_array(prop, 0, NULL);
286     MEM_freeN(prop->data.pointer);
287   }
288 }
289
290 static IDProperty *idp_generic_copy(const IDProperty *prop, const int UNUSED(flag))
291 {
292   IDProperty *newp = MEM_callocN(sizeof(IDProperty), __func__);
293
294   BLI_strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
295   newp->type = prop->type;
296   newp->flag = prop->flag;
297   newp->data.val = prop->data.val;
298   newp->data.val2 = prop->data.val2;
299
300   return newp;
301 }
302
303 static IDProperty *IDP_CopyArray(const IDProperty *prop, const int flag)
304 {
305   IDProperty *newp = idp_generic_copy(prop, flag);
306
307   if (prop->data.pointer) {
308     newp->data.pointer = MEM_dupallocN(prop->data.pointer);
309
310     if (prop->type == IDP_GROUP) {
311       IDProperty **array = newp->data.pointer;
312       int a;
313
314       for (a = 0; a < prop->len; a++) {
315         array[a] = IDP_CopyProperty_ex(array[a], flag);
316       }
317     }
318   }
319   newp->len = prop->len;
320   newp->subtype = prop->subtype;
321   newp->totallen = prop->totallen;
322
323   return newp;
324 }
325 /** \} */
326
327 /* -------------------------------------------------------------------- */
328 /* String Functions */
329
330 /** \name IDProperty String API
331  * \{ */
332
333 /**
334  *
335  * \param st: The string to assign.
336  * \param name: The property name.
337  * \param maxlen: The size of the new string (including the \0 terminator).
338  * \return The new string property.
339  */
340 IDProperty *IDP_NewString(const char *st, const char *name, int maxlen)
341 {
342   IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
343
344   if (st == NULL) {
345     prop->data.pointer = MEM_mallocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
346     *IDP_String(prop) = '\0';
347     prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
348     prop->len = 1; /* NULL string, has len of 1 to account for null byte. */
349   }
350   else {
351     /* include null terminator '\0' */
352     int stlen = (int)strlen(st) + 1;
353
354     if (maxlen > 0 && maxlen < stlen) {
355       stlen = maxlen;
356     }
357
358     prop->data.pointer = MEM_mallocN((size_t)stlen, "id property string 2");
359     prop->len = prop->totallen = stlen;
360     BLI_strncpy(prop->data.pointer, st, (size_t)stlen);
361   }
362
363   prop->type = IDP_STRING;
364   BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
365
366   return prop;
367 }
368
369 static IDProperty *IDP_CopyString(const IDProperty *prop, const int flag)
370 {
371   IDProperty *newp;
372
373   BLI_assert(prop->type == IDP_STRING);
374   newp = idp_generic_copy(prop, flag);
375
376   if (prop->data.pointer) {
377     newp->data.pointer = MEM_dupallocN(prop->data.pointer);
378   }
379   newp->len = prop->len;
380   newp->subtype = prop->subtype;
381   newp->totallen = prop->totallen;
382
383   return newp;
384 }
385
386 void IDP_AssignString(IDProperty *prop, const char *st, int maxlen)
387 {
388   int stlen;
389
390   BLI_assert(prop->type == IDP_STRING);
391   stlen = (int)strlen(st);
392   if (maxlen > 0 && maxlen < stlen) {
393     stlen = maxlen;
394   }
395
396   if (prop->subtype == IDP_STRING_SUB_BYTE) {
397     IDP_ResizeArray(prop, stlen);
398     memcpy(prop->data.pointer, st, (size_t)stlen);
399   }
400   else {
401     stlen++;
402     IDP_ResizeArray(prop, stlen);
403     BLI_strncpy(prop->data.pointer, st, (size_t)stlen);
404   }
405 }
406
407 void IDP_ConcatStringC(IDProperty *prop, const char *st)
408 {
409   int newlen;
410
411   BLI_assert(prop->type == IDP_STRING);
412
413   newlen = prop->len + (int)strlen(st);
414   /* we have to remember that prop->len includes the null byte for strings.
415    * so there's no need to add +1 to the resize function.*/
416   IDP_ResizeArray(prop, newlen);
417   strcat(prop->data.pointer, st);
418 }
419
420 void IDP_ConcatString(IDProperty *str1, IDProperty *append)
421 {
422   int newlen;
423
424   BLI_assert(append->type == IDP_STRING);
425
426   /* since ->len for strings includes the NULL byte, we have to subtract one or
427    * we'll get an extra null byte after each concatenation operation.*/
428   newlen = str1->len + append->len - 1;
429   IDP_ResizeArray(str1, newlen);
430   strcat(str1->data.pointer, append->data.pointer);
431 }
432
433 void IDP_FreeString(IDProperty *prop)
434 {
435   BLI_assert(prop->type == IDP_STRING);
436
437   if (prop->data.pointer) {
438     MEM_freeN(prop->data.pointer);
439   }
440 }
441 /** \} */
442
443 /* -------------------------------------------------------------------- */
444 /* ID Type */
445
446 /** \name IDProperty ID API
447  * \{ */
448
449 static IDProperty *IDP_CopyID(const IDProperty *prop, const int flag)
450 {
451   IDProperty *newp;
452
453   BLI_assert(prop->type == IDP_ID);
454   newp = idp_generic_copy(prop, flag);
455
456   newp->data.pointer = prop->data.pointer;
457   if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
458     id_us_plus(IDP_Id(newp));
459   }
460
461   return newp;
462 }
463
464 /** \} */
465
466 /* -------------------------------------------------------------------- */
467 /* Group Functions */
468
469 /** \name IDProperty Group API
470  * \{ */
471
472 /**
473  * Checks if a property with the same name as prop exists, and if so replaces it.
474  */
475 static IDProperty *IDP_CopyGroup(const IDProperty *prop, const int flag)
476 {
477   IDProperty *newp, *link;
478
479   BLI_assert(prop->type == IDP_GROUP);
480   newp = idp_generic_copy(prop, flag);
481   newp->len = prop->len;
482   newp->subtype = prop->subtype;
483
484   for (link = prop->data.group.first; link; link = link->next) {
485     BLI_addtail(&newp->data.group, IDP_CopyProperty_ex(link, flag));
486   }
487
488   return newp;
489 }
490
491 /* use for syncing proxies.
492  * When values name and types match, copy the values, else ignore */
493 void IDP_SyncGroupValues(IDProperty *dest, const IDProperty *src)
494 {
495   IDProperty *other, *prop;
496
497   BLI_assert(dest->type == IDP_GROUP);
498   BLI_assert(src->type == IDP_GROUP);
499
500   for (prop = src->data.group.first; prop; prop = prop->next) {
501     other = BLI_findstring(&dest->data.group, prop->name, offsetof(IDProperty, name));
502     if (other && prop->type == other->type) {
503       switch (prop->type) {
504         case IDP_INT:
505         case IDP_FLOAT:
506         case IDP_DOUBLE:
507           other->data = prop->data;
508           break;
509         case IDP_GROUP:
510           IDP_SyncGroupValues(other, prop);
511           break;
512         default: {
513           BLI_insertlinkreplace(&dest->data.group, other, IDP_CopyProperty(prop));
514           IDP_FreeProperty(other);
515           break;
516         }
517       }
518     }
519   }
520 }
521
522 void IDP_SyncGroupTypes(IDProperty *dst, const IDProperty *src, const bool do_arraylen)
523 {
524   IDProperty *prop_dst, *prop_dst_next;
525   const IDProperty *prop_src;
526
527   for (prop_dst = dst->data.group.first; prop_dst; prop_dst = prop_dst_next) {
528     prop_dst_next = prop_dst->next;
529     if ((prop_src = IDP_GetPropertyFromGroup((IDProperty *)src, prop_dst->name))) {
530       /* check of we should replace? */
531       if ((prop_dst->type != prop_src->type || prop_dst->subtype != prop_src->subtype) ||
532           (do_arraylen && ELEM(prop_dst->type, IDP_ARRAY, IDP_IDPARRAY) &&
533            (prop_src->len != prop_dst->len))) {
534         BLI_insertlinkreplace(&dst->data.group, prop_dst, IDP_CopyProperty(prop_src));
535         IDP_FreeProperty(prop_dst);
536       }
537       else if (prop_dst->type == IDP_GROUP) {
538         IDP_SyncGroupTypes(prop_dst, prop_src, do_arraylen);
539       }
540     }
541     else {
542       IDP_FreeFromGroup(dst, prop_dst);
543     }
544   }
545 }
546
547 /**
548  * Replaces all properties with the same name in a destination group from a source group.
549  */
550 void IDP_ReplaceGroupInGroup(IDProperty *dest, const IDProperty *src)
551 {
552   IDProperty *loop, *prop;
553
554   BLI_assert(dest->type == IDP_GROUP);
555   BLI_assert(src->type == IDP_GROUP);
556
557   for (prop = src->data.group.first; prop; prop = prop->next) {
558     for (loop = dest->data.group.first; loop; loop = loop->next) {
559       if (STREQ(loop->name, prop->name)) {
560         BLI_insertlinkreplace(&dest->data.group, loop, IDP_CopyProperty(prop));
561         IDP_FreeProperty(loop);
562         break;
563       }
564     }
565
566     /* only add at end if not added yet */
567     if (loop == NULL) {
568       IDProperty *copy = IDP_CopyProperty(prop);
569       dest->len++;
570       BLI_addtail(&dest->data.group, copy);
571     }
572   }
573 }
574
575 /**
576  * Checks if a property with the same name as prop exists, and if so replaces it.
577  * Use this to preserve order!
578  */
579 void IDP_ReplaceInGroup_ex(IDProperty *group, IDProperty *prop, IDProperty *prop_exist)
580 {
581   BLI_assert(group->type == IDP_GROUP);
582   BLI_assert(prop_exist == IDP_GetPropertyFromGroup(group, prop->name));
583
584   if (prop_exist != NULL) {
585     BLI_insertlinkreplace(&group->data.group, prop_exist, prop);
586     IDP_FreeProperty(prop_exist);
587   }
588   else {
589     group->len++;
590     BLI_addtail(&group->data.group, prop);
591   }
592 }
593
594 void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
595 {
596   IDProperty *prop_exist = IDP_GetPropertyFromGroup(group, prop->name);
597
598   IDP_ReplaceInGroup_ex(group, prop, prop_exist);
599 }
600
601 /**
602  * If a property is missing in \a dest, add it.
603  * Do it recursively.
604  */
605 void IDP_MergeGroup_ex(IDProperty *dest,
606                        const IDProperty *src,
607                        const bool do_overwrite,
608                        const int flag)
609 {
610   IDProperty *prop;
611
612   BLI_assert(dest->type == IDP_GROUP);
613   BLI_assert(src->type == IDP_GROUP);
614
615   if (do_overwrite) {
616     for (prop = src->data.group.first; prop; prop = prop->next) {
617       if (prop->type == IDP_GROUP) {
618         IDProperty *prop_exist = IDP_GetPropertyFromGroup(dest, prop->name);
619
620         if (prop_exist != NULL) {
621           IDP_MergeGroup_ex(prop_exist, prop, do_overwrite, flag);
622           continue;
623         }
624       }
625
626       IDProperty *copy = IDP_CopyProperty_ex(prop, flag);
627       IDP_ReplaceInGroup(dest, copy);
628     }
629   }
630   else {
631     for (prop = src->data.group.first; prop; prop = prop->next) {
632       IDProperty *prop_exist = IDP_GetPropertyFromGroup(dest, prop->name);
633       if (prop_exist != NULL) {
634         if (prop->type == IDP_GROUP) {
635           IDP_MergeGroup_ex(prop_exist, prop, do_overwrite, flag);
636           continue;
637         }
638       }
639       else {
640         IDProperty *copy = IDP_CopyProperty_ex(prop, flag);
641         dest->len++;
642         BLI_addtail(&dest->data.group, copy);
643       }
644     }
645   }
646 }
647
648 /**
649  * If a property is missing in \a dest, add it.
650  * Do it recursively.
651  */
652 void IDP_MergeGroup(IDProperty *dest, const IDProperty *src, const bool do_overwrite)
653 {
654   IDP_MergeGroup_ex(dest, src, do_overwrite, 0);
655 }
656
657 /**
658  * This function has a sanity check to make sure ID properties with the same name don't
659  * get added to the group.
660  *
661  * The sanity check just means the property is not added to the group if another property
662  * exists with the same name; the client code using ID properties then needs to detect this
663  * (the function that adds new properties to groups, #IDP_AddToGroup,
664  * returns false if a property can't be added to the group, and true if it can)
665  * and free the property.
666  */
667 bool IDP_AddToGroup(IDProperty *group, IDProperty *prop)
668 {
669   BLI_assert(group->type == IDP_GROUP);
670
671   if (IDP_GetPropertyFromGroup(group, prop->name) == NULL) {
672     group->len++;
673     BLI_addtail(&group->data.group, prop);
674     return true;
675   }
676
677   return false;
678 }
679
680 /**
681  * This is the same as IDP_AddToGroup, only you pass an item
682  * in the group list to be inserted after.
683  */
684 bool IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
685 {
686   BLI_assert(group->type == IDP_GROUP);
687
688   if (IDP_GetPropertyFromGroup(group, pnew->name) == NULL) {
689     group->len++;
690     BLI_insertlinkafter(&group->data.group, previous, pnew);
691     return true;
692   }
693
694   return false;
695 }
696
697 /**
698  * \note this does not free the property!!
699  *
700  * To free the property, you have to do:
701  * IDP_FreeProperty(prop);
702  */
703 void IDP_RemoveFromGroup(IDProperty *group, IDProperty *prop)
704 {
705   BLI_assert(group->type == IDP_GROUP);
706
707   group->len--;
708   BLI_remlink(&group->data.group, prop);
709 }
710
711 /**
712  * Removes the property from the group and frees it.
713  */
714 void IDP_FreeFromGroup(IDProperty *group, IDProperty *prop)
715 {
716   IDP_RemoveFromGroup(group, prop);
717   IDP_FreeProperty(prop);
718 }
719
720 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, const char *name)
721 {
722   BLI_assert(prop->type == IDP_GROUP);
723
724   return (IDProperty *)BLI_findstring(&prop->data.group, name, offsetof(IDProperty, name));
725 }
726 /** same as above but ensure type match */
727 IDProperty *IDP_GetPropertyTypeFromGroup(IDProperty *prop, const char *name, const char type)
728 {
729   IDProperty *idprop = IDP_GetPropertyFromGroup(prop, name);
730   return (idprop && idprop->type == type) ? idprop : NULL;
731 }
732
733 /* Ok, the way things work, Groups free the ID Property structs of their children.
734  * This is because all ID Property freeing functions free only direct data (not the ID Property
735  * struct itself), but for Groups the child properties *are* considered
736  * direct data. */
737 static void IDP_FreeGroup(IDProperty *prop, const bool do_id_user)
738 {
739   IDProperty *loop;
740
741   BLI_assert(prop->type == IDP_GROUP);
742   for (loop = prop->data.group.first; loop; loop = loop->next) {
743     IDP_FreePropertyContent_ex(loop, do_id_user);
744   }
745   BLI_freelistN(&prop->data.group);
746 }
747 /** \} */
748
749 /* -------------------------------------------------------------------- */
750 /* Main Functions */
751
752 /** \name IDProperty Main API
753  * \{ */
754 IDProperty *IDP_CopyProperty_ex(const IDProperty *prop, const int flag)
755 {
756   switch (prop->type) {
757     case IDP_GROUP:
758       return IDP_CopyGroup(prop, flag);
759     case IDP_STRING:
760       return IDP_CopyString(prop, flag);
761     case IDP_ID:
762       return IDP_CopyID(prop, flag);
763     case IDP_ARRAY:
764       return IDP_CopyArray(prop, flag);
765     case IDP_IDPARRAY:
766       return IDP_CopyIDPArray(prop, flag);
767     default:
768       return idp_generic_copy(prop, flag);
769   }
770 }
771
772 IDProperty *IDP_CopyProperty(const IDProperty *prop)
773 {
774   return IDP_CopyProperty_ex(prop, 0);
775 }
776
777 /* Updates ID pointers after an object has been copied */
778 /* TODO Nuke this once its only user has been correctly converted
779  * to use generic ID management from BKE_library! */
780 void IDP_RelinkProperty(struct IDProperty *prop)
781 {
782   if (!prop) {
783     return;
784   }
785
786   switch (prop->type) {
787     case IDP_GROUP: {
788       for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) {
789         IDP_RelinkProperty(loop);
790       }
791       break;
792     }
793     case IDP_IDPARRAY: {
794       IDProperty *idp_array = IDP_Array(prop);
795       for (int i = 0; i < prop->len; i++) {
796         IDP_RelinkProperty(&idp_array[i]);
797       }
798       break;
799     }
800     case IDP_ID: {
801       ID *id = IDP_Id(prop);
802       if (id && id->newid) {
803         id_us_min(IDP_Id(prop));
804         prop->data.pointer = id->newid;
805         id_us_plus(IDP_Id(prop));
806       }
807       break;
808     }
809     default:
810       break; /* Nothing to do for other IDProp types. */
811   }
812 }
813
814 /**
815  * Get the Group property that contains the id properties for ID id.  Set create_if_needed
816  * to create the Group property and attach it to id if it doesn't exist; otherwise
817  * the function will return NULL if there's no Group property attached to the ID.
818  */
819 IDProperty *IDP_GetProperties(ID *id, const bool create_if_needed)
820 {
821   if (id->properties) {
822     return id->properties;
823   }
824   else {
825     if (create_if_needed) {
826       id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
827       id->properties->type = IDP_GROUP;
828       /* don't overwrite the data's name and type
829        * some functions might need this if they
830        * don't have a real ID, should be named elsewhere - Campbell */
831       /* strcpy(id->name, "top_level_group");*/
832     }
833     return id->properties;
834   }
835 }
836
837 /**
838  * \param is_strict: When false treat missing items as a match */
839 bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is_strict)
840 {
841   if (prop1 == NULL && prop2 == NULL) {
842     return true;
843   }
844   else if (prop1 == NULL || prop2 == NULL) {
845     return is_strict ? false : true;
846   }
847   else if (prop1->type != prop2->type) {
848     return false;
849   }
850
851   switch (prop1->type) {
852     case IDP_INT:
853       return (IDP_Int(prop1) == IDP_Int(prop2));
854     case IDP_FLOAT:
855 #if !defined(NDEBUG) && defined(WITH_PYTHON)
856     {
857       float p1 = IDP_Float(prop1);
858       float p2 = IDP_Float(prop2);
859       if ((p1 != p2) && ((fabsf(p1 - p2) / max_ff(p1, p2)) < 0.001f)) {
860         printf(
861             "WARNING: Comparing two float properties that have nearly the same value (%f vs. "
862             "%f)\n",
863             p1,
864             p2);
865         printf("    p1: ");
866         IDP_print(prop1);
867         printf("    p2: ");
868         IDP_print(prop2);
869       }
870     }
871 #endif
872       return (IDP_Float(prop1) == IDP_Float(prop2));
873     case IDP_DOUBLE:
874       return (IDP_Double(prop1) == IDP_Double(prop2));
875     case IDP_STRING: {
876       return (((prop1->len == prop2->len) &&
877                STREQLEN(IDP_String(prop1), IDP_String(prop2), (size_t)prop1->len)));
878     }
879     case IDP_ARRAY:
880       if (prop1->len == prop2->len && prop1->subtype == prop2->subtype) {
881         return (memcmp(IDP_Array(prop1),
882                        IDP_Array(prop2),
883                        idp_size_table[(int)prop1->subtype] * (size_t)prop1->len) == 0);
884       }
885       return false;
886     case IDP_GROUP: {
887       IDProperty *link1, *link2;
888
889       if (is_strict && prop1->len != prop2->len) {
890         return false;
891       }
892
893       for (link1 = prop1->data.group.first; link1; link1 = link1->next) {
894         link2 = IDP_GetPropertyFromGroup(prop2, link1->name);
895
896         if (!IDP_EqualsProperties_ex(link1, link2, is_strict)) {
897           return false;
898         }
899       }
900
901       return true;
902     }
903     case IDP_IDPARRAY: {
904       IDProperty *array1 = IDP_IDPArray(prop1);
905       IDProperty *array2 = IDP_IDPArray(prop2);
906       int i;
907
908       if (prop1->len != prop2->len) {
909         return false;
910       }
911
912       for (i = 0; i < prop1->len; i++) {
913         if (!IDP_EqualsProperties_ex(&array1[i], &array2[i], is_strict)) {
914           return false;
915         }
916       }
917       return true;
918     }
919     case IDP_ID:
920       return (IDP_Id(prop1) == IDP_Id(prop2));
921     default:
922       BLI_assert(0);
923       break;
924   }
925
926   return true;
927 }
928
929 bool IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2)
930 {
931   return IDP_EqualsProperties_ex(prop1, prop2, true);
932 }
933
934 /**
935  * Allocate a new ID.
936  *
937  * This function takes three arguments: the ID property type, a union which defines
938  * it's initial value, and a name.
939  *
940  * The union is simple to use; see the top of this header file for its definition.
941  * An example of using this function:
942  *
943  * \code{.c}
944  * IDPropertyTemplate val;
945  * IDProperty *group, *idgroup, *color;
946  * group = IDP_New(IDP_GROUP, val, "group1"); //groups don't need a template.
947  *
948  * val.array.len = 4
949  * val.array.type = IDP_FLOAT;
950  * color = IDP_New(IDP_ARRAY, val, "color1");
951  *
952  * idgroup = IDP_GetProperties(some_id, 1);
953  * IDP_AddToGroup(idgroup, color);
954  * IDP_AddToGroup(idgroup, group);
955  * \endcode
956  *
957  * Note that you MUST either attach the id property to an id property group with
958  * IDP_AddToGroup or MEM_freeN the property, doing anything else might result in
959  * a memory leak.
960  */
961 IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *name)
962 {
963   IDProperty *prop = NULL;
964
965   switch (type) {
966     case IDP_INT:
967       prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
968       prop->data.val = val->i;
969       break;
970     case IDP_FLOAT:
971       prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
972       *(float *)&prop->data.val = val->f;
973       break;
974     case IDP_DOUBLE:
975       prop = MEM_callocN(sizeof(IDProperty), "IDProperty double");
976       *(double *)&prop->data.val = val->d;
977       break;
978     case IDP_ARRAY: {
979       /* for now, we only support float and int and double arrays */
980       if ((val->array.type == IDP_FLOAT) || (val->array.type == IDP_INT) ||
981           (val->array.type == IDP_DOUBLE) || (val->array.type == IDP_GROUP)) {
982         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
983         prop->subtype = val->array.type;
984         if (val->array.len) {
985           prop->data.pointer = MEM_callocN(
986               idp_size_table[val->array.type] * (size_t)val->array.len, "id property array");
987         }
988         prop->len = prop->totallen = val->array.len;
989         break;
990       }
991       CLOG_ERROR(&LOG, "bad array type.");
992       return NULL;
993     }
994     case IDP_STRING: {
995       const char *st = val->string.str;
996
997       prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
998       if (val->string.subtype == IDP_STRING_SUB_BYTE) {
999         /* note, intentionally not null terminated */
1000         if (st == NULL) {
1001           prop->data.pointer = MEM_mallocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
1002           *IDP_String(prop) = '\0';
1003           prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
1004           prop->len = 0;
1005         }
1006         else {
1007           prop->data.pointer = MEM_mallocN((size_t)val->string.len, "id property string 2");
1008           prop->len = prop->totallen = val->string.len;
1009           memcpy(prop->data.pointer, st, (size_t)val->string.len);
1010         }
1011         prop->subtype = IDP_STRING_SUB_BYTE;
1012       }
1013       else {
1014         if (st == NULL || val->string.len <= 1) {
1015           prop->data.pointer = MEM_mallocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
1016           *IDP_String(prop) = '\0';
1017           prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
1018           /* NULL string, has len of 1 to account for null byte. */
1019           prop->len = 1;
1020         }
1021         else {
1022           BLI_assert((int)val->string.len <= (int)strlen(st) + 1);
1023           prop->data.pointer = MEM_mallocN((size_t)val->string.len, "id property string 3");
1024           memcpy(prop->data.pointer, st, (size_t)val->string.len - 1);
1025           IDP_String(prop)[val->string.len - 1] = '\0';
1026           prop->len = prop->totallen = val->string.len;
1027         }
1028         prop->subtype = IDP_STRING_SUB_UTF8;
1029       }
1030       break;
1031     }
1032     case IDP_GROUP: {
1033       /* Values are set properly by calloc. */
1034       prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
1035       break;
1036     }
1037     case IDP_ID: {
1038       prop = MEM_callocN(sizeof(IDProperty), "IDProperty datablock");
1039       prop->data.pointer = (void *)val->id;
1040       prop->type = IDP_ID;
1041       id_us_plus(IDP_Id(prop));
1042       break;
1043     }
1044     default: {
1045       prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
1046       break;
1047     }
1048   }
1049
1050   prop->type = type;
1051   BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
1052
1053   return prop;
1054 }
1055
1056 /**
1057  * \note This will free allocated data, all child properties of arrays and groups, and unlink IDs!
1058  * But it does not free the actual IDProperty struct itself.
1059  */
1060 void IDP_FreePropertyContent_ex(IDProperty *prop, const bool do_id_user)
1061 {
1062   switch (prop->type) {
1063     case IDP_ARRAY:
1064       IDP_FreeArray(prop);
1065       break;
1066     case IDP_STRING:
1067       IDP_FreeString(prop);
1068       break;
1069     case IDP_GROUP:
1070       IDP_FreeGroup(prop, do_id_user);
1071       break;
1072     case IDP_IDPARRAY:
1073       IDP_FreeIDPArray(prop, do_id_user);
1074       break;
1075     case IDP_ID:
1076       if (do_id_user) {
1077         id_us_min(IDP_Id(prop));
1078       }
1079       break;
1080   }
1081 }
1082
1083 void IDP_FreePropertyContent(IDProperty *prop)
1084 {
1085   IDP_FreePropertyContent_ex(prop, true);
1086 }
1087
1088 void IDP_FreeProperty(IDProperty *prop)
1089 {
1090   IDP_FreePropertyContent(prop);
1091   MEM_freeN(prop);
1092 }
1093
1094 void IDP_ClearProperty(IDProperty *prop)
1095 {
1096   IDP_FreePropertyContent(prop);
1097   prop->data.pointer = NULL;
1098   prop->len = prop->totallen = 0;
1099 }
1100
1101 void IDP_Reset(IDProperty *prop, const IDProperty *reference)
1102 {
1103   if (prop == NULL) {
1104     return;
1105   }
1106   IDP_ClearProperty(prop);
1107   if (reference != NULL) {
1108     IDP_MergeGroup(prop, reference, true);
1109   }
1110 }
1111
1112 /** \} */