== livido.c ==

This is a reference implementation of the livido core library. It will eventually be made into a library which can be linked in at compile time.

==== Code ====
{{{
/* LiViDO is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   LiViDO 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this source code; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


   LiViDO is developed by:

   Niels Elburg - http://veejay.sf.net

   Gabriel "Salsaman" Finch - http://lives.sourceforge.net

   Denis "Jaromil" Rojo - http://freej.dyne.org

   Tom Schouten - http://zwizwa.fartit.com

   Andraz Tori - http://cvs.cinelerra.org

   reviewed with suggestions and contributions from:

   Silvano "Kysucix" Galliani - http://freej.dyne.org

   Kentaro Fukuchi - http://megaui.net/fukuchi

   Jun Iio - http://www.malib.net

   Carlo Prelz - http://www2.fluido.as:8080/

*/

/* (C) Gabriel "Salsaman" Finch, Niels Elburg, Dennis "Jaromil" Rojo, 2005 */

#include <string.h>
#include <sys/types.h>

static inline int livido_atom_type_is_ptr (int atom_type) {
 return (atom_type==LIVIDO_ATOM_TYPE_VOIDPTR||atom_type==LIVIDO_ATOM_TYPE_PORTPTR||\
 atom_type==LIVIDO_ATOM_TYPE_FILTERPTR)?1:0;
}

static inline size_t livido_atom_type_get_size (int atom_type, void *value) {
  return livido_atom_type_is_ptr (atom_type)?0:\
  (atom_type==LIVIDO_ATOM_TYPE_BOOLEAN||atom_type==LIVIDO_ATOM_TYPE_INT)?sizeof(int):\
  (atom_type==LIVIDO_ATOM_TYPE_FLOAT)?sizeof(float):\
  (atom_type==LIVIDO_ATOM_TYPE_LONG)?sizeof(long):\
  (atom_type==LIVIDO_ATOM_TYPE_STRING)?strlen((const char *)value):0;
}

static inline void livido_atom_free(livido_atom_t *atom, int atom_type) {
  if (!livido_atom_type_is_ptr(atom_type)) livido_free_f (atom->value);
  livido_free_f (atom);
}

static inline livido_atom_t *livido_atom_new(void *value, int atom_type, size_t size) {
  livido_atom_t *atom=(livido_atom_t *)livido_malloc_f(sizeof(livido_atom_t));
  if (atom==NULL) return NULL;
  if (livido_atom_type_is_ptr(atom_type)) {
    atom->size=size;
    livido_memcpy_f (&atom->value,value,sizeof(void *));
  }
  else {
    if (atom_type==LIVIDO_ATOM_TYPE_STRING) {
      char ** valuecharptrptr=(char **)value;
      atom->size=livido_atom_type_get_size(atom_type,*valuecharptrptr);
    }
    else atom->size=livido_atom_type_get_size(atom_type,value);
    atom->value=livido_malloc_f(atom->size);
    if (atom->value==NULL) {
      livido_free_f (atom);
      return NULL;
    }
    if (atom_type==LIVIDO_ATOM_TYPE_STRING) {
      char ** valuecharptrptr=(char **)value;
      livido_memcpy_f (atom->value,*valuecharptrptr,atom->size);
    }
    else livido_memcpy_f (atom->value,value,atom->size);
  }
  return atom;
}

static inline void livido_storage_free(livido_storage_t *store) {
  register int i;
  if (store->num_elements==1) livido_atom_free(store->atom,store->atom_type);
  else for (i=0;i<store->num_elements;i++) livido_atom_free(store->array[i],store->atom_type);
}

static inline livido_storage_t *livido_storage_new(int atom_type, int num_elems, void *value, size_t *sizes) {
  livido_storage_t *store=(livido_storage_t *)livido_malloc_f(sizeof(livido_storage_t));
  if (store==NULL) return NULL;
  store->num_elements=num_elems;
  store->atom_type=atom_type;
  if (num_elems==0) store->atom=NULL;
  else {
    if (num_elems==1) {
      if ((store->atom=livido_atom_new (value, atom_type, livido_atom_type_is_ptr(atom_type)?sizes[0]:0))==NULL) {
        livido_free_f (store);
        return NULL;
      }
    }
    else {
      register int i;
      void **valuevoidptrptr=(void **)value;
      if ((store->array=(livido_atom_t **)livido_malloc_f(num_elems*sizeof(livido_atom_t *)))==NULL) {
        livido_free_f (store);
        return NULL;
      }
      for (i=0;i<num_elems;i++) {
        if (livido_atom_type_is_ptr(atom_type)) store->array[i]=livido_atom_new(&valuevoidptrptr[i],atom_type,sizes[i]);
        else {
          if (atom_type==LIVIDO_ATOM_TYPE_STRING) store->array[i]=livido_atom_new(valuevoidptrptr[i],atom_type,0);
          else store->array[i]=livido_atom_new(&valuevoidptrptr[i],atom_type,0);
        }
        if (store->array[i]==NULL) { // memory error
          for (--i;i>=0;i--) livido_free_f (store->array[i]);
          livido_free_f (store);
          return NULL;
        }
      }
    }
  }
  return store;
}

static inline livido_property_t *livido_find_property(livido_port_t *prop, const char *key) {
 while (prop!=NULL) {
  if (!strcmp((char *)prop->key,(char *)key)) return prop;
  prop=prop->next;
 }
 return NULL;
}

static inline livido_storage_t *livido_get_storage_for(livido_port_t *port, const char *key) {
  livido_property_t *prop=livido_find_property (port,key);
  if (prop==NULL) return NULL;
  return prop->data;
}

static inline livido_atom_t *livido_get_atom_from_storage(livido_storage_t *store, int idx) {
  if (idx>store->num_elements||store->num_elements==0) return NULL;
  if (store->num_elements==1) return store->atom;
  return store->array[idx];
}

static inline void livido_property_free(livido_property_t *prop) {
  livido_storage_t *store=prop->data;
  if (store==NULL) return;
  livido_storage_free (store);
  livido_free_f ((char *)prop->key);
}

static inline livido_property_t *livido_property_new(const char *key) {
  size_t lstrlen;
  livido_property_t *prop=(livido_property_t *)livido_malloc_f(sizeof(livido_property_t));
  if (prop==NULL) return NULL;
  if ((prop->key=(char *)livido_malloc_f((lstrlen=(strlen(key)+1))))==NULL) {
    livido_free_f (prop);
    return NULL;
  }
  livido_memcpy_f ((char *)prop->key,key,lstrlen);
  prop->data=NULL;
  prop->next=NULL;
  prop->flags=0;
  return prop;
}

static inline void livido_property_append(livido_port_t *prop,livido_property_t *newprop) {
  livido_property_t *propnext;
  while (prop!=NULL) {
    propnext=prop->next;
    if (propnext==NULL) {
      prop->next=newprop;
      return;
    }
    prop=propnext;
  }
}

void livido_port_free(livido_port_t *prop) {
  livido_property_t *propnext;
  while (prop!=NULL) {
    propnext=prop->next;
    livido_property_free (prop);
    prop=propnext;
  }
}

livido_port_t *livido_port_new(int port_type) {
  livido_property_t *prop;
  size_t size=livido_atom_type_get_size(LIVIDO_ATOM_TYPE_INT,NULL);
  if ((prop=livido_property_new("type"))==NULL) return NULL;
  if ((prop->data=livido_storage_new (LIVIDO_ATOM_TYPE_INT,1,&port_type,&size))==NULL) {
    livido_free_f ((char *)prop->key);
    livido_free_f (prop);
    return NULL;
  }
  prop->next=NULL;
  livido_property_set_readonly (prop,"type");
  return prop;
}

char **livido_list_properties(livido_port_t *port) {
  livido_property_t *prop=port;
  char **proplist;
  register int i=1;
  size_t lstrlen;
  for (;prop!=NULL;i++) {
    prop=prop->next;
  }
  if ((proplist=(char **)livido_malloc_f(i*sizeof(char *)))==NULL) return NULL;
  i=0;
  for (prop=port;prop!=NULL;prop=prop->next) {
    if ((proplist[i]=(char *)livido_malloc_f((lstrlen=strlen(prop->key))+1))==NULL) {
      for (--i;i>=0;i--) livido_free_f (proplist[i]);
      livido_free_f (proplist);
      return NULL;
    }
    livido_memcpy_f (proplist[i],prop->key,lstrlen);
    livido_memset_f (proplist[i++]+lstrlen,0,1);
  }
  proplist[i]=NULL;
  return proplist;
}

int livido_property_set(livido_port_t *port, const char *key, int atom_type, int num_elems, void *value, size_t *size) {
  livido_storage_t *store;
  livido_property_t *prop=livido_find_property (port,key);
  if (prop==NULL) {
    if ((prop=livido_property_new (key))==NULL) return LIVIDO_ERROR_MEMORY_ALLOCATION;
    livido_property_append (port,prop);
  }
  else {
    if (prop->flags&LIVIDO_PROPERTY_READONLY) return LIVIDO_ERROR_PROPERTY_READONLY;
    if (atom_type!=prop->data->atom_type) return LIVIDO_ERROR_WRONG_ATOM_TYPE;
    livido_storage_free (prop->data);
    prop->data=NULL;
  }
  if ((store=livido_storage_new (atom_type,num_elems,value,size))==NULL) {
    return LIVIDO_ERROR_MEMORY_ALLOCATION;
  }
  prop->data=store;
  return LIVIDO_NO_ERROR;
}

int livido_property_get(livido_port_t *port, const char *key, int idx, void *value) {
  livido_atom_t *atom;
  livido_storage_t *store=livido_get_storage_for (port,key);
  if (store==NULL) return LIVIDO_ERROR_NOSUCH_PROPERTY;
  if ((atom=livido_get_atom_from_storage (store,idx))==NULL) return LIVIDO_ERROR_NOSUCH_ELEMENT;
  if (livido_atom_type_is_ptr (store->atom_type)) livido_memcpy_f (value,&atom->value,sizeof(void *));
  else {
    if (store->atom_type==LIVIDO_ATOM_TYPE_STRING) {
      char **valuecharptrptr=(char **)value;
      livido_memcpy_f(*valuecharptrptr,atom->value,atom->size);
      livido_memset_f(*valuecharptrptr+atom->size,0,1);
    }
    else livido_memcpy_f (value,atom->value,atom->size);
  }
  return LIVIDO_NO_ERROR;
}

int livido_property_num_elements(livido_port_t *port, const char *key) {
  livido_storage_t *store=livido_get_storage_for (port, key);
  if (store==NULL) return 0;
  return store->num_elements;
}

int livido_property_element_size(livido_port_t *port, const char *key, int idx) {
  livido_atom_t *atom;
  livido_storage_t *store=livido_get_storage_for (port, key);
  if (store==NULL) return 0;
  if ((atom=livido_get_atom_from_storage (store, idx))==NULL) return 0;
  return atom->size;
}

int livido_property_atom_type(livido_port_t *port, const char *key) {
  livido_storage_t *store=livido_get_storage_for (port, key);
  if (store==NULL) return 0;
  return store->atom_type;
}

int livido_property_set_readonly(livido_port_t *port, const char *key) {
  livido_property_t *prop=livido_find_property (port, key);
  if (prop==NULL) return LIVIDO_ERROR_NOSUCH_PROPERTY;
  prop->flags|=LIVIDO_PROPERTY_READONLY;
  return LIVIDO_NO_ERROR;
}

int livido_property_get_readonly(livido_port_t *port, const char *key) {
  livido_property_t *prop=livido_find_property (port, key);
  if (prop==NULL) return 0;
  return prop->flags&LIVIDO_PROPERTY_READONLY;
}
}}}