/* $Id: grid.h,v 1.2 2006-03-15 04:37:08 matju Exp $ GridFlow Copyright (c) 2001,2002,2003,2004,2005 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. */ #ifndef __GF_GRID_H #define __GF_GRID_H // current version number as string literal #define GF_VERSION "0.8.1" #define GF_COMPILE_TIME __DATE__ ", " __TIME__ #include #include #include #include #include #include #include #ifdef __APPLE__ static inline void *memalign (size_t a, size_t n) {return malloc(n);} #else #include #endif extern "C" { #include #include #include }; #ifndef RUBY_H #error "Can't do anything without ruby.h" #endif #include "../config.h" #ifdef __WIN32__ #define INT winINT #define random rand #undef send #undef close #define sigjmp_buf jmp_buf #define siglongjmp longjmp #endif #define BUG(s,args...) {fprintf(stderr,s "\nat: %s\n",args,__PRETTY_FUNCTION__); ::raise(11);} #define L gfpost("%s:%d in %s",__FILE__,__LINE__,__PRETTY_FUNCTION__); #ifdef IS_BRIDGE #define RAISE(args...) rb_raise(rb_eArgError,args) #else #define RAISE(args...) rb_raise0(__FILE__,__LINE__,__PRETTY_FUNCTION__,rb_eArgError,args) #endif // avoid ruby warning #ifndef rb_enable_super #define rb_enable_super(a,b) \ if (RUBY_RELEASE_CODE < 20030716) rb_enable_super(a,b) #endif typedef VALUE Ruby; /* undocumented function from Ruby that is one thing we need to fix a very elusive bug that manifests itself when embedding ruby inside a plugin of another app. This exists for all versions of Ruby up to now, and I don't know when it gets fixed. */ extern "C" void Init_stack(VALUE *addr); extern "C" { void rb_raise0( const char *file, int line, const char *func, VALUE exc, const char *fmt, ...) __attribute__ ((noreturn)); }; #define SI(_sym_) (rb_intern(#_sym_)) #define SYM(_sym_) (ID2SYM(SI(_sym_))) #define DGS(_class_) \ _class_ *self; \ Data_Get_Struct(rself,_class_,self); \ self->check_magic(); // returns the size of a statically defined array // warning: does not work with STACK_ARRAY() #define COUNT(_array_) ((int)(sizeof(_array_) / sizeof((_array_)[0]))) static inline long rb_str_len(Ruby s) {return RSTRING(s)->len;} static inline char *rb_str_ptr(Ruby s) {return RSTRING(s)->ptr;} static inline long rb_ary_len(Ruby s) {return RARRAY(s)->len;} static inline Ruby *rb_ary_ptr(Ruby s) {return RARRAY(s)->ptr;} static inline const char *rb_sym_name(Ruby sym) {return rb_id2name(SYM2ID(sym));} #define rb_str_pt(s,t) Pt((t*)rb_str_ptr(s),rb_str_len(s)) #define IEVAL(_self_,s) rb_funcall(_self_,SI(instance_eval),1,rb_str_new2(s)) #define EVAL(s) rb_eval_string(s) #define rassert(_p_) if (!(_p_)) RAISE(#_p_); // because of older versions of Ruby (1.6.?) #define rb_obj_class(o) rb_funcall((o),SI(class),0) #define WATCH(n,ar) { \ char foo[16*1024], *p=foo; p += sprintf(p,"%s: ",#ar); \ for (int q=0; q #undef assert #define assert(_expr_) \ if (!(_expr_)) { \ fprintf(stderr, "%s:%d: assertion failed: %s is false\n", \ __FILE__, __LINE__, #_expr_); \ ::abort(); } // disabling assertion checking? #ifndef HAVE_DEBUG #undef assert #define assert(_foo_) #endif #ifdef HAVE_TSC_PROFILING #define HAVE_PROFILING #endif #ifdef HAVE_PROFILING #define PROF(_self_) for (GFStackMarker gf_marker(_self_);gf_marker.once();) #else #define PROF(_self_) #endif // HAVE_PROFILING static inline Ruby PTR2FIX (const void *ptr) { long p = (long)ptr; if ((p&3)!=0) BUG("unaligned pointer: %p\n",ptr); return LONG2NUM(p>>2); } #define FIX2PTR(type,ruby) ((type *)(TO(long,ruby)<<2)) //**************************************************************** /* int32 was long before, now int, because of amd64 */ typedef char int8; typedef unsigned char uint8; typedef short int16; typedef unsigned short uint16; typedef int int32; typedef unsigned int uint32; typedef long long int64;typedef unsigned long long uint64; typedef float float32; typedef double float64; // three-way comparison (T is assumed Comparable) template static inline T cmp(T a, T b) { return ab; } // a remainder function such that div2(a,b)*b+mod(a,b) = a and for which // mod(a,b) is in [0;b) or (b;0]. in contrast to C-language builtin a%b, // this one has uniform behaviour around zero. static inline int mod(int a, int b) { int c=a%b; c+=b&-(c&&(a<0)^(b<0)); return c;} // counterpart of mod(a,b), just like a/b and a%b are counterparts static inline int div2(int a, int b) { int c=a<0; return (a/b)-(c&&!!(a%b));} static inline int32 gf_abs( int32 a) { return a>0?a:-a; } static inline int64 gf_abs( int64 a) { return a>0?a:-a; } static inline float32 gf_abs(float32 a) { return fabs(a); } static inline float64 gf_abs(float64 a) { return fabs(a); } // integer powers in log(b) time. T is assumed Integer template static inline T ipow(T a, T b) { for(T r=1;;) {if (b&1) r*=a; b>>=1; if (!b) return r; a*=a;} } // kludge static inline float32 ipow(float32 a, float32 b) { return pow(a,b); } static inline float64 ipow(float64 a, float64 b) { return pow(a,b); } #undef min #undef max // minimum/maximum functions; T is assumed to be Comparable template static inline T min(T a, T b) { return a static inline T max(T a, T b) { return a>b?a:b; } //template inline T min(T a, T b) { T c = (a-b)>>31; return (a&c)|(b&~c); } // greatest common divisor, by euclid's algorithm // this runs in log(a+b) number operations template static T gcd (T a, T b) { while (b) {T c=mod(a,b); a=b; b=c;} return a; } // greatest common divisor, the binary algorithm. haven't tried yet. template static T gcd2 (T a, T b) { int s=0; while ((a|b)&1==0) { a>>=1; b>>=1; s++; } while (a) { if (a&1==0) a>>=1; else if (b&1==0) b>>=1; else {T t=abs(a-b); if (a static inline T lcm (T a, T b) {return a*b/gcd(a,b);} // returns the position (0..31) of highest bit set in a word, or 0 if none. #define Z(N) if ((x>>N)&(((typeof(x))1<>=N; i+=N; } static int highest_bit(uint8 x) {int i=0; Z(4)Z(2)Z(1)return i;} static int highest_bit(uint16 x) {int i=0; Z(8)Z(4)Z(2)Z(1)return i;} static int highest_bit(uint32 x) {int i=0; Z(16)Z(8)Z(4)Z(2)Z(1)return i;} static int highest_bit(uint64 x) {int i=0;Z(32)Z(16)Z(8)Z(4)Z(2)Z(1)return i;} #undef Z // returns the position (0..31) of lowest bit set in a word, or 0 if none. template static int lowest_bit(T n) { return highest_bit((~n+1)&n); } static double drand() { return 1.0*rand()/(RAND_MAX+1.0); } // is little-endian static inline bool is_le() {int x=1; return ((char *)&x)[0];} #if defined(HAVE_PENTIUM) static inline uint64 rdtsc() { uint64 x; __asm__ volatile (".byte 0x0f, 0x31":"=A"(x)); return x; } #elif defined(HAVE_PPC) static inline uint64 rdtsc() { #include #include /* see AbsoluteToNanoseconds(), Microseconds() http://www.simdtech.org/apps/group_public/email/altivec/msg01956.html and mach_absolute_time() and mach_timebase_info(&info), then ns=(time*info.numer)/info.denom; and the timebase_info is constant (double)mach_absolute_time*gTimeBaseToNS*/ return 0; } #else static inline uint64 rdtsc() {return 0;} #endif #ifdef HAVE_LITE #define EACH_INT_TYPE(MACRO) MACRO(uint8) MACRO(int16) MACRO(int32) #define EACH_FLOAT_TYPE(MACRO) #else #define EACH_INT_TYPE(MACRO) MACRO(uint8) MACRO(int16) MACRO(int32) MACRO(int64) #define EACH_FLOAT_TYPE(MACRO) MACRO(float32) MACRO(float64) #endif #define EACH_NUMBER_TYPE(MACRO) EACH_INT_TYPE(MACRO) EACH_FLOAT_TYPE(MACRO) MACRO(ruby) // note: loop unrolling macros assume N!=0 // btw this may cause alignment problems when 8 does not divide N #define UNROLL_8(MACRO,N,PTR,ARGS...) \ int n__=(-N)&7; PTR-=n__; N+=n__; \ switch (n__) { start: \ case 0:MACRO(0); case 1:MACRO(1); case 2:MACRO(2); case 3:MACRO(3); \ case 4:MACRO(4); case 5:MACRO(5); case 6:MACRO(6); case 7:MACRO(7); \ PTR+=8; N-=8; ARGS; if (N) goto start; } #define UNROLL_4(MACRO,N,PTR,ARGS...) \ int n__=(-N)&3; PTR-=n__; N+=n__; \ switch (n__) { start: \ case 0:MACRO(0); case 1:MACRO(1); case 2:MACRO(2); case 3:MACRO(3); \ PTR+=4; N-=4; ARGS; if (N) goto start; } //**************************************************************** // my own little Ruby <-> C++ layer //struct Arg { Ruby a; }; //struct ArgList { int n; Pt v; }; static inline bool INTEGER_P(Ruby x) {return FIXNUM_P(x)||TYPE(x)==T_BIGNUM;} static inline bool FLOAT_P(Ruby x) {return TYPE(x)==T_FLOAT;} #define INT(x) TO(int32,x) #define TO(t,x) convert(x,(t*)0) // not using NUM2INT because Ruby can convert Symbol to int // (by compatibility with Ruby 1.4) static inline int32 convert(Ruby x, int32 *foo) { if (INTEGER_P(x)) return NUM2INT(x); if (FLOAT_P(x)) return NUM2INT(rb_funcall(x,SI(round),0)); RAISE("expected Integer or Float (got %s)", rb_str_ptr(rb_funcall(x,SI(inspect),0))); } static int16 convert(Ruby x, int16 *foo) { int v = INT(x); if (v<-0x8000 || v>=0x8000) RAISE("value %d is out of range",v); return v;} static uint16 convert(Ruby x, uint16 *foo) { int v = INT(x); if (v<0 || v>=0x10000) RAISE("value %d is out of range",v); return v;} static bool convert(Ruby x, bool *foo) { if (x==Qtrue) return true; if (x==Qfalse) return false; switch (TYPE(x)) { case T_FIXNUM: case T_BIGNUM: case T_FLOAT: return !!INT(x); default: RAISE("can't convert to bool"); } } #ifdef HAVE_GCC64 static uint64 convert(Ruby val, uint64 *foo) { return NUM2ULONG(val); } static int64 convert(Ruby val, int64 *foo) { return NUM2ULONG(val); } static Ruby gf_ull2num(uint64 val) { return ULONG2NUM(val); } static Ruby gf_ll2num(uint64 val) { return LONG2NUM(val); } #else static uint64 convert(Ruby val, uint64 *foo) { if (FIXNUM_P(val)) return (uint64)FIX2LONG(val); if (TYPE(val)!=T_BIGNUM) RAISE("type error"); uint64 v = (uint64)NUM2UINT(rb_funcall(val,SI(>>),1,INT2FIX(32))) << 32; return v + NUM2UINT(rb_funcall(val,SI(&),1,UINT2NUM(0xffffffff)));} static int64 convert(Ruby val, int64 *foo) { if (FIXNUM_P(val)) return (int64)FIX2LONG(val); if (TYPE(val)!=T_BIGNUM) RAISE("type error"); int64 v = (int64)NUM2INT(rb_funcall(val,SI(>>),1,INT2FIX(32))) << 32; return v + NUM2UINT(rb_funcall(val,SI(&),1,UINT2NUM(0xffffffff)));} static Ruby gf_ull2num(uint64 val) { Ruby x = rb_funcall(UINT2NUM((uint32)(val>>32)),SI(<<),1,INT2FIX(32)); return rb_funcall(x,SI(+),1,UINT2NUM((uint32)val));} static Ruby gf_ll2num(int64 val) { Ruby x = rb_funcall( INT2NUM(( int32)(val>>32)),SI(<<),1,INT2FIX(32)); return rb_funcall(x,SI(+),1,UINT2NUM((uint32)val));} #endif static long convert(Ruby x, long *foo) { return sizeof(long)==sizeof(int32) ? convert(x,(int32 *)0) : convert(x,(int64 *)0); } static float64 convert(Ruby x, float64 *foo) { if (INTEGER_P(x)) return INT(x); if (TYPE(x)!=T_FLOAT) RAISE("not a Float"); return ((RFloat*)x)->value;} static float32 convert(Ruby x, float32 *foo) { return (float32) convert(x,(float64 *)0);} typedef Ruby Symbol, Array, String, Integer; static Ruby convert(Ruby x, Ruby *bogus) { return x; } typedef Ruby (*RMethod)(...); /* !@#$ fishy */ #define BUILTIN_SYMBOLS(MACRO) \ MACRO(_grid,"grid") MACRO(_bang,"bang") MACRO(_float,"float") \ MACRO(_list,"list") MACRO(_sharp,"#") \ MACRO(iv_outlets,"@outlets") \ MACRO(iv_ninlets,"@ninlets") \ MACRO(iv_noutlets,"@noutlets") extern struct BuiltinSymbols { #define FOO(_sym_,_str_) Ruby _sym_; BUILTIN_SYMBOLS(FOO) #undef FOO } bsym; typedef struct R { VALUE r; R() {r=Qnil;} R(int x) {r=INT2NUM(x);} R(unsigned x) {r=UINT2NUM(x);} R(long x) {r=LONG2NUM(x);} R(unsigned long x) {r=ULONG2NUM(x);} R(double x) {r=rb_float_new(x);} R( int64 x) {r= gf_ll2num(x);} R(uint64 x) {r=gf_ull2num(x);} operator bool() {return !!INT2NUM(r);} operator uint8 () {return INT2NUM(r);} operator int16 () {return INT2NUM(r);} operator int32 () {return INT2NUM(r);} operator int64 () {return convert(r,(int64*)0);} operator float32 () {return convert(r,(float32*)0);} operator float64 () {return convert(r,(float64*)0);} #define FOO(As,Op) \ R &operator As (int x) {r=rb_funcall(r, SI(Op),1,INT2NUM(x)); return *this;} FOO(+=,+) FOO(-=,-) FOO(*=,*) FOO(/=,/) FOO(%=,%) FOO(&=,&) FOO(|=,|) FOO(^=,^) FOO(<<=,<<) FOO(>>=,>>) #undef FOO // bool operator == (int x) {return rb_funcall(r,SI(==),1,INT2NUM(x));} #define FOO(Op) \ R operator Op (R x) {return rb_funcall(r,SI(Op),1,x.r);} \ R operator Op (int x) {return rb_funcall(r,SI(Op),1,INT2NUM(x));} FOO(+) FOO(-) FOO(*) FOO(/) FOO(%) FOO(&) FOO(|) FOO(^) FOO(<<) FOO(>>) FOO(<) FOO(>) FOO(<=) FOO(>=) FOO(==) FOO(!=) #undef FOO static R value(VALUE r) {R x; x.r=r; return x;} } ruby; static R operator -(int a, R b) {return rb_funcall(a,SI(Op),1,INT2NUM(b.r));} static inline R ipow(R a, R b) {return R::value(rb_funcall(a.r,SI(**),1,b.r));} static inline R gf_abs(R a) { return R::value(rb_funcall(a.r,SI(abs),0)); } static inline R cmp(R a, R b) { return R::value(rb_funcall(a.r,SI(<=>),1,b.r));} //**************************************************************** // hook into pointer manipulation. will help find memory corruption bugs. template class Pt { typedef ptrdiff_t /* and not int nor long */ Z; public: T *p; #ifdef HAVE_DEBUG T *start; Z n; Pt() : p(0), start(0), n(0) {} Pt(T *q, Z _n) : p(q), start(q), n(_n) {} Pt(T *q, Z _n, T *_start) : p(q), start(_start), n(_n) {} #else Pt() : p(0) {} Pt(T *q, Z _n, T *_start=0) : p(q) {} #endif T &operator *() { return *p; } Pt operator+=(Z i) { p+=i; return *this; } Pt operator-=(Z i) { p-=i; return *this; } Pt operator++( ) { p++; return *this; } Pt operator--( ) { p--; return *this; } Pt operator++(int) { Pt f(*this); ++*this; return f; } Pt operator--(int) { Pt f(*this); --*this; return f; } T &operator[](Z i) { #ifdef HAVE_DEBUG_HARDER if (!(p+i>=start && p+i=start && p=start && q<=start+n)) BUG("BUFFER OVERFLOW: 0x%08lx...0x%08lx is not all inside 0x%08lx...0x%08lx", (long)p,(long)q,start,(long)(start+n)); #endif } Z operator-(Pt x) { return p-x.p; } operator bool () { return (bool )p; } operator void *() { return (void *)p; } operator int8 *() { return (int8 *)p; } /* how do i make typecast operators that are not also default conversions??? */ /* this should be found so i can clean up the following: */ #define FOO(S) operator S *() { return (S *)p; } EACH_NUMBER_TYPE(FOO) #undef FOO #ifdef HAVE_DEBUG #define FOO(S) operator Pt() { return Pt((S *)p,n*sizeof(T)/1,(S *)start); } EACH_NUMBER_TYPE(FOO) #undef FOO #else #define FOO(S) operator Pt() { return Pt((S *)p,0); } EACH_NUMBER_TYPE(FOO) #undef FOO #endif /* end 0.8 (TESTING) */ #ifdef HAVE_DEBUG template Pt operator+(U x) { return Pt(p+x,n,start); } template Pt operator-(U x) { return Pt(p-x,n,start); } #else template Pt operator+(U x) { return Pt(p+x,0); } template Pt operator-(U x) { return Pt(p-x,0); } #endif }; //template class P : Pt {}; //a reference counting pointer class template class P { public: #define INCR if (p) p->refcount++; #define DECR if (p) {p->refcount--; if (!p->refcount) delete p;} T *p; P() : p(0) {} P(T *_p) { p = _p; INCR; } P(const P &_p) { p = _p.p; INCR; } P &operator =(T * _p) { DECR; p=_p; INCR; return *this; } P &operator =(P _p) { DECR; p=_p.p; INCR; return *this; } bool operator ==(P _p) { return p == _p.p; } bool operator !=(P _p) { return p != _p.p; } ~P() { DECR; } bool operator !(){ return !p; } operator bool() { return !!p; } T &operator *() { return *p; } T *operator ->() { return p; } //#undef INCR //#undef DECR }; #ifndef IS_BRIDGE extern "C" void *gfmalloc(size_t n); extern "C" void gffree(void *p); // note that C++ (GCC 3.4) now refuses the :: prefix so i removed it in the 4 following lines: inline void *operator new (size_t n) { return gfmalloc(n); } inline void *operator new[] (size_t n) { return gfmalloc(n); } inline void operator delete (void *p) { gffree(p); } inline void operator delete[] (void *p) { gffree(p); } #endif #define STACK_ARRAY(T,V,N) T V##_foo[N]; Pt V(V##_foo,N); #define ARRAY_NEW(T,N) (Pt((T *)new T[N],N)) void gfmemcopy(uint8 *out, const uint8 *in, int n); template inline void COPY(Pt dest, Pt src, int n) { src.will_use(n); dest.will_use(n); gfmemcopy((uint8*)dest,(const uint8*)src,n*sizeof(T)); } template inline void CLEAR(Pt dest, int n) { dest.will_use(n); memset(dest,0,n*sizeof(T)); } template static void memswap (Pt a, Pt b, int n) { STACK_ARRAY(T,c,n); COPY(c,a,n); COPY(a,b,n); COPY(b,c,n); } //**************************************************************** // CObject is the base class for C++ classes that get exported to Ruby. // BTW: It's quite convenient to have virtual-methods in the base class // because otherwise the vtable* isn't at the beginning of the object // and that's pretty confusing to a lot of people, especially when simple // casting causes a pointer to change its value. struct CObject { #define OBJECT_MAGIC 1618033989 int32 magic; int32 refcount; Ruby rself; // point to Ruby peer CObject() : magic(OBJECT_MAGIC), refcount(0), rself(0) {} void check_magic () { if (magic != OBJECT_MAGIC) { fprintf(stderr,"Object memory corruption! (ask the debugger)\n"); for (int i=-2; i<=2; i++) fprintf(stderr,"this[%d]=0x%08x\n",i,((int*)this)[i]); raise(11); } } virtual ~CObject() { magic = 0xDEADBEEF; } virtual void mark() {} // not used for now }; void CObject_free (void *); // you shouldn't use MethodDecl directly (used by source_filter.rb) struct MethodDecl { const char *selector; RMethod method; }; void define_many_methods(Ruby rself, int n, MethodDecl *methods); extern Ruby mGridFlow, cFObject, cGridObject, cFormat; //**************************************************************** // a Dim is a list of dimensions that describe the shape of a grid \class Dim < CObject struct Dim : CObject { static const int MAX_DIM=16; // maximum number of dimensions in a grid int n; Pt v; // safe pointer int32 v2[MAX_DIM]; // real stuff void check(); // test invariants Dim(int n, Pt v) { this->v = Pt(v2,MAX_DIM); this->n = n; COPY(this->v,v,n); check();} Dim(int n, int32 *v) { this->v = Pt(v2,MAX_DIM); this->n = n; COPY(this->v,Pt(v,n),n); check();} Dim() {v=Pt(v2,MAX_DIM); n=0; check();} Dim(int a) {v=Pt(v2,MAX_DIM); n=1;v[0]=a; check();} Dim(int a,int b) {v=Pt(v2,MAX_DIM); n=2;v[0]=a;v[1]=b; check();} Dim(int a,int b,int c){v=Pt(v2,MAX_DIM); n=3;v[0]=a;v[1]=b;v[2]=c;check();} int count() {return n;} int get(int i) { return v[i]; } int32 prod(int start=0, int end=-1) { if (end<0) end+=n; int32 tot=1; for (int i=start; i<=end; i++) tot *= v[i]; return tot; } char *to_s(); bool equal(P o) { if (n!=o->n) return false; for (int i=0; iv[i]) return false; return true; } }; \end class Dim //**************************************************************** //BitPacking objects encapsulate optimised loops of conversion struct BitPacking; // those are the types of the optimised loops of conversion // inputs are const struct Packer { #define FOO(S) void (*as_##S)(BitPacking *self, int n, Pt in, Pt out); EACH_INT_TYPE(FOO) #undef FOO }; struct Unpacker { #define FOO(S) void (*as_##S)(BitPacking *self, int n, Pt in, Pt out); EACH_INT_TYPE(FOO) #undef FOO }; \class BitPacking < CObject struct BitPacking : CObject { Packer *packer; Unpacker *unpacker; unsigned int endian; // 0=big, 1=little, 2=same, 3=different int bytes; int size; uint32 mask[4]; BitPacking(){::abort();} // don't call, but don't remove. sorry. BitPacking(int endian, int bytes, int size, uint32 *mask, Packer *packer=0, Unpacker *unpacker=0); bool is_le(); bool eq(BitPacking *o); \decl void initialize(Ruby foo1, Ruby foo2, Ruby foo3); \decl String pack2(String ins, String outs=Qnil); \decl String unpack2(String ins, String outs=Qnil); \decl String to_s(); // main entrances to Packers/Unpackers template void pack( int n, Pt in, Pt out); template void unpack(int n, Pt in, Pt out); }; \end class int high_bit(uint32 n); int low_bit(uint32 n); void swap32 (int n, Pt data); void swap16 (int n, Pt data); #define NT_UNSIGNED (1<<0) #define NT_FLOAT (1<<1) #define NT_UNIMPL (1<<2) #define NUMBER_TYPE_LIMITS(T,a,b,c) \ inline T nt_smallest(T *bogus) {return a;} \ inline T nt_greatest(T *bogus) {return b;} \ inline T nt_all_ones(T *bogus) {return c;} NUMBER_TYPE_LIMITS( uint8,0,255,255) NUMBER_TYPE_LIMITS( int16,-0x8000,0x7fff,-1) NUMBER_TYPE_LIMITS( int32,-0x80000000,0x7fffffff,-1) NUMBER_TYPE_LIMITS( int64,-0x8000000000000000LL,0x7fffffffffffffffLL,-1) NUMBER_TYPE_LIMITS(float32,-HUGE_VAL,+HUGE_VAL,(RAISE("all_ones"),0)) NUMBER_TYPE_LIMITS(float64,-HUGE_VAL,+HUGE_VAL,(RAISE("all_ones"),0)) NUMBER_TYPE_LIMITS( ruby,ruby(-HUGE_VAL),ruby(+HUGE_VAL),(RAISE("all_ones"),0)) #ifdef HAVE_LITE #define NT_NOTLITE NT_UNIMPL #define NONLITE(x...) #else #define NT_NOTLITE 0 #define NONLITE(x...) x #endif #define NUMBER_TYPES(MACRO) \ MACRO( uint8, 8,NT_UNSIGNED, "u8,b") MACRO( int8, 8,NT_UNIMPL, "i8") \ MACRO( uint16,16,NT_UNSIGNED|NT_UNIMPL, "u16") MACRO(int16,16,0, "i16,s") \ MACRO( uint32,32,NT_UNSIGNED|NT_UNIMPL, "u32") MACRO(int32,32,0, "i32,i") \ MACRO( uint64,64,NT_UNSIGNED|NT_UNIMPL, "u64") MACRO(int64,64,NT_NOTLITE, "i64,l") \ MACRO(float32,32,NT_NOTLITE|NT_FLOAT, "f32,f") \ MACRO(float64,64,NT_NOTLITE|NT_FLOAT, "f64,d") \ MACRO( ruby,sizeof(long),NT_NOTLITE,"r") enum NumberTypeE { #define FOO(_sym_,args...) _sym_##_e, NUMBER_TYPES(FOO) #undef FOO number_type_table_end }; #define FOO(_type_) \ inline NumberTypeE NumberTypeE_type_of(_type_ &x) { \ return _type_##_e; } EACH_NUMBER_TYPE(FOO) #undef FOO \class NumberType < CObject struct NumberType : CObject { Ruby /*Symbol*/ sym; const char *name; int size; int flags; const char *aliases; NumberTypeE index; NumberType(const char *name_, int size_, int flags_, const char *aliases_) : name(name_), size(size_), flags(flags_), aliases(aliases_) {} \decl Symbol sym_m(); \decl int size_m(); \decl int flags_m(); \decl int index_m(); }; \end class NumberTypeE NumberTypeE_find (Ruby sym); #define TYPESWITCH(T,C,E) switch (T) { \ case uint8_e: C(uint8) break; case int16_e: C(int16) break; \ case int32_e: C(int32) break; NONLITE(case int64_e: C(int64) break; \ case float32_e: C(float32) break; case float64_e: C(float64) break; \ case ruby_e: C(ruby) break;) \ default: E; RAISE("type '%s' not available here",number_type_table[T].sym);} #define TYPESWITCH_JUSTINT(T,C,E) switch (T) { \ case uint8_e: C(uint8) break; case int16_e: C(int16) break; \ case int32_e: C(int32) break; NONLITE(case int64_e: C(int64) break;) \ default: E; RAISE("type '%s' not available here",number_type_table[T].sym);} // Numop objects encapsulate optimised loops of simple operations enum LeftRight { at_left, at_right }; template struct NumopOn : CObject { // Function Vectorisations typedef void (*Map )( int n, T *as, T b ); Map op_map; typedef void (*Zip )( int n, T *as, T *bs); Zip op_zip; typedef void (*Fold)(int an, int n, T *as, T *bs); Fold op_fold; typedef void (*Scan)(int an, int n, T *as, T *bs); Scan op_scan; // Algebraic Properties (those involving simply numeric types) typedef bool (*AlgebraicCheck)(T x, LeftRight side); // neutral: right: forall y {f(x,y)=x}; left: forall x {f(x,y)=y}; // absorbent: right: exists a forall y {f(x,y)=a}; ... T (*neutral)(LeftRight); // default neutral: e.g. 0 for addition, 1 for multiplication AlgebraicCheck is_neutral, is_absorbent; NumopOn(Map m, Zip z, Fold f, Scan s, T (*neu)(LeftRight), AlgebraicCheck n, AlgebraicCheck a) : op_map(m), op_zip(z), op_fold(f), op_scan(s), neutral(neu), is_neutral(n), is_absorbent(a) {} NumopOn() {} NumopOn(const NumopOn &z) { op_map = z.op_map; op_zip = z.op_zip; op_fold = z.op_fold; op_scan = z.op_scan; is_neutral = z.is_neutral; neutral = z.neutral; is_absorbent = z.is_absorbent; } }; // semigroup property: associativity: f(a,f(b,c))=f(f(a,b),c) #define OP_ASSOC (1<<0) // abelian property: commutativity: f(a,b)=f(b,a) #define OP_COMM (1<<1) \class Numop < CObject struct Numop : CObject { Ruby /*Symbol*/ sym; const char *name; int flags; //private: #define FOO(T) \ NumopOn on_##T; \ NumopOn *on(T &foo) { \ if (!on_##T.op_map) \ RAISE("operator %s does not support type "#T,rb_sym_name(sym)); \ return &on_##T;} EACH_NUMBER_TYPE(FOO) #undef FOO //public: template inline void map(int n, Pt as, T b) { as.will_use(n); on(*as)->op_map(n,(T *)as,b);} template inline void zip(int n, Pt as, Pt bs) { as.will_use(n); bs.will_use(n); on(*as)->op_zip(n,(T *)as,(T *)bs);} template inline void fold(int an, int n, Pt as, Pt bs) { as.will_use(an); bs.will_use(an*n); typename NumopOn::Fold f = on(*as)->op_fold; if (!f) RAISE("operator %s does not support fold",rb_sym_name(sym)); f(an,n,(T *)as,(T *)bs);} template inline void scan(int an, int n, Pt as, Pt bs) { as.will_use(an); bs.will_use(an*n); typename NumopOn::Scan f = on(*as)->op_scan; if (!f) RAISE("operator %s does not support scan",rb_sym_name(sym)); f(an,n,(T *)as,(T *)bs);} \decl void map_m (NumberTypeE nt, int n, String as, String b); \decl void zip_m (NumberTypeE nt, int n, String as, String bs); \decl void fold_m (NumberTypeE nt, int an, int n, String as, String bs); \decl void scan_m (NumberTypeE nt, int an, int n, String as, String bs); Numop(Ruby /*Symbol*/ sym_, const char *name_, #define FOO(T) NumopOn op_##T, EACH_NUMBER_TYPE(FOO) #undef FOO int flags_) : sym(sym_), name(name_), flags(flags_) { #define FOO(T) on_##T = op_##T; EACH_NUMBER_TYPE(FOO) #undef FOO } }; \end class extern NumberType number_type_table[]; extern Ruby number_type_dict; // GridFlow.@number_type_dict={} extern Ruby op_dict; // GridFlow.@op_dict={} static inline NumberTypeE convert(Ruby x, NumberTypeE *bogus) { return NumberTypeE_find(x); } #ifndef IS_BRIDGE static Numop *convert(Ruby x, Numop **bogus) { Ruby s = rb_hash_aref(rb_ivar_get(mGridFlow,SI(@op_dict)),x); if (s==Qnil) RAISE("expected two-input-operator"); return FIX2PTR(Numop,s); } #endif // **************************************************************** \class Grid < CObject struct Grid : CObject { P dim; NumberTypeE nt; void *data; Grid(P dim, NumberTypeE nt, bool clear=false) : dim(0), nt(int32_e), data(0) { if (!dim) RAISE("hell"); init(dim,nt); if (clear) {int size = bytes(); CLEAR(Pt((char *)data,size),size);} } Grid(Ruby x) : dim(0), nt(int32_e), data(0) { init_from_ruby(x); } Grid(int n, Ruby *a, NumberTypeE nt=int32_e) : dim(0), nt(int32_e), data(0) { init_from_ruby_list(n,a,nt); } int32 bytes() { return dim->prod()*number_type_table[nt].size/8; } P to_dim () { return new Dim(dim->prod(),(Pt)*this); } #define FOO(type) \ operator type *() { return (type *)data; } \ operator Pt() { return Pt((type *)data,dim->prod()); } EACH_NUMBER_TYPE(FOO) #undef FOO Grid *dup () { Grid *foo=new Grid(dim,nt); memcpy(foo->data,data,bytes()); return foo; } ~Grid() {if (data) free(data);} private: void init(P dim, NumberTypeE nt) { this->dim = dim; this->nt = nt; data = 0; if (dim) data = memalign(16,bytes()+16); //fprintf(stderr,"rdata=%p data=%p align=%d\n",rdata,data,align); } void init_from_ruby(Ruby x); void init_from_ruby_list(int n, Ruby *a, NumberTypeE nt=int32_e); }; \end class Grid static inline Grid *convert (Ruby r, Grid **bogus) { return r ? new Grid(r) : 0; } // DimConstraint interface: // return if d is acceptable // else RAISE with proper descriptive message typedef void (*DimConstraint)(P d); struct PtrGrid : public P { DimConstraint dc; void constrain(DimConstraint dc_) { dc=dc_; } P next; PtrGrid() : P(), dc(0), next(0) {} PtrGrid(const PtrGrid &_p) : P(), dc(0), next(0) {dc=_p.dc; p=_p.p; INCR;} PtrGrid &operator =( Grid *_p) {if(dc&&p)dc(_p->dim); DECR; p=_p; INCR; return *this;} PtrGrid &operator =(P _p) {if(dc&&p)dc(_p->dim); DECR; p=_p.p; INCR; return *this;} PtrGrid &operator =(PtrGrid _p) {if(dc&&p)dc(_p->dim); DECR; p=_p.p; INCR; return *this;} }; #ifndef IS_BRIDGE static inline P convert(Ruby x, P *foo) { Grid *d = convert(x,(Grid **)0); if (!d) RAISE("urgh"); if (d->dim->n!=1) RAISE("dimension list must have only one dimension itself"); return new Dim(d->dim->v[0],(int32 *)(d->data)); } static inline PtrGrid convert(Ruby x, PtrGrid *foo) { PtrGrid pg; pg = convert(x,(Grid **)0); return pg; } #endif //**************************************************************** // GridInlet represents a grid-aware inlet // four-part macro for defining the behaviour of a gridinlet in a class // C:Class I:Inlet #define GRID_INLET(C,I) \ template void C::grinw_##I (GridInlet *in, int n, Pt data) { \ ((C*)(in->parent))->grin_##I(in,n,data); } \ template void C::grin_##I (GridInlet *in, int n, Pt data) { \ if (n==-1) #define GRID_ALLOC else if (n==-3) #define GRID_FLOW else if (n>=0) #define GRID_FINISH else if (n==-2) #define GRID_END } /* macro for defining a gridinlet's behaviour as just storage (no backstore) */ // V is a PtrGrid instance-var #define GRID_INPUT(C,I,V) \ GRID_INLET(C,I) { V=new Grid(in->dim,NumberTypeE_type_of(*data)); } \ GRID_FLOW { COPY((Pt)*(V)+in->dex, data, n); } GRID_FINISH // macro for defining a gridinlet's behaviour as just storage (with backstore) // V is a PtrGrid instance-var #define GRID_INPUT2(C,I,V) \ GRID_INLET(C,I) { \ if (is_busy_except(in)) { \ V.next = new Grid(in->dim,NumberTypeE_type_of(*data)); \ } else V= new Grid(in->dim,NumberTypeE_type_of(*data)); \ } GRID_FLOW { \ COPY(((Pt)*(V.next?V.next.p:&*V.p))+in->dex, data, n); \ } GRID_FINISH typedef struct GridInlet GridInlet; typedef struct GridHandler { #define FOO(T) \ void (*flow_##T)(GridInlet *in, int n, Pt data); \ void flow(GridInlet *in, int n, Pt data) const { \ assert(flow_##T); flow_##T(in,n,data); } EACH_NUMBER_TYPE(FOO) #undef FOO } GridHandler; typedef struct GridObject GridObject; \class GridInlet < CObject struct GridInlet : CObject { GridObject *parent; const GridHandler *gh; GridObject *sender; P dim; NumberTypeE nt; int dex; PtrGrid buf;// factor-chunk buffer int bufi; // buffer index: how much of buf is filled int mode; // 0=ignore; 4=ro; 6=rw int allocfactor,allocmin,allocmax,allocn; Pt alloc; // methods GridInlet(GridObject *parent_, const GridHandler *gh_) : parent(parent_), gh(gh_), sender(0), dim(0), nt(int32_e), dex(0), bufi(0), mode(4) {} ~GridInlet() {} void set_factor(int factor); void set_mode(int mode_) { mode=mode_; } int32 factor() {return buf?buf->dim->prod():1;} Ruby begin(int argc, Ruby *argv); // n=-1 is begin, and n=-2 is _finish_. the name "end" is now used // as an end-marker for inlet definitions... sorry for the confusion // GF-0.8: n=-3 is alloc. template void flow(int mode, int n, Pt data); void end(); // this one ought to be called finish(). void from_ruby_list(int argc, Ruby *argv, NumberTypeE nt=int32_e) { Grid t(argc,argv,nt); from_grid(&t); } void from_ruby(int argc, Ruby *argv) { Grid t(argv[0]); from_grid(&t); } void from_grid(Grid *g); bool supports_type(NumberTypeE nt); private: template void from_grid2(Grid *g, T foo); }; \end class GridInlet //**************************************************************** // for use by source_filter.rb ONLY (for \grin and \classinfo) // C is for class, I for inlet number // GRIN1 : int32 only // GRIN4 : all types // GRIN2 : integers only; no floats (no R either actually) // GRINF : floats only; no integers #ifndef HAVE_LITE #define GRIN(TB,TS,TI,TL,TF,TD,TR) {TB,TS,TI,TL,TF,TD,TR} #else #define GRIN(TB,TS,TI,TL,TF,TD,TR) {TB,TS,TI} #endif // HAVE_LITE #define GRIN1(C,I) GRIN(0,0,C::grinw_##I,0,0,0,0) #define GRIN4(C,I) GRIN(C::grinw_##I,C::grinw_##I,C::grinw_##I,C::grinw_##I,C::grinw_##I,C::grinw_##I,C::grinw_##I) #define GRIN2(C,I) GRIN(C::grinw_##I,C::grinw_##I,C::grinw_##I,C::grinw_##I,0,0,0) #define GRINF(C,I) GRIN(0,0,0,0,C::grinw_##I,C::grinw_##I,0) struct FClass { // 0.7.8: removed all GridObject-specific stuff. void *(*allocator)(); // returns a new C++ object void (*startup)(Ruby rself); // initializer for the Ruby class const char *name; // C++/Ruby name (not PD name) int methodsn; MethodDecl *methods; // C++ -> Ruby methods }; //**************************************************************** // GridOutlet represents a grid-aware outlet \class GridOutlet < CObject struct GridOutlet : CObject { // number of (minimum,maximum) BYTES to send at once // starting with version 0.8, this is amount of BYTES, not amount of NUMBERS. static const int MIN_PACKET_SIZE = 1<<8; static const int MAX_PACKET_SIZE = 1<<12; // those are set only once GridObject *parent; // not a P<> because of circular refs P dim; // dimensions of the grid being sent NumberTypeE nt; PtrGrid buf; // temporary buffer bool frozen; // is the "begin" phase finished? std::vector inlets; // which inlets are we connected to // those are updated during transmission int dex; // how many numbers were already sent in this connection int bufi; // number of bytes used in the buffer // methods GridOutlet(GridObject *parent_, int woutlet, P dim_, NumberTypeE nt_=int32_e) : parent(parent_), dim(dim_), nt(nt_), frozen(false), dex(0), bufi(0) { int ntsz = number_type_table[nt].size; buf=new Grid(new Dim(MAX_PACKET_SIZE/*/ntsz*/), nt); begin(woutlet,dim,nt); } ~GridOutlet() {} void callback(GridInlet *in); // send/send_direct: data belongs to caller, may be stack-allocated, // receiver doesn't modify the data; in send(), there is buffering; // in send_direct(), there is not. When switching from buffered to // unbuffered mode, flush() must be called template void send(int n, Pt data); void flush(); // goes with send(); // give: data must be dynamically allocated as a whole: the data // will be deleted eventually, and should not be used by the caller // beyond the call to give(). template void give(int n, Pt data); // third way to send (upcoming, in GF-0.8.??) is called "ask". template void ask(int &n, Pt &data, int factor, int min, int max); private: void begin(int woutlet, P dim, NumberTypeE nt=int32_e); template void send_direct(int n, Pt data); void end() { flush(); for (uint32 i=0; iend(); dim=0; } }; \end class GridOutlet //**************************************************************** typedef struct BFObject BFObject; // Pd t_object or something // represents objects that have inlets/outlets \class FObject < CObject struct FObject : CObject { BFObject *bself; // point to PD peer uint64 total_time; FObject() : bself(0), total_time(0) {} const char *args() { Ruby s=rb_funcall(rself,SI(args),0); if (s==Qnil) return 0; return rb_str_ptr(s); } \decl Ruby total_time_get(); \decl Ruby total_time_set(Ruby x); \decl void send_in (...); \decl void send_out (...); \decl void delete_m (); }; \end class FObject \class GridObject < FObject struct GridObject : FObject { std::vector > in; P out; // Make sure you distinguish #close/#delete, and C++'s delete. The first // two are quite equivalent and should never make an object "crashable". // C++'s delete is called by Ruby's garbage collector or by PureData's delete. GridObject() {} ~GridObject() {check_magic();} bool is_busy_except(P gin) { for (uint32 i=0; idim) return true; return false; } \decl Ruby method_missing(...); \decl Array inlet_dim(int inln); \decl Symbol inlet_nt(int inln); \decl void inlet_set_factor(int inln, int factor); \decl void send_out_grid_begin(int outlet, Array dim, NumberTypeE nt=int32_e); \decl void send_out_grid_flow (int outlet, String buf, NumberTypeE nt=int32_e); }; \end class GridObject uint64 gf_timeofday(); extern "C" void Init_gridflow (); void gfpost(const char *fmt, ...); extern Numop *op_add,*op_sub,*op_mul,*op_div,*op_mod,*op_shl,*op_and,*op_put; #define INFO(OBJ) rb_str_ptr(rb_funcall(OBJ->rself,SI(info),0)) //#define INFO(OBJ) "(bleh)" #define NOTEMPTY(_a_) if (!(_a_)) RAISE("in [%s], '%s' is empty",INFO(this), #_a_); #define SAME_TYPE(_a_,_b_) \ if ((_a_)->nt != (_b_)->nt) RAISE("%s: same type please (%s has %s; %s has %s)", \ INFO(this), \ #_a_, number_type_table[(_a_)->nt].name, \ #_b_, number_type_table[(_b_)->nt].name); static void SAME_DIM(int n, P a, int ai, P b, int bi) { if (ai+n > a->n) RAISE("left hand: not enough dimensions"); if (bi+n > b->n) RAISE("right hand: not enough dimensions"); for (int i=0; iv[ai+i] != b->v[bi+i]) { RAISE("mismatch: left dim #%d is %d, right dim #%d is %d", ai+i, a->v[ai+i], bi+i, b->v[bi+i]);}}} // a stack for the profiler, etc. #define GF_STACK_MAX 256 //#define NO_INLINE(decl) decl __attribute__((noinline)) #define NO_INLINE(decl) decl struct GFStack { struct GFStackFrame { FObject *o; void *bp; // a pointer into system stack uint64 time; }; // sizeof() == 16 (in 32-bit mode) GFStackFrame s[GF_STACK_MAX]; int n; GFStack() { n = 0; } NO_INLINE(void push (FObject *o)); NO_INLINE(void pop ()); }; extern GFStack gf_stack; struct GFStackMarker { int n; bool flag; GFStackMarker(FObject *o) { n = gf_stack.n; gf_stack.push(o); flag=true; } ~GFStackMarker() { while (gf_stack.n != n) gf_stack.pop(); } bool once () { if (flag) { flag=false; return true; } else return false; } }; typedef GridObject Format; #endif // __GF_GRID_H