Fix slow resizing of ID property arrays with more than 1619 items, it incorrectly
[blender.git] / source / blender / blenkernel / intern / idprop.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * Contributor(s): Joseph Eagar
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/blenkernel/intern/idprop.c
27  *  \ingroup bke
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stddef.h>
33 #include <string.h>
34
35 #include "BLI_utildefines.h"
36 #include "BLI_string.h"
37 #include "BLI_listbase.h"
38
39 #include "BKE_idprop.h"
40 #include "BKE_library.h"
41
42 #include "MEM_guardedalloc.h"
43
44 /* IDPropertyTemplate is a union in DNA_ID.h */
45
46 /*local size table.*/
47 static char idp_size_table[] = {
48         1, /*strings*/
49         sizeof(int),
50         sizeof(float),
51         sizeof(float) * 3, /*Vector type, deprecated*/
52         sizeof(float) * 16, /*Matrix type, deprecated*/
53         0, /*arrays don't have a fixed size*/
54         sizeof(ListBase), /*Group type*/
55         sizeof(void *),
56         sizeof(double)
57 };
58
59 /* ------------Property Array Type ----------- */
60 #define GETPROP(prop, i) (((IDProperty *)(prop)->data.pointer) + (i))
61
62 /* --------- property array type -------------*/
63
64 /**
65  * \note as a start to move away from the stupid IDP_New function, this type
66  * has it's own allocation function.
67  */
68 IDProperty *IDP_NewIDPArray(const char *name)
69 {
70         IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty prop array");
71         prop->type = IDP_IDPARRAY;
72         prop->len = 0;
73         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
74         
75         return prop;
76 }
77
78 IDProperty *IDP_CopyIDPArray(IDProperty *array)
79 {
80         /* don't use MEM_dupallocN because this may be part of an array */
81         IDProperty *narray = MEM_mallocN(sizeof(IDProperty), "IDP_CopyIDPArray"), *tmp;
82         int i;
83
84         *narray = *array;
85
86         narray->data.pointer = MEM_dupallocN(array->data.pointer);
87         for (i = 0; i < narray->len; i++) {
88                 /* ok, the copy functions always allocate a new structure,
89                  * which doesn't work here.  instead, simply copy the
90                  * contents of the new structure into the array cell,
91                  * then free it.  this makes for more maintainable
92                  * code than simply reimplementing the copy functions
93                  * in this loop.*/
94                 tmp = IDP_CopyProperty(GETPROP(narray, i));
95                 memcpy(GETPROP(narray, i), tmp, sizeof(IDProperty));
96                 MEM_freeN(tmp);
97         }
98         
99         return narray;
100 }
101
102 void IDP_FreeIDPArray(IDProperty *prop)
103 {
104         int i;
105         
106         for (i = 0; i < prop->len; i++)
107                 IDP_FreeProperty(GETPROP(prop, i));
108
109         if (prop->data.pointer)
110                 MEM_freeN(prop->data.pointer);
111 }
112
113 /*shallow copies item*/
114 void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item)
115 {
116         IDProperty *old = GETPROP(prop, index);
117         if (index >= prop->len || index < 0) return;
118         if (item != old) IDP_FreeProperty(old);
119         
120         memcpy(GETPROP(prop, index), item, sizeof(IDProperty));
121 }
122
123 IDProperty *IDP_GetIndexArray(IDProperty *prop, int index)
124 {
125         return GETPROP(prop, index);
126 }
127
128 void IDP_AppendArray(IDProperty *prop, IDProperty *item)
129 {
130         IDP_ResizeIDPArray(prop, prop->len + 1);
131         IDP_SetIndexArray(prop, prop->len - 1, item);
132 }
133
134 void IDP_ResizeIDPArray(IDProperty *prop, int newlen)
135 {
136         void *newarr;
137         int newsize = newlen;
138
139         /*first check if the array buffer size has room*/
140         /*if newlen is 200 chars less then totallen, reallocate anyway*/
141         if (newlen <= prop->totallen) {
142                 if (newlen < prop->len && prop->totallen - newlen < 200) {
143                         int i;
144
145                         for (i = newlen; i < prop->len; i++)
146                                 IDP_FreeProperty(GETPROP(prop, i));
147
148                         prop->len = newlen;
149                         return;
150                 }
151                 else if (newlen >= prop->len) {
152                         prop->len = newlen;
153                         return;
154                 }
155         }
156
157         /* - Note: This code comes from python, here's the corresponding comment. - */
158         /* This over-allocates proportional to the list size, making room
159          * for additional growth.  The over-allocation is mild, but is
160          * enough to give linear-time amortized behavior over a long
161          * sequence of appends() in the presence of a poorly-performing
162          * system realloc().
163          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
164          */
165         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
166
167         newarr = MEM_callocN(sizeof(IDProperty) * newsize, "idproperty array resized");
168         if (newlen >= prop->len) {
169                 /* newlen is bigger */
170                 memcpy(newarr, prop->data.pointer, prop->len * sizeof(IDProperty));
171         }
172         else {
173                 int i;
174                 /* newlen is smaller */
175                 for (i = newlen; i < prop->len; i++) {
176                         IDP_FreeProperty(GETPROP(prop, i));
177                 }
178                 memcpy(newarr, prop->data.pointer, newlen * sizeof(IDProperty));
179         }
180
181         if (prop->data.pointer)
182                 MEM_freeN(prop->data.pointer);
183         prop->data.pointer = newarr;
184         prop->len = newlen;
185         prop->totallen = newsize;
186 }
187
188 /* ----------- Numerical Array Type ----------- */
189 static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr)
190 {
191         if (prop->subtype != IDP_GROUP)
192                 return;
193
194         if (newlen >= prop->len) {
195                 /* bigger */
196                 IDProperty **array = newarr;
197                 IDPropertyTemplate val;
198                 int a;
199
200                 for (a = prop->len; a < newlen; a++) {
201                         val.i = 0; /* silence MSVC warning about uninitialized var when debugging */
202                         array[a] = IDP_New(IDP_GROUP, &val, "IDP_ResizeArray group");
203                 }
204         }
205         else {
206                 /* smaller */
207                 IDProperty **array = prop->data.pointer;
208                 int a;
209
210                 for (a = newlen; a < prop->len; a++) {
211                         IDP_FreeProperty(array[a]);
212                         MEM_freeN(array[a]);
213                 }
214         }
215 }
216
217 /*this function works for strings too!*/
218 void IDP_ResizeArray(IDProperty *prop, int newlen)
219 {
220         void *newarr;
221         int newsize = newlen;
222
223         /*first check if the array buffer size has room*/
224         /*if newlen is 200 chars less then totallen, reallocate anyway*/
225         if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
226                 idp_resize_group_array(prop, newlen, prop->data.pointer);
227                 prop->len = newlen;
228                 return;
229         }
230
231         /* - Note: This code comes from python, here's the corresponding comment. - */
232         /* This over-allocates proportional to the list size, making room
233          * for additional growth.  The over-allocation is mild, but is
234          * enough to give linear-time amortized behavior over a long
235          * sequence of appends() in the presence of a poorly-performing
236          * system realloc().
237          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
238          */
239         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
240
241         newarr = MEM_callocN(idp_size_table[(int)prop->subtype] * newsize, "idproperty array resized");
242         if (newlen >= prop->len) {
243                 /* newlen is bigger */
244                 memcpy(newarr, prop->data.pointer, prop->len * idp_size_table[(int)prop->subtype]);
245                 idp_resize_group_array(prop, newlen, newarr);
246         }
247         else {
248                 /* newlen is smaller */
249                 idp_resize_group_array(prop, newlen, newarr);
250                 memcpy(newarr, prop->data.pointer, newlen * idp_size_table[(int)prop->subtype]);
251         }
252
253         MEM_freeN(prop->data.pointer);
254         prop->data.pointer = newarr;
255         prop->len = newlen;
256         prop->totallen = newsize;
257 }
258
259 void IDP_FreeArray(IDProperty *prop)
260 {
261         if (prop->data.pointer) {
262                 idp_resize_group_array(prop, 0, NULL);
263                 MEM_freeN(prop->data.pointer);
264         }
265 }
266
267
268 static IDProperty *idp_generic_copy(IDProperty *prop)
269 {
270         IDProperty *newp = MEM_callocN(sizeof(IDProperty), "IDProperty array dup");
271
272         BLI_strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
273         newp->type = prop->type;
274         newp->flag = prop->flag;
275         newp->data.val = prop->data.val;
276         newp->data.val2 = prop->data.val2;
277
278         return newp;
279 }
280
281 static IDProperty *IDP_CopyArray(IDProperty *prop)
282 {
283         IDProperty *newp = idp_generic_copy(prop);
284
285         if (prop->data.pointer) {
286                 newp->data.pointer = MEM_dupallocN(prop->data.pointer);
287
288                 if (prop->type == IDP_GROUP) {
289                         IDProperty **array = newp->data.pointer;
290                         int a;
291
292                         for (a = 0; a < prop->len; a++)
293                                 array[a] = IDP_CopyProperty(array[a]);
294                 }
295         }
296         newp->len = prop->len;
297         newp->subtype = prop->subtype;
298         newp->totallen = prop->totallen;
299
300         return newp;
301 }
302
303 /* ---------- String Type ------------ */
304 IDProperty *IDP_NewString(const char *st, const char *name, int maxlen)
305 {
306         IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
307
308         if (st == NULL) {
309                 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
310                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
311                 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
312         }
313         else {
314                 int stlen = strlen(st);
315
316                 if (maxlen > 0 && maxlen < stlen)
317                         stlen = maxlen;
318
319                 stlen++; /* null terminator '\0' */
320
321                 prop->data.pointer = MEM_callocN(stlen, "id property string 2");
322                 prop->len = prop->totallen = stlen;
323                 BLI_strncpy(prop->data.pointer, st, stlen);
324         }
325
326         prop->type = IDP_STRING;
327         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
328
329         return prop;
330 }
331
332 static IDProperty *IDP_CopyString(IDProperty *prop)
333 {
334         IDProperty *newp = idp_generic_copy(prop);
335
336         if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer);
337         newp->len = prop->len;
338         newp->subtype = prop->subtype;
339         newp->totallen = prop->totallen;
340
341         return newp;
342 }
343
344
345 void IDP_AssignString(IDProperty *prop, const char *st, int maxlen)
346 {
347         int stlen = strlen(st);
348
349         if (maxlen > 0 && maxlen < stlen)
350                 stlen = maxlen;
351
352         if (prop->subtype == IDP_STRING_SUB_BYTE) {
353                 IDP_ResizeArray(prop, stlen);
354                 memcpy(prop->data.pointer, st, stlen);
355         }
356         else {
357                 stlen++; /* make room for null byte */
358                 IDP_ResizeArray(prop, stlen);
359                 BLI_strncpy(prop->data.pointer, st, stlen);
360         }
361 }
362
363 void IDP_ConcatStringC(IDProperty *prop, const char *st)
364 {
365         int newlen;
366
367         newlen = prop->len + strlen(st);
368         /* we have to remember that prop->len includes the null byte for strings.
369          * so there's no need to add +1 to the resize function.*/
370         IDP_ResizeArray(prop, newlen);
371         strcat(prop->data.pointer, st);
372 }
373
374 void IDP_ConcatString(IDProperty *str1, IDProperty *append)
375 {
376         int newlen;
377
378         /* since ->len for strings includes the NULL byte, we have to subtract one or
379          * we'll get an extra null byte after each concatenation operation.*/
380         newlen = str1->len + append->len - 1;
381         IDP_ResizeArray(str1, newlen);
382         strcat(str1->data.pointer, append->data.pointer);
383 }
384
385 void IDP_FreeString(IDProperty *prop)
386 {
387         if (prop->data.pointer)
388                 MEM_freeN(prop->data.pointer);
389 }
390
391
392 /*-------- ID Type, not in use yet -------*/
393
394 void IDP_LinkID(IDProperty *prop, ID *id)
395 {
396         if (prop->data.pointer) ((ID *)prop->data.pointer)->us--;
397         prop->data.pointer = id;
398         id_us_plus(id);
399 }
400
401 void IDP_UnlinkID(IDProperty *prop)
402 {
403         ((ID *)prop->data.pointer)->us--;
404 }
405
406 /*-------- Group Functions -------*/
407
408 /*checks if a property with the same name as prop exists, and if so replaces it.*/
409 static IDProperty *IDP_CopyGroup(IDProperty *prop)
410 {
411         IDProperty *newp = idp_generic_copy(prop), *link;
412         newp->len = prop->len;
413         
414         for (link = prop->data.group.first; link; link = link->next) {
415                 BLI_addtail(&newp->data.group, IDP_CopyProperty(link));
416         }
417
418         return newp;
419 }
420
421 /* use for syncing proxies.
422  * When values name and types match, copy the values, else ignore */
423 void IDP_SyncGroupValues(IDProperty *dest, IDProperty *src)
424 {
425         IDProperty *other, *prop;
426         for (prop = src->data.group.first; prop; prop = prop->next) {
427                 other = BLI_findstring(&dest->data.group, prop->name, offsetof(IDProperty, name));
428                 if (other && prop->type == other->type) {
429                         switch (prop->type) {
430                                 case IDP_INT:
431                                 case IDP_FLOAT:
432                                 case IDP_DOUBLE:
433                                         other->data = prop->data;
434                                         break;
435                                 case IDP_GROUP:
436                                         IDP_SyncGroupValues(other, prop);
437                                         break;
438                                 default:
439                                 {
440                                         IDProperty *tmp = other;
441                                         IDProperty *copy = IDP_CopyProperty(prop);
442
443                                         BLI_insertlinkafter(&dest->data.group, other, copy);
444                                         BLI_remlink(&dest->data.group, tmp);
445
446                                         IDP_FreeProperty(tmp);
447                                         MEM_freeN(tmp);
448                                 }
449                         }
450                 }
451         }
452 }
453
454 /**
455  * Replaces all properties with the same name in a destination group from a source group.
456  */
457 void IDP_ReplaceGroupInGroup(IDProperty *dest, IDProperty *src)
458 {
459         IDProperty *loop, *prop;
460         for (prop = src->data.group.first; prop; prop = prop->next) {
461                 for (loop = dest->data.group.first; loop; loop = loop->next) {
462                         if (STREQ(loop->name, prop->name)) {
463                                 IDProperty *copy = IDP_CopyProperty(prop);
464
465                                 BLI_insertlinkafter(&dest->data.group, loop, copy);
466
467                                 BLI_remlink(&dest->data.group, loop);
468                                 IDP_FreeProperty(loop);
469                                 MEM_freeN(loop);
470                                 break;
471                         }
472                 }
473
474                 /* only add at end if not added yet */
475                 if (loop == NULL) {
476                         IDProperty *copy = IDP_CopyProperty(prop);
477                         dest->len++;
478                         BLI_addtail(&dest->data.group, copy);
479                 }
480         }
481 }
482
483 /**
484  * Checks if a property with the same name as prop exists, and if so replaces it.
485  * Use this to preserve order!
486  */
487 void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
488 {
489         IDProperty *loop;
490         if ((loop = IDP_GetPropertyFromGroup(group, prop->name))) {
491                 BLI_insertlinkafter(&group->data.group, loop, prop);
492                 
493                 BLI_remlink(&group->data.group, loop);
494                 IDP_FreeProperty(loop);
495                 MEM_freeN(loop);
496         }
497         else {
498                 group->len++;
499                 BLI_addtail(&group->data.group, prop);
500         }
501 }
502
503 /*
504  * If a property is missing in \a dest, add it.
505  */
506 void IDP_MergeGroup(IDProperty *dest, IDProperty *src, const int do_overwrite)
507 {
508         IDProperty *prop;
509
510         if (do_overwrite) {
511                 for (prop = src->data.group.first; prop; prop = prop->next) {
512                         IDProperty *copy = IDP_CopyProperty(prop);
513                         IDP_ReplaceInGroup(dest, copy);
514                 }
515         }
516         else {
517                 for (prop = src->data.group.first; prop; prop = prop->next) {
518                         if (IDP_GetPropertyFromGroup(dest, prop->name) == NULL) {
519                                 IDProperty *copy = IDP_CopyProperty(prop);
520                                 dest->len++;
521                                 BLI_addtail(&dest->data.group, copy);
522                         }
523                 }
524         }
525 }
526
527 /**
528  * This function has a sanity check to make sure ID properties with the same name don't
529  * get added to the group.
530  *
531  * The sanity check just means the property is not added to the group if another property
532  * exists with the same name; the client code using ID properties then needs to detect this
533  * (the function that adds new properties to groups, IDP_AddToGroup,returns 0 if a property can't
534  * be added to the group, and 1 if it can) and free the property.
535  *
536  * Currently the code to free ID properties is designed to leave the actual struct
537  * you pass it un-freed, this is needed for how the system works.  This means
538  * to free an ID property, you first call IDP_FreeProperty then MEM_freeN the
539  * struct.  In the future this will just be IDP_FreeProperty and the code will
540  * be reorganized to work properly.
541  */
542 int IDP_AddToGroup(IDProperty *group, IDProperty *prop)
543 {
544         if (IDP_GetPropertyFromGroup(group, prop->name) == NULL) {
545                 group->len++;
546                 BLI_addtail(&group->data.group, prop);
547                 return 1;
548         }
549
550         return 0;
551 }
552
553 /**
554  * This is the same as IDP_AddToGroup, only you pass an item
555  * in the group list to be inserted after.
556  */
557 int IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
558 {
559         if (IDP_GetPropertyFromGroup(group, pnew->name) == NULL) {
560                 group->len++;
561                 BLI_insertlinkafter(&group->data.group, previous, pnew);
562                 return 1;
563         }
564
565         return 0;
566 }
567
568 /**
569  * \note this does not free the property!!
570  *
571  * To free the property, you have to do:
572  * IDP_FreeProperty(prop); //free all subdata
573  * MEM_freeN(prop); //free property struct itself
574  */
575 void IDP_RemFromGroup(IDProperty *group, IDProperty *prop)
576 {
577         group->len--;
578         BLI_remlink(&group->data.group, prop);
579 }
580
581 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, const char *name)
582 {
583         return (IDProperty *)BLI_findstring(&prop->data.group, name, offsetof(IDProperty, name));
584 }
585 /** same as above but ensure type match */
586 IDProperty *IDP_GetPropertyTypeFromGroup(IDProperty *prop, const char *name, const char type)
587 {
588         IDProperty *idprop = IDP_GetPropertyFromGroup(prop, name);
589         return (idprop && idprop->type == type) ? idprop : NULL;
590 }
591
592 typedef struct IDPIter {
593         void *next;
594         IDProperty *parent;
595 } IDPIter;
596
597 /**
598  * Get an iterator to iterate over the members of an id property group.
599  * Note that this will automatically free the iterator once iteration is complete;
600  * if you stop the iteration before hitting the end, make sure to call
601  * IDP_FreeIterBeforeEnd().
602  */
603 void *IDP_GetGroupIterator(IDProperty *prop)
604 {
605         IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter");
606         iter->next = prop->data.group.first;
607         iter->parent = prop;
608         return (void *) iter;
609 }
610
611 /**
612  * Returns the next item in the iteration.  To use, simple for a loop like the following:
613  * while (IDP_GroupIterNext(iter) != NULL) {
614  *     ...
615  * }
616  */
617 IDProperty *IDP_GroupIterNext(void *vself)
618 {
619         IDPIter *self = (IDPIter *) vself;
620         Link *next = (Link *) self->next;
621         if (self->next == NULL) {
622                 MEM_freeN(self);
623                 return NULL;
624         }
625
626         self->next = next->next;
627         return (void *) next;
628 }
629
630 /**
631  * Frees the iterator pointed to at vself, only use this if iteration is stopped early;
632  * when the iterator hits the end of the list it'll automatically free itself.\
633  */
634 void IDP_FreeIterBeforeEnd(void *vself)
635 {
636         MEM_freeN(vself);
637 }
638
639 /* Ok, the way things work, Groups free the ID Property structs of their children.
640  * This is because all ID Property freeing functions free only direct data (not the ID Property
641  * struct itself), but for Groups the child properties *are* considered
642  * direct data. */
643 static void IDP_FreeGroup(IDProperty *prop)
644 {
645         IDProperty *loop;
646         for (loop = prop->data.group.first; loop; loop = loop->next) {
647                 IDP_FreeProperty(loop);
648         }
649         BLI_freelistN(&prop->data.group);
650 }
651
652
653 /*-------- Main Functions --------*/
654 IDProperty *IDP_CopyProperty(IDProperty *prop)
655 {
656         switch (prop->type) {
657                 case IDP_GROUP: return IDP_CopyGroup(prop);
658                 case IDP_STRING: return IDP_CopyString(prop);
659                 case IDP_ARRAY: return IDP_CopyArray(prop);
660                 case IDP_IDPARRAY: return IDP_CopyIDPArray(prop);
661                 default: return idp_generic_copy(prop);
662         }
663 }
664
665 /**
666  * Get the Group property that contains the id properties for ID id.  Set create_if_needed
667  * to create the Group property and attach it to id if it doesn't exist; otherwise
668  * the function will return NULL if there's no Group property attached to the ID.
669  */
670 IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
671 {
672         if (id->properties) {
673                 return id->properties;
674         }
675         else {
676                 if (create_if_needed) {
677                         id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
678                         id->properties->type = IDP_GROUP;
679                         /* don't overwrite the data's name and type
680                          * some functions might need this if they
681                          * don't have a real ID, should be named elsewhere - Campbell */
682                         /* strcpy(id->name, "top_level_group");*/
683                 }
684                 return id->properties;
685         }
686 }
687
688 /**
689  * \param is_strict When FALSE treat missing items as a match */
690 int IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const int is_strict)
691 {
692         if (prop1 == NULL && prop2 == NULL)
693                 return 1;
694         else if (prop1 == NULL || prop2 == NULL)
695                 return is_strict ? 0 : 1;
696         else if (prop1->type != prop2->type)
697                 return 0;
698
699         switch (prop1->type) {
700                 case IDP_INT:
701                         return (IDP_Int(prop1) == IDP_Int(prop2));
702                 case IDP_FLOAT:
703                         return (IDP_Float(prop1) == IDP_Float(prop2));
704                 case IDP_DOUBLE:
705                         return (IDP_Double(prop1) == IDP_Double(prop2));
706                 case IDP_STRING:
707                         return ((prop1->len == prop2->len) && strncmp(IDP_String(prop1), IDP_String(prop2), prop1->len) == 0);
708                 case IDP_ARRAY:
709                         if (prop1->len == prop2->len && prop1->subtype == prop2->subtype) {
710                                 return memcmp(IDP_Array(prop1), IDP_Array(prop2), idp_size_table[(int)prop1->subtype] * prop1->len);
711                         }
712                         else {
713                                 return 0;
714                         }
715                 case IDP_GROUP:
716                 {
717                         IDProperty *link1, *link2;
718
719                         if (is_strict && prop1->len != prop2->len)
720                                 return 0;
721
722                         for (link1 = prop1->data.group.first; link1; link1 = link1->next) {
723                                 link2 = IDP_GetPropertyFromGroup(prop2, link1->name);
724
725                                 if (!IDP_EqualsProperties_ex(link1, link2, is_strict))
726                                         return 0;
727                         }
728
729                         return 1;
730                 }
731                 case IDP_IDPARRAY:
732                 {
733                         IDProperty *array1 = IDP_IDPArray(prop1);
734                         IDProperty *array2 = IDP_IDPArray(prop2);
735                         int i;
736
737                         if (prop1->len != prop2->len)
738                                 return 0;
739
740                         for (i = 0; i < prop1->len; i++)
741                                 if (!IDP_EqualsProperties(&array1[i], &array2[i]))
742                                         return 0;
743                         return 1;
744                 }
745                 default:
746                         /* should never get here */
747                         BLI_assert(0);
748                         break;
749         }
750
751         return 1;
752 }
753
754 int IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2)
755 {
756         return IDP_EqualsProperties_ex(prop1, prop2, TRUE);
757 }
758
759 /**
760  * Allocate a new ID.
761  *
762  * This function takes three arguments: the ID property type, a union which defines
763  * it's initial value, and a name.
764  *
765  * The union is simple to use; see the top of this header file for its definition.
766  * An example of using this function:
767  *
768  *     IDPropertyTemplate val;
769  *     IDProperty *group, *idgroup, *color;
770  *     group = IDP_New(IDP_GROUP, val, "group1"); //groups don't need a template.
771  *
772  *     val.array.len = 4
773  *     val.array.type = IDP_FLOAT;
774  *     color = IDP_New(IDP_ARRAY, val, "color1");
775  *
776  *     idgroup = IDP_GetProperties(some_id, 1);
777  *     IDP_AddToGroup(idgroup, color);
778  *     IDP_AddToGroup(idgroup, group);
779  *
780  * Note that you MUST either attach the id property to an id property group with
781  * IDP_AddToGroup or MEM_freeN the property, doing anything else might result in
782  * a memory leak.
783  */
784 IDProperty *IDP_New(const int type, const IDPropertyTemplate *val, const char *name)
785 {
786         IDProperty *prop = NULL;
787
788         switch (type) {
789                 case IDP_INT:
790                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
791                         prop->data.val = val->i;
792                         break;
793                 case IDP_FLOAT:
794                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
795                         *(float *)&prop->data.val = val->f;
796                         break;
797                 case IDP_DOUBLE:
798                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
799                         *(double *)&prop->data.val = val->d;
800                         break;
801                 case IDP_ARRAY:
802                 {
803                         /* for now, we only support float and int and double arrays */
804                         if ( (val->array.type == IDP_FLOAT) ||
805                              (val->array.type == IDP_INT) ||
806                              (val->array.type == IDP_DOUBLE) ||
807                              (val->array.type == IDP_GROUP) )
808                         {
809                                 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
810                                 prop->subtype = val->array.type;
811                                 if (val->array.len)
812                                         prop->data.pointer = MEM_callocN(idp_size_table[val->array.type] * val->array.len, "id property array");
813                                 prop->len = prop->totallen = val->array.len;
814                                 break;
815                         }
816                         else {
817                                 return NULL;
818                         }
819                 }
820                 case IDP_STRING:
821                 {
822                         const char *st = val->string.str;
823
824                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
825                         if (val->string.subtype == IDP_STRING_SUB_BYTE) {
826                                 /* note, intentionally not null terminated */
827                                 if (st == NULL) {
828                                         prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
829                                         prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
830                                         prop->len = 0;
831                                 }
832                                 else {
833                                         prop->data.pointer = MEM_mallocN(val->string.len, "id property string 2");
834                                         prop->len = prop->totallen = val->string.len;
835                                         memcpy(prop->data.pointer, st, val->string.len);
836                                 }
837                                 prop->subtype = IDP_STRING_SUB_BYTE;
838                         }
839                         else {
840                                 if (st == NULL) {
841                                         prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
842                                         prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
843                                         prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
844                                 }
845                                 else {
846                                         int stlen = strlen(st) + 1;
847                                         prop->data.pointer = MEM_mallocN(stlen, "id property string 3");
848                                         prop->len = prop->totallen = stlen;
849                                         memcpy(prop->data.pointer, st, stlen);
850                                 }
851                                 prop->subtype = IDP_STRING_SUB_UTF8;
852                         }
853                         break;
854                 }
855                 case IDP_GROUP:
856                 {
857                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
858                         /* heh I think all needed values are set properly by calloc anyway :) */
859                         break;
860                 }
861                 default:
862                 {
863                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
864                         break;
865                 }
866         }
867
868         prop->type = type;
869         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
870         
871         return prop;
872 }
873
874 /**
875  * \note this will free all child properties of list arrays and groups!
876  * Also, note that this does NOT unlink anything!  Plus it doesn't free
877  * the actual struct IDProperty struct either.
878  */
879 void IDP_FreeProperty(IDProperty *prop)
880 {
881         switch (prop->type) {
882                 case IDP_ARRAY:
883                         IDP_FreeArray(prop);
884                         break;
885                 case IDP_STRING:
886                         IDP_FreeString(prop);
887                         break;
888                 case IDP_GROUP:
889                         IDP_FreeGroup(prop);
890                         break;
891                 case IDP_IDPARRAY:
892                         IDP_FreeIDPArray(prop);
893                         break;
894         }
895 }
896
897 void IDP_ClearProperty(IDProperty *prop)
898 {
899         IDP_FreeProperty(prop);
900         prop->data.pointer = NULL;
901         prop->len = prop->totallen = 0;
902 }
903
904 /**
905  * Unlinks any struct IDProperty<->ID linkage that might be going on.
906  *
907  * \note currently unused
908  */
909 void IDP_UnlinkProperty(IDProperty *prop)
910 {
911         switch (prop->type) {
912                 case IDP_ID:
913                         IDP_UnlinkID(prop);
914         }
915 }