diff options
Diffstat (limited to 'externals/gridflow/base/grid.c')
-rw-r--r-- | externals/gridflow/base/grid.c | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/externals/gridflow/base/grid.c b/externals/gridflow/base/grid.c new file mode 100644 index 00000000..da8d5532 --- /dev/null +++ b/externals/gridflow/base/grid.c @@ -0,0 +1,402 @@ +/* + $Id: grid.c 3941 2008-06-25 18:56:09Z matju $ + + GridFlow + Copyright (c) 2001-2008 by Mathieu Bouchard + + 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. + + See file ../COPYING for further informations on licensing terms. + + 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, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/time.h> +#include "../gridflow.h.fcs" +#include <ctype.h> + +//#define TRACE fprintf(stderr,"%s %s [%s:%d]\n",ARGS(parent),__PRETTY_FUNCTION__,__FILE__,__LINE__); +#define TRACE + +#define CHECK_TYPE(d) \ + if (NumberTypeE_type_of(&d)!=this->nt) RAISE("%s(%s): " \ + "type mismatch during transmission (got %s expecting %s)", \ + ARGS(parent), __PRETTY_FUNCTION__, \ + number_type_table[NumberTypeE_type_of(&d)].name, \ + number_type_table[this->nt].name); + +#define CHECK_BUSY1(s) \ + if (!dim) RAISE("%s: " #s " not busy",ARGS(parent)); + +#define CHECK_BUSY(s) \ + if (!dim) RAISE("%s: " #s " not busy (wanting to write %ld values)",ARGS(parent),(long)n); + +#define CHECK_ALIGN(d) \ + {int bytes = number_type_table[nt].size/8; \ + int align = ((long)(void*)d)%bytes; \ + if (align) {_L_;post("%s(%s): Alignment Warning: %p is not %d-aligned: %d", \ + ARGS(parent), __PRETTY_FUNCTION__, (void*)d,bytes,align);}} + +#define CHECK_ALIGN2(d,nt) \ + {int bytes = number_type_table[nt].size/8; \ + int align = ((long)(void*)d)%bytes; \ + if (align) {_L_;post("Alignment Warning: %p is not %d-aligned: %d", \ + (void*)d,bytes,align);}} + +// **************** Grid ****************************************** + +void Grid::init_from_list(int n, t_atom *aa, NumberTypeE nt) { + t_atom2 *a = (t_atom2 *)aa; + t_symbol *delim = gensym("#"); + for (int i=0; i<n; i++) { + if (a[i] == delim) { + int32 v[i]; + if (i!=0 && a[i-1].a_type==A_SYMBOL) nt=NumberTypeE_find(a[--i]); + for (int j=0; j<i; j++) v[j] = convert(a[j],(int32*)0); + init(new Dim(i,v),nt); + CHECK_ALIGN2(this->data,nt); + if (a[i] != delim) i++; + i++; a+=i; n-=i; + goto fill; + } + } + if (n!=0 && a[0].a_type==A_SYMBOL) { + nt = NumberTypeE_find(a[0]); + a++, n--; + } + init(new Dim(n),nt); + CHECK_ALIGN2(this->data,nt); + fill: + int nn = dim->prod(); + n = min(n,nn); +#define FOO(T) { \ + T *p = (T *)*this; \ + if (n==0) CLEAR(p,nn); \ + else { \ + for (int i=0; i<n; i++) p[i] = a[i]; \ + for (int i=n; i<nn; i+=n) COPY(p+i,p,min(n,nn-i)); }} + TYPESWITCH(nt,FOO,) +#undef FOO +} + +void Grid::init_from_atom(const t_atom &x) { + if (x.a_type==A_LIST) { + t_binbuf *b = (t_binbuf *)x.a_gpointer; + init_from_list(binbuf_getnatom(b),binbuf_getvec(b)); + } else if (x.a_type==A_FLOAT) { + init(new Dim(),int32_e); + CHECK_ALIGN2(this->data,nt); + ((int32 *)*this)[0] = (int32)x.a_float; + } else RAISE("can't convert to grid"); +} + +// **************** GridInlet ************************************* + +// must be set before the end of GRID_BEGIN phase, and so cannot be changed +// afterwards. This is to allow some optimisations. Anyway there is no good reason +// why this would be changed afterwards. +void GridInlet::set_factor(long factor) { + if(!dim) RAISE("huh?"); + if(factor<=0) RAISE("%s: factor=%d should be >= 1",ARGS(parent),factor); + int i; + for (i=0; i<=dim->n; i++) if (dim->prod(i)==factor) break; + if (i>dim->n) RAISE("%s: set_factor: expecting dim->prod(i) for some i, " + "but factor=%ld and dim=%s",ARGS(parent),factor,dim->to_s()); + if (factor > 1) { + buf=new Grid(new Dim(factor), nt); + bufi=0; + } else { + buf=0; + } +} + +void GridInlet::set_chunk(long whichdim) { + long n = dim->prod(whichdim); + if (n) set_factor(n); +} + +bool GridInlet::supports_type(NumberTypeE nt) { +#define FOO(T) return !! gh->flow_##T; + TYPESWITCH(nt,FOO,return false) +#undef FOO +} + +void GridInlet::begin(int argc, t_atom2 *argv) {TRACE; + GridOutlet *back_out = (GridOutlet *) (void *)argv[0]; + nt = back_out->nt; + if (dim) RAISE("%s: grid inlet conflict; aborting %s in favour of %s, index %ld of %ld", + ARGS(parent), ARGS(sender), ARGS(back_out->parent), (long)dex, (long)dim->prod()); + sender = back_out->parent; + if ((int)nt<0 || (int)nt>=(int)number_type_table_end) RAISE("%s: inlet: unknown number type",ARGS(parent)); + if (!supports_type(nt)) RAISE("%s: number type %s not supported here", ARGS(parent), number_type_table[nt].name); + P<Dim> dim = this->dim = back_out->dim; + dex=0; + buf=0; + try { +#define FOO(T) gh->flow(this,-1,(T *)0); break; + TYPESWITCH(this->nt,FOO,) +#undef FOO + } catch (Barf &barf) { + this->dim = 0; // hack + throw; + } + this->dim = dim; + back_out->callback(this); +} + +#define CATCH_IT catch (Barf &slimy) {post("error during flow: %s",slimy.text);} + +template <class T> void GridInlet::flow(int mode, long n, T *data) {TRACE; + CHECK_BUSY(inlet); + CHECK_TYPE(*data); + CHECK_ALIGN(data); + if (this->mode==0) {dex += n; return;} // ignore data + if (n==0) return; // no data + switch(mode) { + case 4:{ + long d = dex + bufi; + if (d+n > dim->prod()) { + post("grid input overflow: %d of %d from [%s] to [%s]", d+n, dim->prod(), ARGS(sender), 0); + n = dim->prod() - d; + if (n<=0) return; + } + int bufn = factor(); + if (buf && bufi) { + T *bufd = *buf; + long k = min((long)n,bufn-bufi); + COPY(bufd+bufi,data,k); + bufi+=k; data+=k; n-=k; + if (bufi==bufn) { + long newdex = dex+bufn; + if (this->mode==6) { + T *data2 = NEWBUF(T,bufn); + COPY(data2,bufd,bufn); + CHECK_ALIGN(data2); + try {gh->flow(this,bufn,data2);} CATCH_IT; + } else { + CHECK_ALIGN(bufd); + try {gh->flow(this,bufn,bufd);} CATCH_IT; + } + dex = newdex; + bufi = 0; + } + } + int m = (n/bufn)*bufn; + if (m) { + int newdex = dex + m; + if (this->mode==6) { + T *data2 = NEWBUF(T,m); + COPY(data2,data,m); + CHECK_ALIGN(data2); + try {gh->flow(this,m,data2);} CATCH_IT; + } else { + try {gh->flow(this,m,data);} CATCH_IT; + } + dex = newdex; + } + data += m; + n -= m; + if (buf && n>0) COPY((T *)*buf+bufi,data,n), bufi+=n; + }break; + case 6:{ + int newdex = dex + n; + try {gh->flow(this,n,data);} CATCH_IT; + if (this->mode==4) DELBUF(data); + dex = newdex; + }break; + case 0: break; // ignore data + default: RAISE("%s: unknown inlet mode",ARGS(parent)); + } +} + +void GridInlet::finish() {TRACE; + if (!dim) RAISE("%s: inlet not busy",ARGS(parent)); + if (dim->prod() != dex) { + post("incomplete grid: %d of %d from [%s] to [%s]", + dex, dim->prod(), ARGS(sender), ARGS(parent)); + } +#define FOO(T) try {gh->flow(this,-2,(T *)0);} CATCH_IT; + TYPESWITCH(nt,FOO,) +#undef FOO + dim=0; + buf=0; + dex=0; +} + +template <class T> void GridInlet::from_grid2(Grid *g, T foo) {TRACE; + nt = g->nt; + dim = g->dim; + int n = g->dim->prod(); + gh->flow(this,-1,(T *)0); + if (n>0 && this->mode!=0) { + T *data = (T *)*g; + CHECK_ALIGN(data); + int size = g->dim->prod(); + if (this->mode==6) { + T *d = data; + data = NEWBUF(T,size); + COPY(data,d,size); + CHECK_ALIGN(data); + try {gh->flow(this,n,data);} CATCH_IT; + } else { + //int ntsz = number_type_table[nt].size; + int m = GridOutlet::MAX_PACKET_SIZE/*/ntsz*//factor(); + if (!m) m++; + m *= factor(); + while (n) { + if (m>n) m=n; + CHECK_ALIGN(data); + try {gh->flow(this,m,data);} CATCH_IT; + data+=m; n-=m; dex+=m; + } + } + } + try {gh->flow(this,-2,(T *)0);} CATCH_IT; + //!@#$ add error handling. + dim = 0; + dex = 0; +} + +void GridInlet::from_grid(Grid *g) {TRACE; + if (!supports_type(g->nt)) + RAISE("%s: number type %s not supported here", ARGS(parent), number_type_table[g->nt].name); +#define FOO(T) from_grid2(g,(T)0); + TYPESWITCH(g->nt,FOO,) +#undef FOO +} + +/* **************** GridOutlet ************************************ */ + +GridOutlet::GridOutlet(FObject *parent_, int woutlet, P<Dim> dim_, NumberTypeE nt_) { + parent=parent_; dim=dim_; nt=nt_; dex=0; frozen=false; bufi=0; buf=0; + begin(woutlet,dim,nt); +} + +//void GridOutlet::alloc_buf() { +//} + +void GridOutlet::begin(int woutlet, P<Dim> dim, NumberTypeE nt) {TRACE; + this->nt = nt; + this->dim = dim; + t_atom a[3]; + SETPOINTER(a,(t_gpointer *)this); // hack + outlet_anything(parent->bself->outlets[woutlet],bsym._grid,1,a); + frozen=true; + if (!dim->prod()) {finish(); return;} + int32 lcm_factor = 1; + for (uint32 i=0; i<inlets.size(); i++) lcm_factor = lcm(lcm_factor,inlets[i]->factor()); + //size_t ntsz = number_type_table[nt].size; + // biggest packet size divisible by lcm_factor + int32 v = (MAX_PACKET_SIZE/lcm_factor)*lcm_factor; + if (v==0) v=MAX_PACKET_SIZE; // factor too big. don't have a choice. + buf=new Grid(new Dim(v),nt); +} + +// send modifies dex; send_direct doesn't +template <class T> +void GridOutlet::send_direct(long n, T *data) {TRACE; + CHECK_BUSY(outlet); CHECK_TYPE(*data); CHECK_ALIGN(data); + for (; n>0; ) { + long pn = n;//min((long)n,MAX_PACKET_SIZE); + for (uint32 i=0; i<inlets.size(); i++) try {inlets[i]->flow(4,pn,data);} CATCH_IT; + data+=pn, n-=pn; + } +} + +void GridOutlet::flush() {TRACE; + if (!bufi) return; +#define FOO(T) send_direct(bufi,(T *)*buf); + TYPESWITCH(buf->nt,FOO,) +#undef FOO + bufi = 0; +} + +template <class T, class S> +static void convert_number_type(int n, T *out, S *in) { + for (int i=0; i<n; i++) out[i]=(T)in[i]; +} + +//!@#$ buffering in outlet still is 8x faster...? +//!@#$ should use BitPacking for conversion...? +// send modifies dex; send_direct doesn't +template <class T> +void GridOutlet::send(long n, T *data) {TRACE; + if (!n) return; + CHECK_BUSY(outlet); CHECK_ALIGN(data); + if (NumberTypeE_type_of(data)!=nt) { + int bs = MAX_PACKET_SIZE; +#define FOO(T) { \ + T data2[bs]; \ + for (;n>=bs;n-=bs,data+=bs) {convert_number_type(bs,data2,data); send(bs,data2);} \ + convert_number_type(n,data2,data); \ + send(n,data2); } + TYPESWITCH(nt,FOO,) +#undef FOO + } else { + dex += n; + if (n > MIN_PACKET_SIZE || bufi + n > MAX_PACKET_SIZE) flush(); + if (n > MIN_PACKET_SIZE) { + send_direct(n,data); + } else { + COPY((T *)*buf+bufi,data,n); + bufi += n; + } + if (dex==dim->prod()) finish(); + } +} + +template <class T> +void GridOutlet::give(long n, T *data) {TRACE; + CHECK_BUSY(outlet); + CHECK_ALIGN(data); + if (NumberTypeE_type_of(data)!=nt) { + send(n,data); + DELBUF(data); + return; + } + if (inlets.size()==1 && inlets[0]->mode == 6) { + // this is the copyless buffer passing + flush(); + try {inlets[0]->flow(6,n,data);} CATCH_IT; + dex += n; + } else { + flush(); + send_direct(n,data); + dex += n; + DELBUF(data); + } + if (dex==dim->prod()) finish(); +} + +void GridOutlet::callback(GridInlet *in) {TRACE; + CHECK_BUSY1(outlet); + if (!(in->mode==6 || in->mode==4 || in->mode==0)) RAISE("mode error"); + inlets.push_back(in); +} + +// never call this. this is a hack to make some things work. +// i'm trying to circumvent either a bug in the compiler or i don't have a clue. :-( +void make_gimmick () { + GridOutlet foo(0,0,0); +#define FOO(S) foo.give(0,(S *)0); +EACH_NUMBER_TYPE(FOO) +#undef FOO + //foo.send(0,(float64 *)0); // this doesn't work, when trying to fix the new link problem in --lite mode. +} + |