4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21 * All rights reserved.
23 * Contributor(s): Joseph Eagar
25 * ***** END GPL LICENSE BLOCK *****
32 #include "DNA_listBase.h"
35 #include "BKE_idprop.h"
36 #include "BKE_global.h"
37 #include "BKE_library.h"
38 #include "BKE_utildefines.h"
40 #include "BLI_blenlib.h"
42 #include "MEM_guardedalloc.h"
44 #define BSTR_EQ(a, b) (*(a) == *(b) && !strcmp(a, b))
46 /* IDPropertyTemplate is a union in DNA_ID.h */
49 static char idp_size_table[] = {
53 sizeof(float)*3, /*Vector type, deprecated*/
54 sizeof(float)*16, /*Matrix type, deprecated*/
55 0, /*arrays don't have a fixed size*/
56 sizeof(ListBase), /*Group type*/
61 /* ------------Property Array Type ----------- */
62 #define GETPROP(prop, i) (((IDProperty*)(prop)->data.pointer)+(i))
64 /* --------- property array type -------------*/
66 /*note: as a start to move away from the stupid IDP_New function, this type
67 has it's own allocation function.*/
68 IDProperty *IDP_NewIDPArray(const char *name)
70 IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty prop array");
71 prop->type = IDP_IDPARRAY;
73 BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
78 IDProperty *IDP_CopyIDPArray(IDProperty *array)
80 IDProperty *narray = MEM_dupallocN(array), *tmp;
83 narray->data.pointer = MEM_dupallocN(array->data.pointer);
84 for (i=0; i<narray->len; i++) {
85 /*ok, the copy functions always allocate a new structure,
86 which doesn't work here. instead, simply copy the
87 contents of the new structure into the array cell,
88 then free it. this makes for more maintainable
89 code than simply reimplementing the copy functions
91 tmp = IDP_CopyProperty(GETPROP(narray, i));
92 memcpy(GETPROP(narray, i), tmp, sizeof(IDProperty));
99 void IDP_FreeIDPArray(IDProperty *prop)
103 for (i=0; i<prop->len; i++)
104 IDP_FreeProperty(GETPROP(prop, i));
106 if(prop->data.pointer)
107 MEM_freeN(prop->data.pointer);
110 /*shallow copies item*/
111 void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item)
113 IDProperty *old = GETPROP(prop, index);
114 if (index >= prop->len || index < 0) return;
115 if (item != old) IDP_FreeProperty(old);
117 memcpy(GETPROP(prop, index), item, sizeof(IDProperty));
120 IDProperty *IDP_GetIndexArray(IDProperty *prop, int index)
122 return GETPROP(prop, index);
125 IDProperty *IDP_AppendArray(IDProperty *prop, IDProperty *item)
127 IDP_ResizeIDPArray(prop, prop->len+1);
128 IDP_SetIndexArray(prop, prop->len-1, item);
132 void IDP_ResizeIDPArray(IDProperty *prop, int newlen)
137 /*first check if the array buffer size has room*/
138 /*if newlen is 200 chars less then totallen, reallocate anyway*/
139 if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
142 for(i=newlen; i<prop->len; i++)
143 IDP_FreeProperty(GETPROP(prop, i));
149 /* - Note: This code comes from python, here's the corrusponding comment. - */
150 /* This over-allocates proportional to the list size, making room
151 * for additional growth. The over-allocation is mild, but is
152 * enough to give linear-time amortized behavior over a long
153 * sequence of appends() in the presence of a poorly-performing
155 * The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
157 newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
159 newarr = MEM_callocN(sizeof(IDProperty)*newsize, "idproperty array resized");
160 if (newlen >= prop->len) {
161 /* newlen is bigger*/
162 memcpy(newarr, prop->data.pointer, prop->len*sizeof(IDProperty));
166 /* newlen is smaller*/
167 for (i=newlen; i<prop->len; i++) {
168 IDP_FreeProperty(GETPROP(prop, i));
170 memcpy(newarr, prop->data.pointer, newlen*sizeof(IDProperty));
173 if(prop->data.pointer)
174 MEM_freeN(prop->data.pointer);
175 prop->data.pointer = newarr;
177 prop->totallen = newsize;
180 /* ----------- Numerical Array Type ----------- */
181 static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr)
183 if(prop->subtype != IDP_GROUP)
186 if(newlen >= prop->len) {
188 IDProperty **array= newarr;
189 IDPropertyTemplate val;
192 for(a=prop->len; a<newlen; a++) {
193 val.i = 0; /* silence MSVC warning about uninitialized var when debugging */
194 array[a]= IDP_New(IDP_GROUP, val, "IDP_ResizeArray group");
199 IDProperty **array= prop->data.pointer;
202 for(a=newlen; a<prop->len; a++) {
203 IDP_FreeProperty(array[a]);
209 /*this function works for strings too!*/
210 void IDP_ResizeArray(IDProperty *prop, int newlen)
215 /*first check if the array buffer size has room*/
216 /*if newlen is 200 chars less then totallen, reallocate anyway*/
217 if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
218 idp_resize_group_array(prop, newlen, prop->data.pointer);
223 /* - Note: This code comes from python, here's the corrusponding comment. - */
224 /* This over-allocates proportional to the list size, making room
225 * for additional growth. The over-allocation is mild, but is
226 * enough to give linear-time amortized behavior over a long
227 * sequence of appends() in the presence of a poorly-performing
229 * The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
231 newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
233 newarr = MEM_callocN(idp_size_table[(int)prop->subtype]*newsize, "idproperty array resized");
234 if (newlen >= prop->len) {
235 /* newlen is bigger*/
236 memcpy(newarr, prop->data.pointer, prop->len*idp_size_table[(int)prop->subtype]);
237 idp_resize_group_array(prop, newlen, newarr);
240 /* newlen is smaller*/
241 idp_resize_group_array(prop, newlen, newarr);
242 memcpy(newarr, prop->data.pointer, newlen*prop->len*idp_size_table[(int)prop->subtype]);
245 MEM_freeN(prop->data.pointer);
246 prop->data.pointer = newarr;
248 prop->totallen = newsize;
251 void IDP_FreeArray(IDProperty *prop)
253 if (prop->data.pointer) {
254 idp_resize_group_array(prop, 0, NULL);
255 MEM_freeN(prop->data.pointer);
260 static IDProperty *idp_generic_copy(IDProperty *prop)
262 IDProperty *newp = MEM_callocN(sizeof(IDProperty), "IDProperty array dup");
264 BLI_strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
265 newp->type = prop->type;
266 newp->flag = prop->flag;
267 newp->data.val = prop->data.val;
268 newp->data.val2 = prop->data.val2;
273 IDProperty *IDP_CopyArray(IDProperty *prop)
275 IDProperty *newp = idp_generic_copy(prop);
277 if (prop->data.pointer) {
278 newp->data.pointer = MEM_dupallocN(prop->data.pointer);
280 if(prop->type == IDP_GROUP) {
281 IDProperty **array= newp->data.pointer;
284 for(a=0; a<prop->len; a++)
285 array[a]= IDP_CopyProperty(array[a]);
288 newp->len = prop->len;
289 newp->subtype = prop->subtype;
290 newp->totallen = prop->totallen;
295 /*taken from readfile.c*/
296 #define SWITCH_LONGINT(a) { \
299 s_i=p_i[0]; p_i[0]=p_i[7]; p_i[7]=s_i; \
300 s_i=p_i[1]; p_i[1]=p_i[6]; p_i[6]=s_i; \
301 s_i=p_i[2]; p_i[2]=p_i[5]; p_i[5]=s_i; \
302 s_i=p_i[3]; p_i[3]=p_i[4]; p_i[4]=s_i; }
306 /* ---------- String Type ------------ */
307 IDProperty *IDP_CopyString(IDProperty *prop)
309 IDProperty *newp = idp_generic_copy(prop);
311 if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer);
312 newp->len = prop->len;
313 newp->subtype = prop->subtype;
314 newp->totallen = prop->totallen;
320 void IDP_AssignString(IDProperty *prop, char *st)
326 IDP_ResizeArray(prop, stlen+1); /*make room for null byte :) */
327 strcpy(prop->data.pointer, st);
330 void IDP_ConcatStringC(IDProperty *prop, char *st)
334 newlen = prop->len + strlen(st);
335 /*we have to remember that prop->len includes the null byte for strings.
336 so there's no need to add +1 to the resize function.*/
337 IDP_ResizeArray(prop, newlen);
338 strcat(prop->data.pointer, st);
341 void IDP_ConcatString(IDProperty *str1, IDProperty *append)
345 /*since ->len for strings includes the NULL byte, we have to subtract one or
346 we'll get an extra null byte after each concatination operation.*/
347 newlen = str1->len + append->len - 1;
348 IDP_ResizeArray(str1, newlen);
349 strcat(str1->data.pointer, append->data.pointer);
352 void IDP_FreeString(IDProperty *prop)
354 if(prop->data.pointer)
355 MEM_freeN(prop->data.pointer);
359 /*-------- ID Type, not in use yet -------*/
361 void IDP_LinkID(IDProperty *prop, ID *id)
363 if (prop->data.pointer) ((ID*)prop->data.pointer)->us--;
364 prop->data.pointer = id;
368 void IDP_UnlinkID(IDProperty *prop)
370 ((ID*)prop->data.pointer)->us--;
373 /*-------- Group Functions -------*/
375 /*checks if a property with the same name as prop exists, and if so replaces it.*/
376 IDProperty *IDP_CopyGroup(IDProperty *prop)
378 IDProperty *newp = idp_generic_copy(prop), *link;
379 newp->len = prop->len;
381 for (link=prop->data.group.first; link; link=link->next) {
382 BLI_addtail(&newp->data.group, IDP_CopyProperty(link));
389 replaces a property with the same name in a group, or adds
390 it if the propery doesn't exist.
392 void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
395 for (loop=group->data.group.first; loop; loop=loop->next) {
396 if (BSTR_EQ(loop->name, prop->name)) {
397 if (loop->next) BLI_insertlinkbefore(&group->data.group, loop->next, prop);
398 else BLI_addtail(&group->data.group, prop);
400 BLI_remlink(&group->data.group, loop);
401 IDP_FreeProperty(loop);
408 BLI_addtail(&group->data.group, prop);
411 /*returns 0 if an id property with the same name exists and it failed,
412 or 1 if it succeeded in adding to the group.*/
413 int IDP_AddToGroup(IDProperty *group, IDProperty *prop)
416 for (loop=group->data.group.first; loop; loop=loop->next) {
417 if (BSTR_EQ(loop->name, prop->name)) return 0;
421 BLI_addtail(&group->data.group, prop);
426 int IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
429 for (loop=group->data.group.first; loop; loop=loop->next) {
430 if (BSTR_EQ(loop->name, pnew->name)) return 0;
435 BLI_insertlink(&group->data.group, previous, pnew);
439 void IDP_RemFromGroup(IDProperty *group, IDProperty *prop)
442 BLI_remlink(&group->data.group, prop);
445 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, char *name)
448 for (loop=prop->data.group.first; loop; loop=loop->next) {
449 if (strcmp(loop->name, name)==0) return loop;
454 typedef struct IDPIter {
459 void *IDP_GetGroupIterator(IDProperty *prop)
461 IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter");
462 iter->next = prop->data.group.first;
467 IDProperty *IDP_GroupIterNext(void *vself)
469 IDPIter *self = (IDPIter*) vself;
470 Link *next = (Link*) self->next;
471 if (self->next == NULL) {
476 self->next = next->next;
480 void IDP_FreeIterBeforeEnd(void *vself)
485 /*Ok, the way things work, Groups free the ID Property structs of their children.
486 This is because all ID Property freeing functions free only direct data (not the ID Property
487 struct itself), but for Groups the child properties *are* considered
489 static void IDP_FreeGroup(IDProperty *prop)
491 IDProperty *loop, *next;
492 for (loop=prop->data.group.first; loop; loop=next)
495 BLI_remlink(&prop->data.group, loop);
496 IDP_FreeProperty(loop);
502 /*-------- Main Functions --------*/
503 IDProperty *IDP_CopyProperty(IDProperty *prop)
505 switch (prop->type) {
506 case IDP_GROUP: return IDP_CopyGroup(prop);
507 case IDP_STRING: return IDP_CopyString(prop);
508 case IDP_ARRAY: return IDP_CopyArray(prop);
509 case IDP_IDPARRAY: return IDP_CopyIDPArray(prop);
510 default: return idp_generic_copy(prop);
514 IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
516 if (id->properties) return id->properties;
518 if (create_if_needed) {
519 id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
520 id->properties->type = IDP_GROUP;
521 /* dont overwite the data's name and type
522 * some functions might need this if they
523 * dont have a real ID, should be named elsewhere - Campbell */
524 /* strcpy(id->name, "top_level_group");*/
526 return id->properties;
530 int IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2)
532 if(prop1 == NULL && prop2 == NULL)
534 else if(prop1 == NULL || prop2 == NULL)
536 else if(prop1->type != prop2->type)
539 if(prop1->type == IDP_INT)
540 return (IDP_Int(prop1) == IDP_Int(prop2));
541 else if(prop1->type == IDP_FLOAT)
542 return (IDP_Float(prop1) == IDP_Float(prop2));
543 else if(prop1->type == IDP_DOUBLE)
544 return (IDP_Double(prop1) == IDP_Double(prop2));
545 else if(prop1->type == IDP_STRING)
546 return BSTR_EQ(IDP_String(prop1), IDP_String(prop2));
547 else if(prop1->type == IDP_ARRAY) {
548 if(prop1->len == prop2->len && prop1->subtype == prop2->subtype)
549 return memcmp(IDP_Array(prop1), IDP_Array(prop2), idp_size_table[(int)prop1->subtype]*prop1->len);
553 else if(prop1->type == IDP_GROUP) {
554 IDProperty *link1, *link2;
556 if(BLI_countlist(&prop1->data.group) != BLI_countlist(&prop2->data.group))
559 for(link1=prop1->data.group.first; link1; link1=link1->next) {
560 link2= IDP_GetPropertyFromGroup(prop2, link1->name);
562 if(!IDP_EqualsProperties(link1, link2))
568 else if(prop1->type == IDP_IDPARRAY) {
569 IDProperty *array1= IDP_IDPArray(prop1);
570 IDProperty *array2= IDP_IDPArray(prop2);
573 if(prop1->len != prop2->len)
576 for(i=0; i<prop1->len; i++)
577 if(!IDP_EqualsProperties(&array1[i], &array2[i]))
584 IDProperty *IDP_New(int type, IDPropertyTemplate val, const char *name)
586 IDProperty *prop=NULL;
590 prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
591 prop->data.val = val.i;
594 prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
595 *(float*)&prop->data.val = val.f;
598 prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
599 *(double*)&prop->data.val = val.d;
603 /*for now, we only support float and int and double arrays*/
604 if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT || val.array.type == IDP_DOUBLE || val.array.type == IDP_GROUP) {
605 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
606 prop->subtype = val.array.type;
608 prop->data.pointer = MEM_callocN(idp_size_table[val.array.type]*val.array.len, "id property array");
609 prop->len = prop->totallen = val.array.len;
620 prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
622 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
623 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
624 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
626 stlen = strlen(st) + 1;
627 prop->data.pointer = MEM_callocN(stlen, "id property string 2");
628 prop->len = prop->totallen = stlen;
629 strcpy(prop->data.pointer, st);
635 prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
636 /* heh I think all needed values are set properly by calloc anyway :) */
641 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
647 BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
649 /*security null byte*/
650 prop->name[MAX_IDPROP_NAME-1] = 0;
655 /*NOTE: this will free all child properties including list arrays and groups!
656 Also, note that this does NOT unlink anything! Plus it doesn't free
657 the actual IDProperty struct either.*/
658 void IDP_FreeProperty(IDProperty *prop)
660 switch (prop->type) {
665 IDP_FreeString(prop);
671 IDP_FreeIDPArray(prop);
676 /*Unlinks any IDProperty<->ID linkage that might be going on.
677 note: currently unused.*/
678 void IDP_UnlinkProperty(IDProperty *prop)
680 switch (prop->type) {