Cleanup: style, use braces for blenkernel
[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_FreeProperty_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_FreeProperty(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_FreeProperty(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_FreeProperty(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       MEM_freeN(array[a]);
240     }
241   }
242 }
243
244 /*this function works for strings too!*/
245 void IDP_ResizeArray(IDProperty *prop, int newlen)
246 {
247   int newsize;
248   const bool is_grow = newlen >= prop->len;
249
250   /* first check if the array buffer size has room */
251   if (newlen <= prop->totallen && prop->totallen - newlen < IDP_ARRAY_REALLOC_LIMIT) {
252     idp_resize_group_array(prop, newlen, prop->data.pointer);
253     prop->len = newlen;
254     return;
255   }
256
257   /* - Note: This code comes from python, here's the corresponding comment. - */
258   /* This over-allocates proportional to the list size, making room
259    * for additional growth.  The over-allocation is mild, but is
260    * enough to give linear-time amortized behavior over a long
261    * sequence of appends() in the presence of a poorly-performing
262    * system realloc().
263    * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
264    */
265   newsize = newlen;
266   newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
267
268   if (is_grow == false) {
269     idp_resize_group_array(prop, newlen, prop->data.pointer);
270   }
271
272   prop->data.pointer = MEM_recallocN(prop->data.pointer,
273                                      idp_size_table[(int)prop->subtype] * (size_t)newsize);
274
275   if (is_grow == true) {
276     idp_resize_group_array(prop, newlen, prop->data.pointer);
277   }
278
279   prop->len = newlen;
280   prop->totallen = newsize;
281 }
282
283 void IDP_FreeArray(IDProperty *prop)
284 {
285   if (prop->data.pointer) {
286     idp_resize_group_array(prop, 0, NULL);
287     MEM_freeN(prop->data.pointer);
288   }
289 }
290
291 static IDProperty *idp_generic_copy(const IDProperty *prop, const int UNUSED(flag))
292 {
293   IDProperty *newp = MEM_callocN(sizeof(IDProperty), __func__);
294
295   BLI_strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
296   newp->type = prop->type;
297   newp->flag = prop->flag;
298   newp->data.val = prop->data.val;
299   newp->data.val2 = prop->data.val2;
300
301   return newp;
302 }
303
304 static IDProperty *IDP_CopyArray(const IDProperty *prop, const int flag)
305 {
306   IDProperty *newp = idp_generic_copy(prop, flag);
307
308   if (prop->data.pointer) {
309     newp->data.pointer = MEM_dupallocN(prop->data.pointer);
310
311     if (prop->type == IDP_GROUP) {
312       IDProperty **array = newp->data.pointer;
313       int a;
314
315       for (a = 0; a < prop->len; a++) {
316         array[a] = IDP_CopyProperty_ex(array[a], flag);
317       }
318     }
319   }
320   newp->len = prop->len;
321   newp->subtype = prop->subtype;
322   newp->totallen = prop->totallen;
323
324   return newp;
325 }
326 /** \} */
327
328 /* -------------------------------------------------------------------- */
329 /* String Functions */
330
331 /** \name IDProperty String API
332  * \{ */
333
334 /**
335  *
336  * \param st: The string to assign.
337  * \param name: The property name.
338  * \param maxlen: The size of the new string (including the \0 terminator).
339  * \return The new string property.
340  */
341 IDProperty *IDP_NewString(const char *st, const char *name, int maxlen)
342 {
343   IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
344
345   if (st == NULL) {
346     prop->data.pointer = MEM_mallocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
347     *IDP_String(prop) = '\0';
348     prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
349     prop->len = 1; /* NULL string, has len of 1 to account for null byte. */
350   }
351   else {
352     /* include null terminator '\0' */
353     int stlen = (int)strlen(st) + 1;
354
355     if (maxlen > 0 && maxlen < stlen) {
356       stlen = maxlen;
357     }
358
359     prop->data.pointer = MEM_mallocN((size_t)stlen, "id property string 2");
360     prop->len = prop->totallen = stlen;
361     BLI_strncpy(prop->data.pointer, st, (size_t)stlen);
362   }
363
364   prop->type = IDP_STRING;
365   BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
366
367   return prop;
368 }
369
370 static IDProperty *IDP_CopyString(const IDProperty *prop, const int flag)
371 {
372   IDProperty *newp;
373
374   BLI_assert(prop->type == IDP_STRING);
375   newp = idp_generic_copy(prop, flag);
376
377   if (prop->data.pointer) {
378     newp->data.pointer = MEM_dupallocN(prop->data.pointer);
379   }
380   newp->len = prop->len;
381   newp->subtype = prop->subtype;
382   newp->totallen = prop->totallen;
383
384   return newp;
385 }
386
387 void IDP_AssignString(IDProperty *prop, const char *st, int maxlen)
388 {
389   int stlen;
390
391   BLI_assert(prop->type == IDP_STRING);
392   stlen = (int)strlen(st);
393   if (maxlen > 0 && maxlen < stlen) {
394     stlen = maxlen;
395   }
396
397   if (prop->subtype == IDP_STRING_SUB_BYTE) {
398     IDP_ResizeArray(prop, stlen);
399     memcpy(prop->data.pointer, st, (size_t)stlen);
400   }
401   else {
402     stlen++;
403     IDP_ResizeArray(prop, stlen);
404     BLI_strncpy(prop->data.pointer, st, (size_t)stlen);
405   }
406 }
407
408 void IDP_ConcatStringC(IDProperty *prop, const char *st)
409 {
410   int newlen;
411
412   BLI_assert(prop->type == IDP_STRING);
413
414   newlen = prop->len + (int)strlen(st);
415   /* we have to remember that prop->len includes the null byte for strings.
416    * so there's no need to add +1 to the resize function.*/
417   IDP_ResizeArray(prop, newlen);
418   strcat(prop->data.pointer, st);
419 }
420
421 void IDP_ConcatString(IDProperty *str1, IDProperty *append)
422 {
423   int newlen;
424
425   BLI_assert(append->type == IDP_STRING);
426
427   /* since ->len for strings includes the NULL byte, we have to subtract one or
428    * we'll get an extra null byte after each concatenation operation.*/
429   newlen = str1->len + append->len - 1;
430   IDP_ResizeArray(str1, newlen);
431   strcat(str1->data.pointer, append->data.pointer);
432 }
433
434 void IDP_FreeString(IDProperty *prop)
435 {
436   BLI_assert(prop->type == IDP_STRING);
437
438   if (prop->data.pointer) {
439     MEM_freeN(prop->data.pointer);
440   }
441 }
442 /** \} */
443
444 /* -------------------------------------------------------------------- */
445 /* ID Type */
446
447 /** \name IDProperty ID API
448  * \{ */
449
450 static IDProperty *IDP_CopyID(const IDProperty *prop, const int flag)
451 {
452   IDProperty *newp;
453
454   BLI_assert(prop->type == IDP_ID);
455   newp = idp_generic_copy(prop, flag);
456
457   newp->data.pointer = prop->data.pointer;
458   if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
459     id_us_plus(IDP_Id(newp));
460   }
461
462   return newp;
463 }
464
465 /** \} */
466
467 /* -------------------------------------------------------------------- */
468 /* Group Functions */
469
470 /** \name IDProperty Group API
471  * \{ */
472
473 /**
474  * Checks if a property with the same name as prop exists, and if so replaces it.
475  */
476 static IDProperty *IDP_CopyGroup(const IDProperty *prop, const int flag)
477 {
478   IDProperty *newp, *link;
479
480   BLI_assert(prop->type == IDP_GROUP);
481   newp = idp_generic_copy(prop, flag);
482   newp->len = prop->len;
483   newp->subtype = prop->subtype;
484
485   for (link = prop->data.group.first; link; link = link->next) {
486     BLI_addtail(&newp->data.group, IDP_CopyProperty_ex(link, flag));
487   }
488
489   return newp;
490 }
491
492 /* use for syncing proxies.
493  * When values name and types match, copy the values, else ignore */
494 void IDP_SyncGroupValues(IDProperty *dest, const IDProperty *src)
495 {
496   IDProperty *other, *prop;
497
498   BLI_assert(dest->type == IDP_GROUP);
499   BLI_assert(src->type == IDP_GROUP);
500
501   for (prop = src->data.group.first; prop; prop = prop->next) {
502     other = BLI_findstring(&dest->data.group, prop->name, offsetof(IDProperty, name));
503     if (other && prop->type == other->type) {
504       switch (prop->type) {
505         case IDP_INT:
506         case IDP_FLOAT:
507         case IDP_DOUBLE:
508           other->data = prop->data;
509           break;
510         case IDP_GROUP:
511           IDP_SyncGroupValues(other, prop);
512           break;
513         default: {
514           BLI_insertlinkreplace(&dest->data.group, other, IDP_CopyProperty(prop));
515           IDP_FreeProperty(other);
516           MEM_freeN(other);
517           break;
518         }
519       }
520     }
521   }
522 }
523
524 void IDP_SyncGroupTypes(IDProperty *dst, const IDProperty *src, const bool do_arraylen)
525 {
526   IDProperty *prop_dst, *prop_dst_next;
527   const IDProperty *prop_src;
528
529   for (prop_dst = dst->data.group.first; prop_dst; prop_dst = prop_dst_next) {
530     prop_dst_next = prop_dst->next;
531     if ((prop_src = IDP_GetPropertyFromGroup((IDProperty *)src, prop_dst->name))) {
532       /* check of we should replace? */
533       if ((prop_dst->type != prop_src->type || prop_dst->subtype != prop_src->subtype) ||
534           (do_arraylen && ELEM(prop_dst->type, IDP_ARRAY, IDP_IDPARRAY) &&
535            (prop_src->len != prop_dst->len))) {
536         BLI_insertlinkreplace(&dst->data.group, prop_dst, IDP_CopyProperty(prop_src));
537         IDP_FreeProperty(prop_dst);
538         MEM_freeN(prop_dst);
539       }
540       else if (prop_dst->type == IDP_GROUP) {
541         IDP_SyncGroupTypes(prop_dst, prop_src, do_arraylen);
542       }
543     }
544     else {
545       IDP_FreeFromGroup(dst, prop_dst);
546     }
547   }
548 }
549
550 /**
551  * Replaces all properties with the same name in a destination group from a source group.
552  */
553 void IDP_ReplaceGroupInGroup(IDProperty *dest, const IDProperty *src)
554 {
555   IDProperty *loop, *prop;
556
557   BLI_assert(dest->type == IDP_GROUP);
558   BLI_assert(src->type == IDP_GROUP);
559
560   for (prop = src->data.group.first; prop; prop = prop->next) {
561     for (loop = dest->data.group.first; loop; loop = loop->next) {
562       if (STREQ(loop->name, prop->name)) {
563         BLI_insertlinkreplace(&dest->data.group, loop, IDP_CopyProperty(prop));
564         IDP_FreeProperty(loop);
565         MEM_freeN(loop);
566         break;
567       }
568     }
569
570     /* only add at end if not added yet */
571     if (loop == NULL) {
572       IDProperty *copy = IDP_CopyProperty(prop);
573       dest->len++;
574       BLI_addtail(&dest->data.group, copy);
575     }
576   }
577 }
578
579 /**
580  * Checks if a property with the same name as prop exists, and if so replaces it.
581  * Use this to preserve order!
582  */
583 void IDP_ReplaceInGroup_ex(IDProperty *group, IDProperty *prop, IDProperty *prop_exist)
584 {
585   BLI_assert(group->type == IDP_GROUP);
586   BLI_assert(prop_exist == IDP_GetPropertyFromGroup(group, prop->name));
587
588   if (prop_exist != NULL) {
589     BLI_insertlinkreplace(&group->data.group, prop_exist, prop);
590     IDP_FreeProperty(prop_exist);
591     MEM_freeN(prop_exist);
592   }
593   else {
594     group->len++;
595     BLI_addtail(&group->data.group, prop);
596   }
597 }
598
599 void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
600 {
601   IDProperty *prop_exist = IDP_GetPropertyFromGroup(group, prop->name);
602
603   IDP_ReplaceInGroup_ex(group, prop, prop_exist);
604 }
605
606 /**
607  * If a property is missing in \a dest, add it.
608  * Do it recursively.
609  */
610 void IDP_MergeGroup_ex(IDProperty *dest,
611                        const IDProperty *src,
612                        const bool do_overwrite,
613                        const int flag)
614 {
615   IDProperty *prop;
616
617   BLI_assert(dest->type == IDP_GROUP);
618   BLI_assert(src->type == IDP_GROUP);
619
620   if (do_overwrite) {
621     for (prop = src->data.group.first; prop; prop = prop->next) {
622       if (prop->type == IDP_GROUP) {
623         IDProperty *prop_exist = IDP_GetPropertyFromGroup(dest, prop->name);
624
625         if (prop_exist != NULL) {
626           IDP_MergeGroup_ex(prop_exist, prop, do_overwrite, flag);
627           continue;
628         }
629       }
630
631       IDProperty *copy = IDP_CopyProperty_ex(prop, flag);
632       IDP_ReplaceInGroup(dest, copy);
633     }
634   }
635   else {
636     for (prop = src->data.group.first; prop; prop = prop->next) {
637       IDProperty *prop_exist = IDP_GetPropertyFromGroup(dest, prop->name);
638       if (prop_exist != NULL) {
639         if (prop->type == IDP_GROUP) {
640           IDP_MergeGroup_ex(prop_exist, prop, do_overwrite, flag);
641           continue;
642         }
643       }
644       else {
645         IDProperty *copy = IDP_CopyProperty_ex(prop, flag);
646         dest->len++;
647         BLI_addtail(&dest->data.group, copy);
648       }
649     }
650   }
651 }
652
653 /**
654  * If a property is missing in \a dest, add it.
655  * Do it recursively.
656  */
657 void IDP_MergeGroup(IDProperty *dest, const IDProperty *src, const bool do_overwrite)
658 {
659   IDP_MergeGroup_ex(dest, src, do_overwrite, 0);
660 }
661
662 /**
663  * This function has a sanity check to make sure ID properties with the same name don't
664  * get added to the group.
665  *
666  * The sanity check just means the property is not added to the group if another property
667  * exists with the same name; the client code using ID properties then needs to detect this
668  * (the function that adds new properties to groups, IDP_AddToGroup, returns false if a property can't
669  * be added to the group, and true if it can) and free the property.
670  *
671  * Currently the code to free ID properties is designed to leave the actual struct
672  * you pass it un-freed, this is needed for how the system works.  This means
673  * to free an ID property, you first call IDP_FreeProperty then MEM_freeN the
674  * struct.  In the future this will just be IDP_FreeProperty and the code will
675  * be reorganized to work properly.
676  */
677 bool IDP_AddToGroup(IDProperty *group, IDProperty *prop)
678 {
679   BLI_assert(group->type == IDP_GROUP);
680
681   if (IDP_GetPropertyFromGroup(group, prop->name) == NULL) {
682     group->len++;
683     BLI_addtail(&group->data.group, prop);
684     return true;
685   }
686
687   return false;
688 }
689
690 /**
691  * This is the same as IDP_AddToGroup, only you pass an item
692  * in the group list to be inserted after.
693  */
694 bool IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
695 {
696   BLI_assert(group->type == IDP_GROUP);
697
698   if (IDP_GetPropertyFromGroup(group, pnew->name) == NULL) {
699     group->len++;
700     BLI_insertlinkafter(&group->data.group, previous, pnew);
701     return true;
702   }
703
704   return false;
705 }
706
707 /**
708  * \note this does not free the property!!
709  *
710  * To free the property, you have to do:
711  * IDP_FreeProperty(prop); //free all subdata
712  * MEM_freeN(prop); //free property struct itself
713  */
714 void IDP_RemoveFromGroup(IDProperty *group, IDProperty *prop)
715 {
716   BLI_assert(group->type == IDP_GROUP);
717
718   group->len--;
719   BLI_remlink(&group->data.group, prop);
720 }
721
722 /**
723  * Removes the property from the group and frees it.
724  */
725 void IDP_FreeFromGroup(IDProperty *group, IDProperty *prop)
726 {
727   IDP_RemoveFromGroup(group, prop);
728   IDP_FreeProperty(prop);
729   MEM_freeN(prop);
730 }
731
732 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, const char *name)
733 {
734   BLI_assert(prop->type == IDP_GROUP);
735
736   return (IDProperty *)BLI_findstring(&prop->data.group, name, offsetof(IDProperty, name));
737 }
738 /** same as above but ensure type match */
739 IDProperty *IDP_GetPropertyTypeFromGroup(IDProperty *prop, const char *name, const char type)
740 {
741   IDProperty *idprop = IDP_GetPropertyFromGroup(prop, name);
742   return (idprop && idprop->type == type) ? idprop : NULL;
743 }
744
745 /* Ok, the way things work, Groups free the ID Property structs of their children.
746  * This is because all ID Property freeing functions free only direct data (not the ID Property
747  * struct itself), but for Groups the child properties *are* considered
748  * direct data. */
749 static void IDP_FreeGroup(IDProperty *prop, const bool do_id_user)
750 {
751   IDProperty *loop;
752
753   BLI_assert(prop->type == IDP_GROUP);
754   for (loop = prop->data.group.first; loop; loop = loop->next) {
755     IDP_FreeProperty_ex(loop, do_id_user);
756   }
757   BLI_freelistN(&prop->data.group);
758 }
759 /** \} */
760
761 /* -------------------------------------------------------------------- */
762 /* Main Functions */
763
764 /** \name IDProperty Main API
765  * \{ */
766 IDProperty *IDP_CopyProperty_ex(const IDProperty *prop, const int flag)
767 {
768   switch (prop->type) {
769     case IDP_GROUP:
770       return IDP_CopyGroup(prop, flag);
771     case IDP_STRING:
772       return IDP_CopyString(prop, flag);
773     case IDP_ID:
774       return IDP_CopyID(prop, flag);
775     case IDP_ARRAY:
776       return IDP_CopyArray(prop, flag);
777     case IDP_IDPARRAY:
778       return IDP_CopyIDPArray(prop, flag);
779     default:
780       return idp_generic_copy(prop, flag);
781   }
782 }
783
784 IDProperty *IDP_CopyProperty(const IDProperty *prop)
785 {
786   return IDP_CopyProperty_ex(prop, 0);
787 }
788
789 /* Updates ID pointers after an object has been copied */
790 /* TODO Nuke this once its only user has been correctly converted to use generic ID management from BKE_library! */
791 void IDP_RelinkProperty(struct IDProperty *prop)
792 {
793   if (!prop) {
794     return;
795   }
796
797   switch (prop->type) {
798     case IDP_GROUP: {
799       for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) {
800         IDP_RelinkProperty(loop);
801       }
802       break;
803     }
804     case IDP_IDPARRAY: {
805       IDProperty *idp_array = IDP_Array(prop);
806       for (int i = 0; i < prop->len; i++) {
807         IDP_RelinkProperty(&idp_array[i]);
808       }
809       break;
810     }
811     case IDP_ID: {
812       ID *id = IDP_Id(prop);
813       if (id && id->newid) {
814         id_us_min(IDP_Id(prop));
815         prop->data.pointer = id->newid;
816         id_us_plus(IDP_Id(prop));
817       }
818       break;
819     }
820     default:
821       break; /* Nothing to do for other IDProp types. */
822   }
823 }
824
825 /**
826  * Get the Group property that contains the id properties for ID id.  Set create_if_needed
827  * to create the Group property and attach it to id if it doesn't exist; otherwise
828  * the function will return NULL if there's no Group property attached to the ID.
829  */
830 IDProperty *IDP_GetProperties(ID *id, const bool create_if_needed)
831 {
832   if (id->properties) {
833     return id->properties;
834   }
835   else {
836     if (create_if_needed) {
837       id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
838       id->properties->type = IDP_GROUP;
839       /* don't overwrite the data's name and type
840        * some functions might need this if they
841        * don't have a real ID, should be named elsewhere - Campbell */
842       /* strcpy(id->name, "top_level_group");*/
843     }
844     return id->properties;
845   }
846 }
847
848 /**
849  * \param is_strict: When false treat missing items as a match */
850 bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is_strict)
851 {
852   if (prop1 == NULL && prop2 == NULL) {
853     return true;
854   }
855   else if (prop1 == NULL || prop2 == NULL) {
856     return is_strict ? false : true;
857   }
858   else if (prop1->type != prop2->type) {
859     return false;
860   }
861
862   switch (prop1->type) {
863     case IDP_INT:
864       return (IDP_Int(prop1) == IDP_Int(prop2));
865     case IDP_FLOAT:
866 #if !defined(NDEBUG) && defined(WITH_PYTHON)
867     {
868       float p1 = IDP_Float(prop1);
869       float p2 = IDP_Float(prop2);
870       if ((p1 != p2) && ((fabsf(p1 - p2) / max_ff(p1, p2)) < 0.001f)) {
871         printf(
872             "WARNING: Comparing two float properties that have nearly the same value (%f vs. "
873             "%f)\n",
874             p1,
875             p2);
876         printf("    p1: ");
877         IDP_print(prop1);
878         printf("    p2: ");
879         IDP_print(prop2);
880       }
881     }
882 #endif
883       return (IDP_Float(prop1) == IDP_Float(prop2));
884     case IDP_DOUBLE:
885       return (IDP_Double(prop1) == IDP_Double(prop2));
886     case IDP_STRING: {
887       return (((prop1->len == prop2->len) &&
888                STREQLEN(IDP_String(prop1), IDP_String(prop2), (size_t)prop1->len)));
889     }
890     case IDP_ARRAY:
891       if (prop1->len == prop2->len && prop1->subtype == prop2->subtype) {
892         return (memcmp(IDP_Array(prop1),
893                        IDP_Array(prop2),
894                        idp_size_table[(int)prop1->subtype] * (size_t)prop1->len) == 0);
895       }
896       return false;
897     case IDP_GROUP: {
898       IDProperty *link1, *link2;
899
900       if (is_strict && prop1->len != prop2->len) {
901         return false;
902       }
903
904       for (link1 = prop1->data.group.first; link1; link1 = link1->next) {
905         link2 = IDP_GetPropertyFromGroup(prop2, link1->name);
906
907         if (!IDP_EqualsProperties_ex(link1, link2, is_strict)) {
908           return false;
909         }
910       }
911
912       return true;
913     }
914     case IDP_IDPARRAY: {
915       IDProperty *array1 = IDP_IDPArray(prop1);
916       IDProperty *array2 = IDP_IDPArray(prop2);
917       int i;
918
919       if (prop1->len != prop2->len) {
920         return false;
921       }
922
923       for (i = 0; i < prop1->len; i++) {
924         if (!IDP_EqualsProperties_ex(&array1[i], &array2[i], is_strict)) {
925           return false;
926         }
927       }
928       return true;
929     }
930     case IDP_ID:
931       return (IDP_Id(prop1) == IDP_Id(prop2));
932     default:
933       BLI_assert(0);
934       break;
935   }
936
937   return true;
938 }
939
940 bool IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2)
941 {
942   return IDP_EqualsProperties_ex(prop1, prop2, true);
943 }
944
945 /**
946  * Allocate a new ID.
947  *
948  * This function takes three arguments: the ID property type, a union which defines
949  * it's initial value, and a name.
950  *
951  * The union is simple to use; see the top of this header file for its definition.
952  * An example of using this function:
953  *
954  * \code{.c}
955  * IDPropertyTemplate val;
956  * IDProperty *group, *idgroup, *color;
957  * group = IDP_New(IDP_GROUP, val, "group1"); //groups don't need a template.
958  *
959  * val.array.len = 4
960  * val.array.type = IDP_FLOAT;
961  * color = IDP_New(IDP_ARRAY, val, "color1");
962  *
963  * idgroup = IDP_GetProperties(some_id, 1);
964  * IDP_AddToGroup(idgroup, color);
965  * IDP_AddToGroup(idgroup, group);
966  * \endcode
967  *
968  * Note that you MUST either attach the id property to an id property group with
969  * IDP_AddToGroup or MEM_freeN the property, doing anything else might result in
970  * a memory leak.
971  */
972 IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *name)
973 {
974   IDProperty *prop = NULL;
975
976   switch (type) {
977     case IDP_INT:
978       prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
979       prop->data.val = val->i;
980       break;
981     case IDP_FLOAT:
982       prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
983       *(float *)&prop->data.val = val->f;
984       break;
985     case IDP_DOUBLE:
986       prop = MEM_callocN(sizeof(IDProperty), "IDProperty double");
987       *(double *)&prop->data.val = val->d;
988       break;
989     case IDP_ARRAY: {
990       /* for now, we only support float and int and double arrays */
991       if ((val->array.type == IDP_FLOAT) || (val->array.type == IDP_INT) ||
992           (val->array.type == IDP_DOUBLE) || (val->array.type == IDP_GROUP)) {
993         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
994         prop->subtype = val->array.type;
995         if (val->array.len) {
996           prop->data.pointer = MEM_callocN(
997               idp_size_table[val->array.type] * (size_t)val->array.len, "id property array");
998         }
999         prop->len = prop->totallen = val->array.len;
1000         break;
1001       }
1002       CLOG_ERROR(&LOG, "bad array type.");
1003       return NULL;
1004     }
1005     case IDP_STRING: {
1006       const char *st = val->string.str;
1007
1008       prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
1009       if (val->string.subtype == IDP_STRING_SUB_BYTE) {
1010         /* note, intentionally not null terminated */
1011         if (st == NULL) {
1012           prop->data.pointer = MEM_mallocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
1013           *IDP_String(prop) = '\0';
1014           prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
1015           prop->len = 0;
1016         }
1017         else {
1018           prop->data.pointer = MEM_mallocN((size_t)val->string.len, "id property string 2");
1019           prop->len = prop->totallen = val->string.len;
1020           memcpy(prop->data.pointer, st, (size_t)val->string.len);
1021         }
1022         prop->subtype = IDP_STRING_SUB_BYTE;
1023       }
1024       else {
1025         if (st == NULL || val->string.len <= 1) {
1026           prop->data.pointer = MEM_mallocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
1027           *IDP_String(prop) = '\0';
1028           prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
1029           /* NULL string, has len of 1 to account for null byte. */
1030           prop->len = 1;
1031         }
1032         else {
1033           BLI_assert((int)val->string.len <= (int)strlen(st) + 1);
1034           prop->data.pointer = MEM_mallocN((size_t)val->string.len, "id property string 3");
1035           memcpy(prop->data.pointer, st, (size_t)val->string.len - 1);
1036           IDP_String(prop)[val->string.len - 1] = '\0';
1037           prop->len = prop->totallen = val->string.len;
1038         }
1039         prop->subtype = IDP_STRING_SUB_UTF8;
1040       }
1041       break;
1042     }
1043     case IDP_GROUP: {
1044       /* Values are set properly by calloc. */
1045       prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
1046       break;
1047     }
1048     case IDP_ID: {
1049       prop = MEM_callocN(sizeof(IDProperty), "IDProperty datablock");
1050       prop->data.pointer = (void *)val->id;
1051       prop->type = IDP_ID;
1052       id_us_plus(IDP_Id(prop));
1053       break;
1054     }
1055     default: {
1056       prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
1057       break;
1058     }
1059   }
1060
1061   prop->type = type;
1062   BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
1063
1064   return prop;
1065 }
1066
1067 /**
1068  * \note This will free allocated data, all child properties of arrays and groups, and unlink IDs!
1069  * But it does not free the actual IDProperty struct itself.
1070  */
1071 void IDP_FreeProperty_ex(IDProperty *prop, const bool do_id_user)
1072 {
1073   switch (prop->type) {
1074     case IDP_ARRAY:
1075       IDP_FreeArray(prop);
1076       break;
1077     case IDP_STRING:
1078       IDP_FreeString(prop);
1079       break;
1080     case IDP_GROUP:
1081       IDP_FreeGroup(prop, do_id_user);
1082       break;
1083     case IDP_IDPARRAY:
1084       IDP_FreeIDPArray(prop, do_id_user);
1085       break;
1086     case IDP_ID:
1087       if (do_id_user) {
1088         id_us_min(IDP_Id(prop));
1089       }
1090       break;
1091   }
1092 }
1093
1094 void IDP_FreeProperty(IDProperty *prop)
1095 {
1096   IDP_FreeProperty_ex(prop, true);
1097 }
1098
1099 void IDP_ClearProperty(IDProperty *prop)
1100 {
1101   IDP_FreeProperty(prop);
1102   prop->data.pointer = NULL;
1103   prop->len = prop->totallen = 0;
1104 }
1105
1106 void IDP_Reset(IDProperty *prop, const IDProperty *reference)
1107 {
1108   if (prop == NULL) {
1109     return;
1110   }
1111   IDP_ClearProperty(prop);
1112   if (reference != NULL) {
1113     IDP_MergeGroup(prop, reference, true);
1114   }
1115 }
1116
1117 /** \} */