This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/blenkernel/intern/idprop.c

794 lines
20 KiB
C
Raw Normal View History

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
2010-02-12 13:34:04 +00:00
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* Contributor(s): Joseph Eagar
*
* ***** END GPL LICENSE BLOCK *****
*/
2011-02-27 20:40:57 +00:00
/** \file blender/blenkernel/intern/idprop.c
* \ingroup bke
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include "BKE_idprop.h"
#include "BKE_library.h"
#include "BLI_blenlib.h"
#include "MEM_guardedalloc.h"
/* IDPropertyTemplate is a union in DNA_ID.h */
/*local size table.*/
static char idp_size_table[] = {
1, /*strings*/
sizeof(int),
sizeof(float),
sizeof(float)*3, /*Vector type, deprecated*/
sizeof(float)*16, /*Matrix type, deprecated*/
0, /*arrays don't have a fixed size*/
sizeof(ListBase), /*Group type*/
sizeof(void*),
sizeof(double)
};
/* ------------Property Array Type ----------- */
#define GETPROP(prop, i) (((IDProperty*)(prop)->data.pointer)+(i))
/* --------- property array type -------------*/
/* note: as a start to move away from the stupid IDP_New function, this type
* has it's own allocation function.*/
IDProperty *IDP_NewIDPArray(const char *name)
{
IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty prop array");
prop->type = IDP_IDPARRAY;
prop->len = 0;
BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
return prop;
}
IDProperty *IDP_CopyIDPArray(IDProperty *array)
{
/* dont use MEM_dupallocN because this may be part of an array */
IDProperty *narray = MEM_mallocN(sizeof(IDProperty), "IDP_CopyIDPArray"), *tmp;
int i;
*narray= *array;
narray->data.pointer = MEM_dupallocN(array->data.pointer);
for (i=0; i<narray->len; i++) {
/* ok, the copy functions always allocate a new structure,
* which doesn't work here. instead, simply copy the
* contents of the new structure into the array cell,
* then free it. this makes for more maintainable
* code than simply reimplementing the copy functions
* in this loop.*/
tmp = IDP_CopyProperty(GETPROP(narray, i));
memcpy(GETPROP(narray, i), tmp, sizeof(IDProperty));
MEM_freeN(tmp);
}
return narray;
}
void IDP_FreeIDPArray(IDProperty *prop)
{
int i;
for (i=0; i<prop->len; i++)
IDP_FreeProperty(GETPROP(prop, i));
if(prop->data.pointer)
MEM_freeN(prop->data.pointer);
}
/*shallow copies item*/
void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item)
{
IDProperty *old = GETPROP(prop, index);
if (index >= prop->len || index < 0) return;
if (item != old) IDP_FreeProperty(old);
memcpy(GETPROP(prop, index), item, sizeof(IDProperty));
}
IDProperty *IDP_GetIndexArray(IDProperty *prop, int index)
{
return GETPROP(prop, index);
}
IDProperty *IDP_AppendArray(IDProperty *prop, IDProperty *item)
{
IDP_ResizeIDPArray(prop, prop->len+1);
IDP_SetIndexArray(prop, prop->len-1, item);
return item;
}
void IDP_ResizeIDPArray(IDProperty *prop, int newlen)
{
void *newarr;
int newsize=newlen;
/*first check if the array buffer size has room*/
/*if newlen is 200 chars less then totallen, reallocate anyway*/
if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
int i;
for(i=newlen; i<prop->len; i++)
IDP_FreeProperty(GETPROP(prop, i));
prop->len = newlen;
return;
}
/* - Note: This code comes from python, here's the corresponding comment. - */
/* This over-allocates proportional to the list size, making room
* for additional growth. The over-allocation is mild, but is
* enough to give linear-time amortized behavior over a long
* sequence of appends() in the presence of a poorly-performing
* system realloc().
* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
*/
newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
newarr = MEM_callocN(sizeof(IDProperty)*newsize, "idproperty array resized");
if (newlen >= prop->len) {
/* newlen is bigger*/
memcpy(newarr, prop->data.pointer, prop->len*sizeof(IDProperty));
}
else {
int i;
/* newlen is smaller*/
for (i=newlen; i<prop->len; i++) {
IDP_FreeProperty(GETPROP(prop, i));
}
memcpy(newarr, prop->data.pointer, newlen*sizeof(IDProperty));
}
if(prop->data.pointer)
MEM_freeN(prop->data.pointer);
prop->data.pointer = newarr;
prop->len = newlen;
prop->totallen = newsize;
}
/* ----------- Numerical Array Type ----------- */
static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr)
{
if(prop->subtype != IDP_GROUP)
return;
if(newlen >= prop->len) {
/* bigger */
IDProperty **array= newarr;
IDPropertyTemplate val;
int a;
for(a=prop->len; a<newlen; a++) {
val.i = 0; /* silence MSVC warning about uninitialized var when debugging */
array[a]= IDP_New(IDP_GROUP, &val, "IDP_ResizeArray group");
}
}
else {
/* smaller */
IDProperty **array= prop->data.pointer;
int a;
for(a=newlen; a<prop->len; a++) {
IDP_FreeProperty(array[a]);
MEM_freeN(array[a]);
}
}
}
/*this function works for strings too!*/
void IDP_ResizeArray(IDProperty *prop, int newlen)
{
void *newarr;
int newsize=newlen;
/*first check if the array buffer size has room*/
/*if newlen is 200 chars less then totallen, reallocate anyway*/
if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
idp_resize_group_array(prop, newlen, prop->data.pointer);
prop->len = newlen;
return;
}
/* - Note: This code comes from python, here's the corresponding comment. - */
/* This over-allocates proportional to the list size, making room
* for additional growth. The over-allocation is mild, but is
* enough to give linear-time amortized behavior over a long
* sequence of appends() in the presence of a poorly-performing
* system realloc().
* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
*/
newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
newarr = MEM_callocN(idp_size_table[(int)prop->subtype]*newsize, "idproperty array resized");
if (newlen >= prop->len) {
/* newlen is bigger*/
memcpy(newarr, prop->data.pointer, prop->len*idp_size_table[(int)prop->subtype]);
idp_resize_group_array(prop, newlen, newarr);
}
else {
/* newlen is smaller*/
idp_resize_group_array(prop, newlen, newarr);
memcpy(newarr, prop->data.pointer, newlen*idp_size_table[(int)prop->subtype]);
}
MEM_freeN(prop->data.pointer);
prop->data.pointer = newarr;
prop->len = newlen;
prop->totallen = newsize;
}
void IDP_FreeArray(IDProperty *prop)
{
if (prop->data.pointer) {
idp_resize_group_array(prop, 0, NULL);
MEM_freeN(prop->data.pointer);
}
}
static IDProperty *idp_generic_copy(IDProperty *prop)
{
IDProperty *newp = MEM_callocN(sizeof(IDProperty), "IDProperty array dup");
BLI_strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
newp->type = prop->type;
newp->flag = prop->flag;
newp->data.val = prop->data.val;
newp->data.val2 = prop->data.val2;
return newp;
}
static IDProperty *IDP_CopyArray(IDProperty *prop)
{
IDProperty *newp = idp_generic_copy(prop);
if (prop->data.pointer) {
newp->data.pointer = MEM_dupallocN(prop->data.pointer);
if(prop->type == IDP_GROUP) {
IDProperty **array= newp->data.pointer;
int a;
for(a=0; a<prop->len; a++)
array[a]= IDP_CopyProperty(array[a]);
}
}
newp->len = prop->len;
newp->subtype = prop->subtype;
newp->totallen = prop->totallen;
return newp;
}
/*taken from readfile.c*/
#define SWITCH_LONGINT(a) { \
char s_i, *p_i; \
p_i= (char *)&(a); \
s_i=p_i[0]; p_i[0]=p_i[7]; p_i[7]=s_i; \
s_i=p_i[1]; p_i[1]=p_i[6]; p_i[6]=s_i; \
s_i=p_i[2]; p_i[2]=p_i[5]; p_i[5]=s_i; \
s_i=p_i[3]; p_i[3]=p_i[4]; p_i[4]=s_i; }
/* ---------- String Type ------------ */
IDProperty *IDP_NewString(const char *st, const char *name, int maxlen)
{
IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
if (st == NULL) {
prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
}
else {
int stlen = strlen(st);
if(maxlen > 0 && maxlen < stlen)
stlen = maxlen;
stlen++; /* null terminator '\0' */
prop->data.pointer = MEM_callocN(stlen, "id property string 2");
prop->len = prop->totallen = stlen;
BLI_strncpy(prop->data.pointer, st, stlen);
}
prop->type = IDP_STRING;
BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
return prop;
}
static IDProperty *IDP_CopyString(IDProperty *prop)
{
IDProperty *newp = idp_generic_copy(prop);
if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer);
newp->len = prop->len;
newp->subtype = prop->subtype;
newp->totallen = prop->totallen;
return newp;
}
void IDP_AssignString(IDProperty *prop, const char *st, int maxlen)
{
int stlen = strlen(st);
if(maxlen > 0 && maxlen < stlen)
stlen= maxlen;
if (prop->subtype == IDP_STRING_SUB_BYTE) {
IDP_ResizeArray(prop, stlen);
memcpy(prop->data.pointer, st, stlen);
}
else {
stlen++; /* make room for null byte */
IDP_ResizeArray(prop, stlen);
BLI_strncpy(prop->data.pointer, st, stlen);
}
}
void IDP_ConcatStringC(IDProperty *prop, const char *st)
{
int newlen;
newlen = prop->len + strlen(st);
/* we have to remember that prop->len includes the null byte for strings.
* so there's no need to add +1 to the resize function.*/
IDP_ResizeArray(prop, newlen);
strcat(prop->data.pointer, st);
}
void IDP_ConcatString(IDProperty *str1, IDProperty *append)
{
int newlen;
/* since ->len for strings includes the NULL byte, we have to subtract one or
* we'll get an extra null byte after each concatenation operation.*/
newlen = str1->len + append->len - 1;
IDP_ResizeArray(str1, newlen);
strcat(str1->data.pointer, append->data.pointer);
}
void IDP_FreeString(IDProperty *prop)
{
if(prop->data.pointer)
MEM_freeN(prop->data.pointer);
}
/*-------- ID Type, not in use yet -------*/
void IDP_LinkID(IDProperty *prop, ID *id)
{
if (prop->data.pointer) ((ID*)prop->data.pointer)->us--;
prop->data.pointer = id;
id_us_plus(id);
}
void IDP_UnlinkID(IDProperty *prop)
{
((ID*)prop->data.pointer)->us--;
}
/*-------- Group Functions -------*/
/*checks if a property with the same name as prop exists, and if so replaces it.*/
static IDProperty *IDP_CopyGroup(IDProperty *prop)
{
IDProperty *newp = idp_generic_copy(prop), *link;
newp->len = prop->len;
for (link=prop->data.group.first; link; link=link->next) {
BLI_addtail(&newp->data.group, IDP_CopyProperty(link));
}
return newp;
}
/* use for syncing proxies.
* When values name and types match, copy the values, else ignore */
void IDP_SyncGroupValues(IDProperty *dest, IDProperty *src)
{
IDProperty *other, *prop;
for (prop=src->data.group.first; prop; prop=prop->next) {
other= BLI_findstring(&dest->data.group, prop->name, offsetof(IDProperty, name));
if (other && prop->type==other->type) {
switch (prop->type) {
case IDP_INT:
case IDP_FLOAT:
case IDP_DOUBLE:
other->data= prop->data;
break;
case IDP_GROUP:
IDP_SyncGroupValues(other, prop);
break;
default:
{
IDProperty *tmp= other;
IDProperty *copy= IDP_CopyProperty(prop);
BLI_insertlinkafter(&dest->data.group, other, copy);
BLI_remlink(&dest->data.group, tmp);
IDP_FreeProperty(tmp);
MEM_freeN(tmp);
}
}
}
}
}
/*
* replaces all properties with the same name in a destination group from a source group.
*/
void IDP_ReplaceGroupInGroup(IDProperty *dest, IDProperty *src)
{
IDProperty *loop, *prop;
for (prop=src->data.group.first; prop; prop=prop->next) {
for (loop=dest->data.group.first; loop; loop=loop->next) {
if (strcmp(loop->name, prop->name) == 0) {
IDProperty *copy = IDP_CopyProperty(prop);
BLI_insertlink(&dest->data.group, loop, copy);
BLI_remlink(&dest->data.group, loop);
IDP_FreeProperty(loop);
MEM_freeN(loop);
break;
}
}
/* only add at end if not added yet */
if (loop == NULL) {
IDProperty *copy = IDP_CopyProperty(prop);
dest->len++;
BLI_addtail(&dest->data.group, copy);
}
}
}
/*
* replaces a property with the same name in a group, or adds
* it if the propery doesn't exist.
*/
void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
{
IDProperty *loop;
2012-02-27 10:35:39 +00:00
if ((loop= IDP_GetPropertyFromGroup(group, prop->name))) {
BLI_insertlink(&group->data.group, loop, prop);
BLI_remlink(&group->data.group, loop);
IDP_FreeProperty(loop);
MEM_freeN(loop);
}
else {
group->len++;
BLI_addtail(&group->data.group, prop);
}
}
/* returns 0 if an id property with the same name exists and it failed,
* or 1 if it succeeded in adding to the group.*/
int IDP_AddToGroup(IDProperty *group, IDProperty *prop)
{
2012-02-27 10:35:39 +00:00
if (IDP_GetPropertyFromGroup(group, prop->name) == NULL) {
group->len++;
BLI_addtail(&group->data.group, prop);
return 1;
}
return 0;
}
int IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
{
2012-02-27 10:35:39 +00:00
if (IDP_GetPropertyFromGroup(group, pnew->name) == NULL) {
group->len++;
BLI_insertlink(&group->data.group, previous, pnew);
return 1;
}
return 0;
}
void IDP_RemFromGroup(IDProperty *group, IDProperty *prop)
{
group->len--;
BLI_remlink(&group->data.group, prop);
}
IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, const char *name)
{
return (IDProperty *)BLI_findstring(&prop->data.group, name, offsetof(IDProperty, name));
}
IDProperty *IDP_GetPropertyTypeFromGroup(IDProperty *prop, const char *name, const char type)
{
IDProperty *idprop= IDP_GetPropertyFromGroup(prop, name);
return (idprop && idprop->type == type) ? idprop : NULL;
}
typedef struct IDPIter {
void *next;
IDProperty *parent;
} IDPIter;
void *IDP_GetGroupIterator(IDProperty *prop)
{
IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter");
iter->next = prop->data.group.first;
iter->parent = prop;
return (void*) iter;
}
IDProperty *IDP_GroupIterNext(void *vself)
{
IDPIter *self = (IDPIter*) vself;
Link *next = (Link*) self->next;
if (self->next == NULL) {
MEM_freeN(self);
return NULL;
}
self->next = next->next;
return (void*) next;
}
void IDP_FreeIterBeforeEnd(void *vself)
{
MEM_freeN(vself);
}
/* Ok, the way things work, Groups free the ID Property structs of their children.
* This is because all ID Property freeing functions free only direct data (not the ID Property
* struct itself), but for Groups the child properties *are* considered
* direct data. */
static void IDP_FreeGroup(IDProperty *prop)
{
IDProperty *loop;
for (loop=prop->data.group.first; loop; loop=loop->next)
{
IDP_FreeProperty(loop);
}
BLI_freelistN(&prop->data.group);
}
/*-------- Main Functions --------*/
IDProperty *IDP_CopyProperty(IDProperty *prop)
{
switch (prop->type) {
case IDP_GROUP: return IDP_CopyGroup(prop);
case IDP_STRING: return IDP_CopyString(prop);
case IDP_ARRAY: return IDP_CopyArray(prop);
case IDP_IDPARRAY: return IDP_CopyIDPArray(prop);
default: return idp_generic_copy(prop);
}
}
IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
{
if (id->properties) return id->properties;
else {
if (create_if_needed) {
id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
id->properties->type = IDP_GROUP;
2012-03-01 12:20:18 +00:00
/* dont overwrite the data's name and type
* some functions might need this if they
* dont have a real ID, should be named elsewhere - Campbell */
/* strcpy(id->name, "top_level_group");*/
}
return id->properties;
}
}
int IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2)
{
if(prop1 == NULL && prop2 == NULL)
return 1;
else if(prop1 == NULL || prop2 == NULL)
return 0;
else if(prop1->type != prop2->type)
return 0;
if(prop1->type == IDP_INT)
return (IDP_Int(prop1) == IDP_Int(prop2));
else if(prop1->type == IDP_FLOAT)
return (IDP_Float(prop1) == IDP_Float(prop2));
else if(prop1->type == IDP_DOUBLE)
return (IDP_Double(prop1) == IDP_Double(prop2));
else if(prop1->type == IDP_STRING)
return ((prop1->len == prop2->len) && strncmp(IDP_String(prop1), IDP_String(prop2), prop1->len) == 0);
else if(prop1->type == IDP_ARRAY) {
if(prop1->len == prop2->len && prop1->subtype == prop2->subtype)
return memcmp(IDP_Array(prop1), IDP_Array(prop2), idp_size_table[(int)prop1->subtype]*prop1->len);
else
return 0;
}
else if(prop1->type == IDP_GROUP) {
IDProperty *link1, *link2;
if(BLI_countlist(&prop1->data.group) != BLI_countlist(&prop2->data.group))
return 0;
for(link1=prop1->data.group.first; link1; link1=link1->next) {
link2= IDP_GetPropertyFromGroup(prop2, link1->name);
if(!IDP_EqualsProperties(link1, link2))
return 0;
}
return 1;
}
else if(prop1->type == IDP_IDPARRAY) {
IDProperty *array1= IDP_IDPArray(prop1);
IDProperty *array2= IDP_IDPArray(prop2);
int i;
if(prop1->len != prop2->len)
return 0;
for(i=0; i<prop1->len; i++)
if(!IDP_EqualsProperties(&array1[i], &array2[i]))
return 0;
}
return 1;
}
/* 'val' is never NULL, dont check */
IDProperty *IDP_New(const int type, const IDPropertyTemplate *val, const char *name)
{
IDProperty *prop=NULL;
switch (type) {
case IDP_INT:
prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
prop->data.val = val->i;
break;
case IDP_FLOAT:
prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
*(float*)&prop->data.val = val->f;
break;
case IDP_DOUBLE:
prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
*(double*)&prop->data.val = val->d;
break;
case IDP_ARRAY:
{
/*for now, we only support float and int and double arrays*/
if ( (val->array.type == IDP_FLOAT) ||
(val->array.type == IDP_INT) ||
(val->array.type == IDP_DOUBLE) ||
(val->array.type == IDP_GROUP) )
{
prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
prop->subtype = val->array.type;
if (val->array.len)
prop->data.pointer = MEM_callocN(idp_size_table[val->array.type]*val->array.len, "id property array");
prop->len = prop->totallen = val->array.len;
break;
} else {
return NULL;
}
}
case IDP_STRING:
{
const char *st = val->string.str;
prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
if (val->string.subtype == IDP_STRING_SUB_BYTE) {
/* note, intentionally not null terminated */
if (st == NULL) {
prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
prop->len = 0;
}
else {
prop->data.pointer = MEM_mallocN(val->string.len, "id property string 2");
prop->len = prop->totallen = val->string.len;
memcpy(prop->data.pointer, st, val->string.len);
}
prop->subtype= IDP_STRING_SUB_BYTE;
}
else {
if (st == NULL) {
prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
}
else {
int stlen = strlen(st) + 1;
prop->data.pointer = MEM_mallocN(stlen, "id property string 3");
prop->len = prop->totallen = stlen;
memcpy(prop->data.pointer, st, stlen);
}
prop->subtype= IDP_STRING_SUB_UTF8;
}
break;
}
case IDP_GROUP:
{
prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
/* heh I think all needed values are set properly by calloc anyway :) */
break;
}
default:
{
prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
break;
}
}
prop->type = type;
BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
return prop;
}
/* NOTE: this will free all child properties including list arrays and groups!
* Also, note that this does NOT unlink anything! Plus it doesn't free
* the actual IDProperty struct either.*/
void IDP_FreeProperty(IDProperty *prop)
{
switch (prop->type) {
case IDP_ARRAY:
IDP_FreeArray(prop);
break;
case IDP_STRING:
IDP_FreeString(prop);
break;
case IDP_GROUP:
IDP_FreeGroup(prop);
break;
case IDP_IDPARRAY:
IDP_FreeIDPArray(prop);
break;
}
}
/* Unlinks any IDProperty<->ID linkage that might be going on.
* note: currently unused.*/
void IDP_UnlinkProperty(IDProperty *prop)
{
switch (prop->type) {
case IDP_ID:
IDP_UnlinkID(prop);
}
}