/* Copyright (c) 2004-2005 N.Elburg All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define _GNU_SOURCE #include #include #include #include #include #include #include /* If defined, dont expect a major performance improvment. */ #ifdef VEVO_MEMPOOL #include #endif #ifdef STRICT_CHECKING #include #endif /* The storage container, this holds the atoms of a property */ typedef struct { int atom_type; union { atom_t *atom; atom_t **array; } elements; int num_elements; int flags; } livido_storage_t; /* For livido ports use a linked list , especially when a port does not hold a large number of properties a linked list is usually faster. Non livido ports are initialized with a hash, which is efficient for a large number of properties */ typedef struct { livido_storage_t *st; int key; void *next; } livido_property_t; /* For fast indexing of livido keys, define the port index The function livido_property_set adds a key to the port index if the property does not exist yet. */ typedef struct { const char *key; int hash_code; /* vevo uses a integer representation of key, eliminates strcmp */ void *next; } port_index_t; /* Now, define our port structure */ typedef struct { hash_t *table; /* hashtable for custom ports */ livido_property_t *list; /* linked list for livido ports */ port_index_t *index; /* key index */ void *pool; /* memory pool (compile option) */ int atom_types[70]; /* atom types to vevo types */ } __vevo_port_t; /* Functions for creating new 'properties' */ static livido_property_t *prop_node_new(int hash_key, livido_storage_t * stor) { livido_property_t *p = (livido_property_t *) malloc(sizeof(livido_property_t)); p->st = stor; p->key = hash_key; p->next = NULL; return p; } static void prop_node_free(livido_property_t * p) { if (p) { free(p); } } static livido_property_t *prop_node_append(livido_port_t * p, int key, livido_storage_t * t) { __vevo_port_t *port = (__vevo_port_t *) p; livido_property_t *node = prop_node_new(key, t); livido_property_t *next; livido_property_t *list = port->list; if (list == NULL) port->list = node; else { while (list != NULL) { next = list->next; if (next == NULL) { list->next = node; return node; } list = next; } } return node; } static livido_property_t *prop_node_get(livido_port_t * p, int key) { __vevo_port_t *port = (__vevo_port_t *) p; livido_property_t *l = port->list; while (l != NULL) { if (key == l->key) return l; l = l->next; } return NULL; } /* Functions for new key items */ static inline port_index_t *port_node_new(const char *key, int hash_key) { port_index_t *i = (port_index_t *) malloc(sizeof(port_index_t)); #ifdef STRICT_CHECKING assert(i != NULL); assert(key != NULL); assert(hash_key != 0); #endif i->key = strdup(key); i->hash_code = hash_key; i->next = NULL; return i; } static inline void port_node_free(port_index_t * node) { if (node) { if (node->key) free((void *) node->key); free(node); } node = NULL; } static inline void port_node_append(livido_port_t * p, const char *key, int hash_key) { #ifdef STRICT_CHECKING assert(p != NULL); assert(key != NULL); assert(hash_key != 0); #endif __vevo_port_t *port = (__vevo_port_t *) p; port_index_t *node = port_node_new(key, hash_key); port_index_t *next; port_index_t *list = port->index; if (list == NULL) port->index = node; else { while (list != NULL) { next = list->next; if (next == NULL) { list->next = node; break; } list = next; } } } /* forward declarations */ #define property_exists( port, key ) hash_lookup( (hash_t*) port->table, (const void*) key ) #define atom_store__(value) {\ for (i = 0; i < d->num_elements; i++)\ d->elements.array[i] = livido_put_atom(port, &value[i], v ); } #define array_load__(value) {\ for( i = 0; i < t->num_elements ; i ++ )\ memcpy( &value[i], t->elements.array[i]->value, t->elements.array[i]->size ); } /* fast key hashing */ static inline int hash_key_code(const char *key) { int hash = 0; while (*key) { hash <<= 1; if (hash < 0) hash |= 1; hash ^= *key++; } return hash; } static int livido_property_finalize(livido_port_t * p, const char *key) { __vevo_port_t *port = (__vevo_port_t *) p; int hash_key = hash_key_code(key); if (!port->table) { livido_property_t *node = NULL; if ((node = prop_node_get(port, hash_key)) != NULL) { node->st->flags |= LIVIDO_PROPERTY_READONLY; return LIVIDO_NO_ERROR; } } else { hnode_t *node = NULL; if ((node = property_exists(port, hash_key)) != NULL) { livido_storage_t *stor = (livido_storage_t *) hnode_get(node); stor->flags |= LIVIDO_PROPERTY_READONLY; hnode_t *new_node = hnode_create((void *) stor); hnode_put(new_node, (void *) hash_key); hnode_destroy(new_node); return LIVIDO_NO_ERROR; } } //fixme: error is wrong return LIVIDO_NO_ERROR; } static int atom_get_value(livido_storage_t * t, int idx, void *dst) { #ifdef STRICT_CHECKING assert(t != NULL); assert(dst != NULL); #endif atom_t *atom = NULL; if (t->num_elements == 1 && idx == 0) atom = t->elements.atom; if (t->num_elements > 1 && idx >= 0 && idx <= t->num_elements) atom = t->elements.array[idx]; if (!atom) return LIVIDO_ERROR_NOSUCH_ELEMENT; if (atom->size <= 0) return LIVIDO_NO_ERROR; if (t->atom_type != LIVIDO_ATOM_TYPE_STRING) { memcpy(dst, atom->value, atom->size); } else { char **ptr = (char **) dst; char *p = *ptr; memcpy(p, atom->value, (atom->size - 1)); p[atom->size - 1] = '\0'; } return LIVIDO_NO_ERROR; } static size_t livido_atom_size(int atom_type) { switch (atom_type) { case LIVIDO_ATOM_TYPE_DOUBLE: return sizeof(double); case LIVIDO_ATOM_TYPE_INT: return sizeof(int32_t); case LIVIDO_ATOM_TYPE_VOIDPTR: return sizeof(void *); case LIVIDO_ATOM_TYPE_STRING: return 0; case LIVIDO_ATOM_TYPE_PORTPTR: return sizeof(livido_port_t *); case LIVIDO_ATOM_TYPE_BOOLEAN: return sizeof(int32_t); default: #ifdef STRICT_CHECKING assert(0); #endif break; } return 0; } static atom_t *livido_new_atom(__vevo_port_t * port, int atom_type, size_t atom_size) { #ifdef VEVO_MEMPOOL atom_t *atom = (atom_t *) vevo_malloc(port->pool, ATOM_ATOM); #else atom_t *atom = (atom_t *) malloc(sizeof(atom_t)); #endif #ifdef STRICT_CHECKING assert(atom != NULL); #endif atom->size = atom_size; #ifndef VEVO_MEMPOOL atom->value = (atom_size > 0 ? (void *) malloc(atom_size) : NULL); #else atom->type = port->atom_types[atom_type]; if (atom->type >= 0) { atom->value = (atom_size > 0 ? (void *) vevo_malloc(port->pool, atom->type) : NULL); } else atom->value = (atom_size > 0 ? (void *) malloc(atom_size) : NULL); #endif #ifdef STRICT_CHECING assert(atom->value != NULL); #endif return atom; } static void livido_free_atom(void *pool, atom_t * atom) { #ifdef STRICT_CHECKING assert(atom != NULL); #endif if (atom) { #ifndef VEVO_MEMPOOL free(atom->value); free(atom); #else if (atom->type >= 0) vevo_free(pool, atom->value, atom->type); else free(atom->value); vevo_free(pool, atom, ATOM_ATOM); #endif } atom = NULL; } static atom_t *livido_put_atom(__vevo_port_t * port, void *dst, int atom_type) { atom_t *atom = NULL; size_t atom_size = livido_atom_size(atom_type); if (atom_type == LIVIDO_ATOM_TYPE_STRING) { char **s = (char **) dst; atom_size = strlen(*s) + 1; atom = livido_new_atom(port, atom_type, atom_size); if (atom_size > 0) memcpy(atom->value, *s, (atom_size - 1)); } else { #ifdef STRICT_CHECKING assert(atom_size > 0); assert(dst != NULL); #endif atom = livido_new_atom(port, atom_type, atom_size); #ifdef STRICT_CHECING assert(atom != NULL); assert(atom->value != NULL); #else if (!atom) return NULL; #endif memcpy(atom->value, dst, atom_size); } return atom; } static void storage_put_atom_value(__vevo_port_t * port, void *src, int n, livido_storage_t * d, int v) { int i; #ifdef STRICT_CHECKING if (n > 0) assert((src != NULL)); #endif if (d->num_elements >= 0) { if (d->num_elements >= 0 && d->num_elements <= 1) { if (d->elements.atom) livido_free_atom(port->pool, d->elements.atom); } else if (d->num_elements > 1) { if (d->elements.array) { for (i = 0; i < d->num_elements; i++) livido_free_atom(port->pool, d->elements.array[i]); free(d->elements.array); } } } d->atom_type = v; d->num_elements = n; switch (n) { case 0: d->elements.atom = livido_new_atom(port, v, livido_atom_size(v)); break; case 1: d->elements.atom = livido_put_atom(port, src, v); break; default: d->elements.array = (atom_t **) malloc(sizeof(atom_t *) * n); if (d->atom_type == LIVIDO_ATOM_TYPE_DOUBLE) { double *value = (double *) src; atom_store__(value); } else { if (d->atom_type == LIVIDO_ATOM_TYPE_INT || d->atom_type == LIVIDO_ATOM_TYPE_BOOLEAN) { int32_t *value = (int *) src; atom_store__(value); } else { void **value = (void **) src; atom_store__(value); } } break; } } static inline livido_storage_t *livido_new_storage(int num_elements) { livido_storage_t *d = (livido_storage_t *) malloc(sizeof(livido_storage_t)); #ifdef HAVE_STRICT assert(d != NULL); #endif d->elements.atom = NULL; d->num_elements = num_elements; d->flags = 0; return d; } static inline void livido_free_storage(void *pool, livido_storage_t * t) { if (t) { if (t->num_elements > 1) { int i; for (i = 0; i < t->num_elements; i++) livido_free_atom(pool, t->elements.array[i]); free(t->elements.array); } if (t->num_elements <= 1) livido_free_atom(pool, t->elements.atom); free(t); } t = NULL; } static inline hash_val_t int_hash(const void *key) { return (hash_val_t) key; } static inline int key_compare(const void *key1, const void *key2) { return ((int) key1 == (int) key2 ? 0 : 1); } /* Livido API implementation, public functions follow below */ int livido_property_num_elements(livido_port_t * p, const char *key) { #ifdef STRICT_CHECKING assert(p != NULL); assert(key != NULL); #endif if(!p) return -1; __vevo_port_t *port = (__vevo_port_t *) p; int hash_key = hash_key_code(key); if (!port->table) { livido_property_t *node; if ((node = prop_node_get(port, hash_key)) != NULL) return node->st->num_elements; } else { hnode_t *node = NULL; if ((node = property_exists(port, hash_key)) != NULL) { livido_storage_t *stor = (livido_storage_t *) hnode_get(node); if (stor) return stor->num_elements; } } return -1; } int livido_property_atom_type(livido_port_t * p, const char *key) { #ifdef STRICT_CHECKING assert(p != NULL); assert(key != NULL); #endif __vevo_port_t *port = (__vevo_port_t *) p; #ifdef STRICT_CHECKING assert(port != NULL); #endif int hash_key = hash_key_code(key); if (!port->table) { livido_property_t *node; if ((node = prop_node_get(port, hash_key)) != NULL) return node->st->atom_type; } else { hnode_t *node = NULL; #ifdef STRICT_CHECKING assert(port->table != NULL); assert(hash_verify(port->table) != 0); #endif if ((node = property_exists(port, hash_key)) != NULL) { livido_storage_t *stor = (livido_storage_t *) hnode_get(node); if (stor) return stor->atom_type; } } return -1; } size_t livido_property_element_size(livido_port_t * p, const char *key, const int idx) { #ifdef STRICT_CHECKING assert(p != NULL); assert(key != NULL); assert(idx >= 0); #endif if(!p) return -1; __vevo_port_t *port = (__vevo_port_t *) p; int hash_key = hash_key_code(key); if (!port->table) { livido_property_t *node; if ((node = prop_node_get(port, hash_key)) != NULL) { #ifdef STRICT_CHECKING if (idx > 0) assert(idx < node->st->num_elements); #endif if (node->st->num_elements == 1) return node->st->elements.atom->size; else if (node->st->num_elements > 1) return node->st->elements.array[idx]->size; return 0; } } else { hnode_t *node = NULL; if ((node = property_exists(port, hash_key)) != NULL) { livido_storage_t *stor = (livido_storage_t *) hnode_get(node); #ifdef STRICT_CHECKING assert(idx < stor->num_elements); #endif //todo: sum all element sizes for index of -1 if (stor->num_elements == 1) { return stor->elements.atom->size; } else if (stor->num_elements > 1) { return stor->elements.array[idx]->size; } else { if (stor->num_elements == 0) return 0; } } } return -1; } livido_port_t *livido_port_new(int port_type) { __vevo_port_t *port = (__vevo_port_t *) malloc(sizeof(__vevo_port_t)); #ifdef STRICT_CHECKING assert(port != NULL); #endif port->index = NULL; port->list = NULL; port->table = NULL; /* If the port type is a Livido port this or that */ if (port_type >= 1 && port_type <= 10) port->list = NULL; else port->table = hash_create(HASHCOUNT_T_MAX, key_compare, int_hash); #ifdef VEVO_MEMPOOL port->pool = vevo_new_pool(); port->atom_types[LIVIDO_ATOM_TYPE_INT] = ATOM_INT; port->atom_types[LIVIDO_ATOM_TYPE_DOUBLE] = ATOM_DBL; port->atom_types[LIVIDO_ATOM_TYPE_BOOLEAN] = ATOM_INT; port->atom_types[LIVIDO_ATOM_TYPE_VOIDPTR] = ATOM_PTR; port->atom_types[LIVIDO_ATOM_TYPE_PORTPTR] = ATOM_PTR; port->atom_types[LIVIDO_ATOM_TYPE_STRING] = -1; #else port->pool = NULL; #endif livido_property_set(port, "type", LIVIDO_ATOM_TYPE_INT, 1, &port_type); livido_property_finalize(port, "type"); #ifdef STRICT_CHECKING assert(livido_property_set (port, "type", LIVIDO_ATOM_TYPE_INT, 1, &port_type) != LIVIDO_PROPERTY_READONLY); #endif return (livido_port_t *) port; } void livido_port_free(livido_port_t * p) { __vevo_port_t *port = (__vevo_port_t *) p; if (port) { if (port->table) { if (!hash_isempty((hash_t *) port->table)) { hscan_t scan; hash_scan_begin(&scan, (hash_t *) port->table); hnode_t *node; while ((node = hash_scan_next(&scan)) != NULL) { livido_storage_t *stor; stor = hnode_get(node); #ifdef STRICT_CHECKING assert(stor != NULL); assert(node != NULL); assert((const char *) hnode_getkey(node) != NULL); #endif livido_free_storage(port->pool, stor); } hash_free_nodes((hash_t *) port->table); hash_destroy((hash_t *) port->table); } } else { livido_property_t *l = port->list; livido_property_t *n; while (l != NULL) { n = l->next; livido_free_storage(port->pool, l->st); prop_node_free(l); l = n; } } if (port->index) { port_index_t *l = port->index; port_index_t *n = NULL; while (l != NULL) { n = l->next; port_node_free(l); l = n; } } #ifdef VEVO_MEMPOOL vevo_free_pool(port->pool); #endif free(port); } port = NULL; } int livido_property_set(livido_port_t * p, const char *key, int atom_type, int num_elements, void *src) { #ifdef STRICT_CHECKING assert(p != NULL); #endif __vevo_port_t *port = (__vevo_port_t *) p; int hash_key = hash_key_code(key); int new = 1; void *node = NULL; if (!port->table) { livido_property_t *pnode = NULL; if ((pnode = prop_node_get(port, hash_key)) != NULL) { if (pnode->st->atom_type != atom_type) return LIVIDO_ERROR_WRONG_ATOM_TYPE; if (pnode->st->flags & LIVIDO_PROPERTY_READONLY) return LIVIDO_ERROR_PROPERTY_READONLY; livido_free_storage(port->pool, pnode->st); new = 0; node = (void *) pnode; } } else { hnode_t *old_node = NULL; if ((old_node = property_exists(port, hash_key)) != NULL) { livido_storage_t *oldstor = (livido_storage_t *) hnode_get(old_node); if (oldstor->atom_type != atom_type) return LIVIDO_ERROR_WRONG_ATOM_TYPE; if (oldstor->flags & LIVIDO_PROPERTY_READONLY) return LIVIDO_ERROR_PROPERTY_READONLY; livido_free_storage(port->pool, oldstor); hash_delete((hash_t *) port->table, old_node); hnode_destroy(old_node); new = 0; } } livido_storage_t *stor = livido_new_storage(num_elements); storage_put_atom_value(port, src, num_elements, stor, atom_type); if (new) { #ifdef VEVO_MEMPOOL if (!vevo_pool_verify(port->pool)) return LIVIDO_ERROR_MEMORY_ALLOCATION; #endif port_node_append(port, key, hash_key); if (!port->table) node = (void *) prop_node_append(port, hash_key, stor); } #ifdef STRICT_CHECKING assert(stor != NULL); #endif if (!port->table) { #ifdef STRICT_CHECKING assert(node != NULL); #endif if (!new) { livido_property_t *current = (livido_property_t *) node; current->st = stor; } } else { hnode_t *node2 = hnode_create(stor); #ifdef STRICT_CHECKING assert(node2 != NULL); assert(!hash_isfull((hash_t *) port->table)); assert(!property_exists(port, hash_key)); #endif hash_insert((hash_t *) port->table, node2, (const void *) hash_key); } return LIVIDO_NO_ERROR; } int livido_property_get(livido_port_t * p, const char *key, int idx, void *dst) { #ifdef STRICT_CHECKING assert(p != NULL); #endif __vevo_port_t *port = (__vevo_port_t *) p; if(!p) return LIVIDO_ERROR_NOSUCH_PROPERTY; #ifdef STRICT_CHECKING assert(key != NULL); #endif int hash_key = hash_key_code(key); if (!port->table) { livido_property_t *node = NULL; if ((node = prop_node_get(port, hash_key)) != NULL) { if (dst == NULL) return LIVIDO_NO_ERROR; else return atom_get_value(node->st, idx, dst); } } else { hnode_t *node = NULL; if ((node = property_exists(port, hash_key)) != NULL) { if (dst == NULL) return LIVIDO_NO_ERROR; else return atom_get_value((livido_storage_t *) hnode_get(node), idx, dst); } } return LIVIDO_ERROR_NOSUCH_PROPERTY; } char **livido_list_properties(livido_port_t * p) { __vevo_port_t *port = (__vevo_port_t *) p; #ifdef STRICT_CHECKING assert(port != NULL); #endif if(!p) return NULL; char **list = NULL; #ifdef STRICT_CHECKING int nn = 1; if (port->table) nn += hash_count((hash_t *) port->table); else { livido_property_t *c = port->list; while (c != NULL) { c = c->next; nn++; } } #endif int n = 1; // null terminated list of keys int i = 0; port_index_t *l = port->index; while (l != NULL) { l = l->next; n++; } #ifdef STRICT_CHECKING assert(nn == n); #endif list = (char **) malloc(sizeof(char *) * n); if (!list) return NULL; l = (port_index_t *) port->index; i = 0; while (l != NULL) { list[i] = (char *) strdup(l->key); #ifdef STRICT_CHECING assert(list[i] != NULL); #endif l = l->next; i++; } list[i] = NULL; return list; }