/* Copyright (c) 2002-2005 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/loud.h" #include "common/grow.h" #include "common/fitter.h" #define PREPEND_INISIZE 32 /* LATER rethink */ #define PREPEND_MAXSIZE 256 typedef struct _prepend { t_object x_ob; t_symbol *x_selector; int x_size; /* as allocated */ int x_natoms; /* as used */ t_atom *x_message; t_atom x_messini[PREPEND_INISIZE]; int x_entered; int x_auxsize; t_atom *x_auxbuf; t_pd *x_proxy; } t_prepend; typedef struct _prependxy { t_pd xy_pd; t_prepend *xy_owner; } t_prependxy; static t_class *prepend_class; static t_class *prependxy_class; static int prepend_iscompatible = 0; /* FIXME per-object */ /* Usually a preallocation method is used, except in special cases of: 1) reentrant output request, or 2) an output request which would cause resizing to more than MAXSIZE (no such limit for a 'set' message). In both special cases, a temporary output buffer is allocated. A separately preallocated output buffer is not used, thus avoiding memcpying of the stored message (a small performance gain when the preallocation method is chosen). Instead, self-invoked 'set' messages are postponed, using an auxiliary buffer. */ /* called only from prepend_doanything() */ static void prepend_dooutput(t_prepend *x, int ac, t_atom *av) { if (x->x_selector == &s_float) { if (ac > 1) outlet_list(((t_object *)x)->ob_outlet, &s_list, ac, av); else outlet_float(((t_object *)x)->ob_outlet, av->a_w.w_float); } else if (x->x_selector == &s_list) outlet_list(((t_object *)x)->ob_outlet, &s_list, ac, av); else /* x->x_selector guaranteed non-empty */ /* CHECKED: 'bang' is prepended -- we cannot do so... ('symbol' cannot be compatible too) */ outlet_anything(((t_object *)x)->ob_outlet, x->x_selector, ac, av); } static void prepend_doanything(t_prepend *x, t_symbol *s, int ac, t_atom *av) { int reentered = x->x_entered; x->x_entered = 1; if (s == &s_) s = 0; if (s || x->x_natoms) { int prealloc = !reentered; int ntotal = x->x_natoms + ac; t_atom *buf; if (s) ntotal++; if (prealloc && ntotal > x->x_size) { if (ntotal > PREPEND_MAXSIZE) prealloc = 0; else { int nrequested = ntotal; x->x_message = grow_withdata(&nrequested, &x->x_natoms, &x->x_size, x->x_message, PREPEND_INISIZE, x->x_messini, sizeof(*x->x_message)); prealloc = (nrequested == ntotal); } } if (prealloc) { buf = x->x_message + x->x_natoms; if (s) { SETSYMBOL(buf, s); buf++; } if (ac) memcpy(buf, av, ac * sizeof(*buf)); prepend_dooutput(x, ntotal, x->x_message); } else { /* LATER consider using the stack if ntotal <= MAXSTACK */ if (buf = getbytes(ntotal * sizeof(*buf))) { t_atom *bp = buf + x->x_natoms; if (x->x_natoms) memcpy(buf, x->x_message, x->x_natoms * sizeof(*buf)); if (s) { SETSYMBOL(bp, s); bp++; } if (ac) memcpy(bp, av, ac * sizeof(*bp)); prepend_dooutput(x, ntotal, buf); freebytes(buf, ntotal * sizeof(*buf)); } } } else prepend_dooutput(x, ac, av); if (!reentered) { x->x_entered = 0; if (x->x_auxbuf) { if (x->x_auxsize <= x->x_size) { x->x_natoms = x->x_auxsize / 2; memcpy(x->x_message, x->x_auxbuf, x->x_natoms * sizeof(*x->x_message)); freebytes(x->x_auxbuf, x->x_auxsize * sizeof(*x->x_auxbuf)); } else { if (x->x_message != x->x_messini) freebytes(x->x_message, x->x_size * sizeof(*x->x_message)); x->x_size = x->x_auxsize; x->x_message = x->x_auxbuf; x->x_natoms = x->x_auxsize / 2; } x->x_auxbuf = 0; } } } static void prepend_bang(t_prepend *x) { if (x->x_selector) { if (prepend_iscompatible) { t_atom at; SETSYMBOL(&at, &s_bang); /* CHECKED */ prepend_doanything(x, 0, 1, &at); } else prepend_doanything(x, 0, 0, 0); } else outlet_bang(((t_object *)x)->ob_outlet); } static void prepend_float(t_prepend *x, t_float f) { if (x->x_selector) { t_atom at; SETFLOAT(&at, f); prepend_doanything(x, 0, 1, &at); } else outlet_float(((t_object *)x)->ob_outlet, f); } static void prepend_symbol(t_prepend *x, t_symbol *s) { if (x->x_selector) { t_atom at; SETSYMBOL(&at, s); prepend_doanything(x, 0, 1, &at); } else outlet_symbol(((t_object *)x)->ob_outlet, s); } /* LATER gpointer */ static void prepend_list(t_prepend *x, t_symbol *s, int ac, t_atom *av) { if (x->x_selector) prepend_doanything(x, 0, ac, av); else outlet_list(((t_object *)x)->ob_outlet, s, ac, av); } static void prepend_anything(t_prepend *x, t_symbol *s, int ac, t_atom *av) { if (x->x_selector) prepend_doanything(x, s, ac, av); else outlet_anything(((t_object *)x)->ob_outlet, s, ac, av); } static void prepend_doset(t_prepend *x, t_symbol *s, int ac, t_atom *av) { if (s) x->x_selector = s; else if (ac) { if (av->a_type == A_SYMBOL) { x->x_selector = av->a_w.w_symbol; ac--; av++; } else if (av->a_type == A_FLOAT) x->x_selector = (ac > 1 ? &s_list : &s_float); else return; /* LATER rethink */ } else x->x_selector = 0; if (ac) { int newsize = ac * 2; if (x->x_entered) { if (x->x_auxbuf) { loud_warning((t_pd *)x, 0, "'set' message overridden"); freebytes(x->x_auxbuf, x->x_auxsize * sizeof(*x->x_auxbuf)); x->x_auxsize = 0; } if (x->x_auxbuf = getbytes(newsize * sizeof(*x->x_auxbuf))) { memcpy(x->x_auxbuf, av, ac * sizeof(*x->x_auxbuf)); x->x_auxsize = newsize; } } else { t_atom *ap; if (newsize > x->x_size) { int sz = newsize; x->x_message = grow_nodata(&sz, &x->x_size, x->x_message, PREPEND_INISIZE, x->x_messini, sizeof(*x->x_message)); if (sz != newsize) ac = sz / 2; /* LATER rethink */ } x->x_natoms = ac; ap = x->x_message; while (ac--) *ap++ = *av++; } } else x->x_natoms = 0; } static void prepend_set(t_prepend *x, t_symbol *s, int ac, t_atom *av) { if (x->x_proxy) prepend_anything(x, s, ac, av); else /* LATER (when?) controlled by maxmode */ prepend_doset(x, 0, ac, av); } static void prependxy_bang(t_prependxy *xy) { prepend_doset(xy->xy_owner, 0, 0, 0); /* LATER rethink */ } static void prependxy_float(t_prependxy *xy, t_float f) { t_atom at; SETFLOAT(&at, f); prepend_doset(xy->xy_owner, &s_float, 1, &at); } static void prependxy_symbol(t_prependxy *xy, t_symbol *s) { prepend_doset(xy->xy_owner, (s && s != &s_ ? s : &s_symbol), 0, 0); /* LATER rethink */ } static void prependxy_list(t_prependxy *xy, t_symbol *s, int ac, t_atom *av) { prepend_doset(xy->xy_owner, &s_list, ac, av); /* LATER rethink */ } static void prependxy_anything(t_prependxy *xy, t_symbol *s, int ac, t_atom *av) { prepend_doset(xy->xy_owner, s, ac, av); } static void prepend_free(t_prepend *x) { if (x->x_message != x->x_messini) freebytes(x->x_message, x->x_size * sizeof(*x->x_message)); if (x->x_auxbuf) { loudbug_bug("prepend_free"); /* LATER rethink */ freebytes(x->x_auxbuf, x->x_auxsize * sizeof(*x->x_auxbuf)); } if (x->x_proxy) pd_free(x->x_proxy); } static void *prepend_new(t_symbol *s, int ac, t_atom *av) { t_prepend *x = (t_prepend *)pd_new(prepend_class); x->x_selector = 0; x->x_size = PREPEND_INISIZE; x->x_natoms = 0; x->x_message = x->x_messini; x->x_auxbuf = 0; x->x_entered = 0; if (ac) { x->x_proxy = 0; prepend_doset(x, 0, ac, av); } else { if (prepend_iscompatible) /* CHECKED in max an object without an outlet is created, and there is no warning when loading from a file. */ fittermax_warning(prepend_class, "creating an object without an argument"); x->x_proxy = pd_new(prependxy_class); ((t_prependxy *)x->x_proxy)->xy_owner = x; inlet_new((t_object *)x, x->x_proxy, 0, 0); } outlet_new((t_object *)x, &s_anything); return (x); } static void prepend_fitter(void) { prepend_iscompatible = fittermax_get(); } void prepend_setup(void) { prepend_class = class_new(gensym("prepend"), (t_newmethod)prepend_new, (t_method)prepend_free, sizeof(t_prepend), 0, A_GIMME, 0); class_addbang(prepend_class, prepend_bang); class_addfloat(prepend_class, prepend_float); class_addsymbol(prepend_class, prepend_symbol); class_addlist(prepend_class, prepend_list); class_addanything(prepend_class, prepend_anything); class_addmethod(prepend_class, (t_method)prepend_set, gensym("set"), A_GIMME, 0); prependxy_class = class_new(gensym("prepend"), 0, 0, sizeof(t_prependxy), CLASS_PD | CLASS_NOINLET, 0); class_addbang(prependxy_class, prependxy_bang); class_addfloat(prependxy_class, prependxy_float); class_addsymbol(prependxy_class, prependxy_symbol); class_addlist(prependxy_class, prependxy_list); class_addanything(prependxy_class, prependxy_anything); fitter_setup(prepend_class, prepend_fitter); }