From 9680b47879dfc58f884208f7abf2f945b3b41d25 Mon Sep 17 00:00:00 2001 From: "N.N." Date: Wed, 24 Sep 2003 10:46:19 +0000 Subject: adding toxy project svn path=/trunk/externals/miXed/; revision=1024 --- shared/common/props.c | 518 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 518 insertions(+) create mode 100644 shared/common/props.c (limited to 'shared/common/props.c') diff --git a/shared/common/props.c b/shared/common/props.c new file mode 100644 index 0000000..6b6181a --- /dev/null +++ b/shared/common/props.c @@ -0,0 +1,518 @@ +/* Copyright (c) 2003 krzYszcz and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include +#include "m_pd.h" +#include "common/grow.h" +#include "common/props.h" + +//#define PROPS_DEBUG + +#define PROPS_INISIZE 32 /* LATER rethink */ +#define PROPS_MAXOTHERS 32 + +enum { PROPS_NONE = 0, PROPS_THIS, PROPS_OTHER }; + +typedef struct _propelem +{ + char *e_key; + char *e_value; + struct _propelem *e_next; +} t_propelem; + +struct _props +{ + char p_thisescape; + char *p_thisinitial; + char *p_name; + int p_size; /* as allocated */ + int p_natoms; /* as used */ + t_atom *p_buffer; + t_atom p_bufini[PROPS_INISIZE]; + t_pd *p_owner; + t_propsresolver p_resolver; + t_propelem *p_dict; + t_propelem *p_nextelem; + int p_badupdate; + char p_otherescapes[PROPS_MAXOTHERS]; + t_props *p_otherprops; /* props list's head */ + t_props *p_next; +}; + +/* Dictionary of properties, p_dict, meant to be nothing more, but an + optimalization detail, is handled implicitly, through its owning t_props. + This optimalization has to be enabled by passing a nonzero 'resolver' + argument to props_new(). + Since p_dict stores resolved strings, it is a secondary, `shallow' storage, + which has to be synced to its master, p_buffer of atoms. + Currently, p_dict is implemented as an unsorted linked list, which should + be fine in most cases (but might need revisiting LATER). */ + +static t_propelem *propelem_new(char *key, char *value) +{ + t_propelem *ep = (t_propelem *)getbytes(sizeof(*ep)); + ep->e_key = getbytes(strlen(key) + 1); + strcpy(ep->e_key, key); + ep->e_value = getbytes(strlen(value) + 1); + strcpy(ep->e_value, value); + ep->e_next = 0; + return (ep); +} + +static void propelem_free(t_propelem *ep) +{ + if (ep->e_key) freebytes(ep->e_key, strlen(ep->e_key) + 1); + if (ep->e_value) freebytes(ep->e_value, strlen(ep->e_value) + 1); + freebytes(ep, sizeof(*ep)); +} + +/* Returns zero if the key was found (and value replaced), + nonzero if a new element was added. */ +static t_propelem *propelem_add(t_propelem *ep, char *key, char *value) +{ + while (ep) + { + if (strcmp(ep->e_key, key)) + ep = ep->e_next; + else + break; + } + if (ep) + { + if (strcmp(ep->e_value, value)) + { + if (ep->e_value) + ep->e_value = resizebytes(ep->e_value, strlen(ep->e_value) + 1, + strlen(value) + 1); + else + ep->e_value = getbytes(strlen(value) + 1); + strcpy(ep->e_value, value); + } + return (0); + } + else return (propelem_new(key, value)); +} + +static void props_dictadd(t_props *pp, t_symbol *s, int ac, t_atom *av) +{ + if (s && *s->s_name && s->s_name[1] && ac) + { + t_propelem *ep; + char *value = pp->p_resolver(pp->p_owner, ac, av); + if (value && + (ep = propelem_add(pp->p_dict, s->s_name + 1, value))) + { + ep->e_next = pp->p_dict; + pp->p_dict = ep; + } + } +} + +static int props_atstart(t_props *pp, char *buf) +{ + if (*buf == pp->p_thisescape) + { + char c = buf[1]; + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') + || (pp->p_thisinitial && strchr(pp->p_thisinitial, c))) + return (PROPS_THIS); + } + return (PROPS_NONE); +} + +static char *props_otherinitial(t_props *pp, char c) +{ + t_props *pp1 = pp->p_otherprops; + while (pp1) + { + if (pp1 != pp && pp1->p_thisescape == c) + return (pp1->p_thisinitial); + pp1 = pp1->p_next; + } + bug("props_otherinitial"); + post("(%c \"%s\")", c, pp->p_otherescapes); + return (0); +} + +static int props_atnext(t_props *pp, char *buf) +{ + char *otherinitial; + if (*buf == pp->p_thisescape) + { + char c = buf[1]; + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') + || (pp->p_thisinitial && strchr(pp->p_thisinitial, c))) + return (PROPS_THIS); + } + else if (*pp->p_otherescapes && strchr(pp->p_otherescapes, *buf)) + { + char c = buf[1]; + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') + || ((otherinitial = props_otherinitial(pp, *buf)) + && *otherinitial && strchr(otherinitial, c))) + return (PROPS_OTHER); + } + return (PROPS_NONE); +} + +/* Search for a property, replace its value if found, otherwise add. + Assuming s is valid. Returning nafter - nbefore. */ +static int props_update(t_props *pp, t_symbol *s, int ac, t_atom *av, int doit) +{ + int nadd, ndiff, ibeg, iend = 0; + t_atom *ap; + for (nadd = 0, ap = av; nadd < ac; nadd++, ap++) + if (ap->a_type == A_SYMBOL + && props_atnext(pp, ap->a_w.w_symbol->s_name)) + break; + if (!nadd) + { + pp->p_badupdate = 1; + return (0); + } + pp->p_badupdate = 0; + nadd++; + for (ibeg = 0, ap = pp->p_buffer; ibeg < pp->p_natoms; ibeg++, ap++) + { + if (ap->a_type == A_SYMBOL && ap->a_w.w_symbol == s) + { + for (iend = ibeg + 1, ap++; iend < pp->p_natoms; iend++, ap++) + if (ap->a_type == A_SYMBOL + && props_atnext(pp, ap->a_w.w_symbol->s_name)) + break; + break; + } + } + ndiff = (iend > ibeg ? nadd - (iend - ibeg) : nadd); + if (doit) + { + int i, newnatoms = pp->p_natoms + ndiff; + if (newnatoms > pp->p_size) + { + bug("props_update"); + return (0); + } +#ifdef PROPS_DEBUG + post("%s %s, [%d..%d), ndiff %d", + (iend > ibeg ? "replacing" : "adding"), s->s_name, + ibeg, iend, ndiff); +#endif + if (iend > ibeg) + { + if (ndiff > 0) + { + t_atom *ap2 = pp->p_buffer + newnatoms; + t_atom *ap1 = ap2 - ndiff; + for (i = iend; i < pp->p_natoms; i++) *--ap2 = *--ap1; + } + else if (ndiff < 0) + { + t_atom *ap2 = pp->p_buffer + iend; + t_atom *ap1 = ap2 + ndiff; + for (i = iend; i < pp->p_natoms; i++) *ap1++ = *ap2++; + } + ap = pp->p_buffer + ibeg; + } + else + { + ap = pp->p_buffer + pp->p_natoms; + SETSYMBOL(ap, s); + } + ap++; + nadd--; + if (pp->p_resolver) props_dictadd(pp, s, nadd, av); + for (i = 0; i < nadd; i++) *ap++ = *av++; + pp->p_natoms = newnatoms; + } + return (ndiff); +} + +/* If there is an empty property, do not parse beyond. + Return the offending switch, if any. */ +t_symbol *props_add(t_props *pp, t_symbol *s, int ac, t_atom *av) +{ + t_symbol *empty = 0; + t_atom *av1, *ap; + int ac1, i, ngrown = 0; + if (s && props_atstart(pp, s->s_name)) + ngrown += props_update(pp, s, ac, av, 0); + if (pp->p_badupdate) + empty = s; + else for (i = 0, ap = av; i < ac; i++, ap++) + { + if (ap->a_type == A_SYMBOL + && props_atstart(pp, ap->a_w.w_symbol->s_name)) + { + ngrown += props_update(pp, ap->a_w.w_symbol, ac - i - 1, ap + 1, 0); + if (pp->p_badupdate) + { + empty = ap->a_w.w_symbol; + break; + } + } + } + ngrown += pp->p_natoms; + if (ngrown > pp->p_size) + { + int nrequested = ngrown; + pp->p_buffer = grow_withdata(&nrequested, &pp->p_natoms, + &pp->p_size, pp->p_buffer, + PROPS_INISIZE, pp->p_bufini, + sizeof(*pp->p_buffer)); + if (nrequested != ngrown) + goto done; + } + ac1 = (s ? ac + 1 : ac); + if (!(av1 = getbytes(ac1 * sizeof(*av1)))) + goto done; + ap = av1; + if (s) + { + SETSYMBOL(ap, s); + ap++; + } + while (ac--) *ap++ = *av++; + ac = ac1; + av = av1; + for (i = 0, ap = av; i < ac; i++, ap++) + { + if (ap->a_type == A_SYMBOL + && props_atstart(pp, ap->a_w.w_symbol->s_name)) + { + props_update(pp, ap->a_w.w_symbol, ac - i - 1, ap + 1, 1); + if (pp->p_badupdate) + { + empty = ap->a_w.w_symbol; + break; + } + } + } + freebytes(av1, ac1 * sizeof(*av1)); +done: + return (empty); +} + +/* FIXME remove from p_dict */ +int props_remove(t_props *pp, t_symbol *s) +{ + int ac; + t_atom *av = props_getone(pp, s, &ac); + if (av) + { + int i; + t_atom *ap = av + ac; + t_atom *guard = pp->p_buffer + pp->p_natoms; + while (ap < guard) *av++ = *ap++; + pp->p_natoms -= ac; + return (1); + } + else return (0); +} + +/* LATER think about 'deep' cloning, i.e. propagating source atoms into + the destination buffer. Since cloning, unless requested by the user, + should never be persistent (source atoms should not stick to the + destination object in a .pd file), deep cloning requires introducing + a two-buffer scheme. There is no reason for deep cloning of arguments, + or handlers, but options could benefit. */ + +void props_clone(t_props *to, t_props *from) +{ + if (to->p_resolver) + { + /* LATER make this into a generic traversing method */ + int ibeg = 0, iend = 0; + t_atom *abeg = from->p_buffer; + t_atom *ap = abeg; + while (ibeg < from->p_natoms) + { + if (ap->a_type == A_SYMBOL && + props_atstart(from, ap->a_w.w_symbol->s_name)) + { + for (iend = ibeg + 1, ap++; iend < from->p_natoms; iend++, ap++) + if (ap->a_type == A_SYMBOL + && props_atnext(from, ap->a_w.w_symbol->s_name)) + break; + props_dictadd(to, abeg->a_w.w_symbol, + iend - ibeg - 1, abeg + 1); + if (iend < from->p_natoms) + { + ibeg = iend; + abeg = ap; + } + else break; + } + else + { + ibeg++; + ap++; + } + } + } + else + { + /* LATER */ + } +} + +/* only dictionary-enabled properties handle props_...value() calls */ + +char *props_getvalue(t_props *pp, char *key) +{ + if (pp->p_resolver) + { + t_propelem *ep = pp->p_dict; + while (ep) + { + if (strcmp(ep->e_key, key)) + ep = ep->e_next; + else + return (ep->e_value); + } + } + return (0); +} + +char *props_nextvalue(t_props *pp, char **keyp) +{ + if (pp->p_nextelem) + { + char *value = pp->p_nextelem->e_value; + *keyp = pp->p_nextelem->e_key; + pp->p_nextelem = pp->p_nextelem->e_next; + return (value); + } + return (0); +} + +char *props_firstvalue(t_props *pp, char **keyp) +{ + if (pp->p_resolver) + pp->p_nextelem = pp->p_dict; + return (props_nextvalue(pp, keyp)); +} + +t_atom *props_getone(t_props *pp, t_symbol *s, int *npp) +{ + int ibeg, iend = 0; + t_atom *ap; + if (!(s && props_atstart(pp, s->s_name))) + return (0); + for (ibeg = 0, ap = pp->p_buffer; ibeg < pp->p_natoms; ibeg++, ap++) + { + if (ap->a_type == A_SYMBOL && ap->a_w.w_symbol == s) + { + for (iend = ibeg + 1, ap++; iend < pp->p_natoms; iend++, ap++) + if (ap->a_type == A_SYMBOL + && props_atnext(pp, ap->a_w.w_symbol->s_name)) + break; + break; + } + } + if (iend > ibeg) + { + *npp = iend - ibeg; + return (pp->p_buffer + ibeg); + } + else return (0); +} + +t_atom *props_getall(t_props *pp, int *npp) +{ + *npp = pp->p_natoms; + return (pp->p_buffer); +} + +char *props_getname(t_props *pp) +{ + return (pp ? pp->p_name : "property"); +} + +static void props_freeone(t_props *pp) +{ + if (pp->p_buffer != pp->p_bufini) + freebytes(pp->p_buffer, pp->p_size * sizeof(*pp->p_buffer)); + while (pp->p_dict) + { + t_propelem *ep = pp->p_dict->e_next; + propelem_free(pp->p_dict); + pp->p_dict = ep; + } + freebytes(pp, sizeof(*pp)); +} + +void props_freeall(t_props *pp) +{ + if (pp && (pp = pp->p_otherprops)) + { + while (pp) + { + t_props *pp1 = pp->p_next; + props_freeone(pp); + pp = pp1; + } + } + else bug("props_freeall"); +} + +void props_setupothers(t_props *pp, t_props *otherprops) +{ + t_props *pp1; + pp->p_next = (otherprops ? otherprops->p_otherprops : 0); + for (pp1 = pp; pp1; pp1 = pp1->p_next) + { + t_props *pp2; + char *bp = pp1->p_otherescapes; + int i; + pp1->p_otherprops = pp; + for (pp2 = pp, i = 1; pp2 && i < PROPS_MAXOTHERS; + pp2 = pp2->p_next, i++) + if (pp2 != pp1) + *bp++ = pp2->p_thisescape; + *bp = 0; +#ifdef PROPS_DEBUG + startpost("%c \"%s\" ", pp1->p_thisescape, pp1->p_otherescapes); +#endif + } +#ifdef PROPS_DEBUG + endpost(); +#endif +} + +/* nonzero resolver requires the owner to be nonzero */ +t_props *props_new(t_pd *owner, char *name, char *thisdelim, + t_props *otherprops, t_propsresolver resolver) +{ + t_props *pp = getbytes(sizeof(*pp)); + if (pp) + { + pp->p_name = name; + if (thisdelim && *thisdelim) + { + pp->p_thisescape = *thisdelim++; + pp->p_thisinitial = (*thisdelim ? thisdelim : 0); + } + else + { + bug("props_new (no escape)"); + pp->p_thisescape = '-'; + pp->p_thisinitial = 0; + } + props_setupothers(pp, otherprops); + pp->p_size = PROPS_INISIZE; + pp->p_natoms = 0; + pp->p_buffer = pp->p_bufini; + if (pp->p_owner = owner) + pp->p_resolver = resolver; + else + { + if (resolver) + bug("props_new (no owner)"); + pp->p_resolver = 0; + } + pp->p_dict = 0; + pp->p_nextelem = 0; + } + return (pp); +} -- cgit v1.2.1