Update of /cvsroot/pure-data/externals/gridflow/base In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21117/base
Added Files: bitpacking.c flow_objects.c flow_objects.rb flow_objects_for_image.c flow_objects_for_matrix.c grid.c grid.h main.c main.rb number.c source_filter.rb test.rb Log Message: starting to commit gridflow 0.8.0 ... if you know how to use "cvs import" please mail me and i'll use it for 0.8.1
--- NEW FILE: source_filter.rb --- $keywords = %w(class decl def end grdecl) $stack = [] $classes = []
ClassDecl = Struct.new(:name,:supername,:methods,:grins,:attrs,:info) MethodDecl = Struct.new(:rettype,:selector,:arglist,:minargs,:maxargs,:where) Arg = Struct.new(:type,:name,:default)
class MethodDecl def ==(o) return false unless rettype==o.rettype && maxargs==o.maxargs # && minargs==o.minargs arglist.each_index{|i| arglist[i] == o.arglist[i] or return false } return true end end
class Arg def ==(o) type==o.type && name==o.name # && default==o.default end end
In = File.open ARGV[0], "r" Out = File.open ARGV[1], "w"
def handle_class(line) raise "already in class #{where}" if $stack[-1] and ClassDecl===$stack[-1] #STDERR.puts "class: #{line}" /^(\w+)(?:\s*<\s*(\w+))?$/.match line or raise "syntax error #{where}" q=ClassDecl.new($1,$2,{},{},{},false) $stack << q $classes << q Out.puts "" end
def parse_methoddecl(line,term) /^(\w+)\s+(\w+)\s*(([^)]*))\s*#{term}/.match line or raise "syntax error #{where} #{line}" rettype,selector,arglist = $1,$2,$3 arglist,minargs,maxargs = parse_arglist arglist MethodDecl.new(rettype,selector,arglist,minargs,maxargs,where) end
def parse_arglist(arglist) arglist = arglist.split(/,/) maxargs = arglist.length args = arglist.map {|arg| if /^\s*...\s*$/.match arg then maxargs=-1; next end /^\s*([\w\s*<>]+)\s*\b(\w+)\s*(?:=(.*))?/.match arg or raise "syntax error in "#{arg}" #{where}" type,name,default=$1,$2,$3 Arg.new(type.sub(/\s+$/,""),name,default) }.compact minargs = args.length minargs-=1 while minargs>0 and args[minargs-1].default [args,minargs,maxargs] end
def unparse_arglist(arglist,with_default=true) arglist.map {|arg| x="#{arg.type} #{arg.name} " x<<'='<<arg.default if with_default and arg.default x }.join(", ") end
def where "[#{ARGV[0]}:#{$linenumber}]" end
def handle_attr(line) type = line.gsub(%r"//.*$","").gsub(%r"/*.**/","").gsub(%r";?\s*$","") name = type.slice!(/\w+$/) raise "missing \class #{where}" if not $stack[-1] or not ClassDecl===$stack[-1] $stack[-1].attrs[name]=Arg.new(type,name,nil) Out.print line Out.puts "//FCS" handle_decl "void _0_#{name}_m (#{type} #{name});" Out.puts "# #{$linenumber}" end
def handle_decl(line) raise "missing \class #{where}" if not $stack[-1] or not ClassDecl===$stack[-1] classname = $stack[-1].name m = parse_methoddecl(line,";\s*$") $stack[-1].methods[m.selector] = m
Out.print "#{m.rettype} #{m.selector}(int argc, Ruby *argv" Out.print "," if m.arglist.length>0 Out.print "#{unparse_arglist m.arglist});" Out.puts "static Ruby #{m.selector}_wrap"+ "(int argc, Ruby *argv, Ruby rself);//FCS" Out.puts "# #{$linenumber+1}" end
def handle_def(line) m = parse_methoddecl(line,"{?.*$") term = line[/{.*/] qlass = $stack[-1] raise "missing \class #{where}" if not qlass or not ClassDecl===qlass classname = qlass.name if qlass.methods[m.selector] n = m; m = qlass.methods[m.selector] if m!=n then STDERR.puts "warning: def does not match decl:" STDERR.puts "#{m.where}: \decl #{m.inspect}" STDERR.puts "#{n.where}: \def #{n.inspect}" end else qlass.methods[m.selector] = m end
Out.print "Ruby #{classname}::#{m.selector}_wrap"+ "(int argc, Ruby *argv, Ruby rself) {"+ "static const char *methodspec = "+ ""#{qlass.name}::#{m.selector}(#{unparse_arglist m.arglist,false})";"+ "DGS(#{classname});"
Out.print "if (argc<#{m.minargs}" Out.print "||argc>#{m.maxargs}" if m.maxargs!=-1 Out.print ") RAISE("got %d args instead of %d..%d in %s""+ ",argc,#{m.minargs},#{m.maxargs},methodspec);"
error = proc {|x,y| "RAISE("got %s instead of #{x} in %s","+ "rb_str_ptr(rb_inspect(rb_obj_class(#{y}))),methodspec)" }
m.arglist.each_with_index{|arg,i| case arg.type when "Symbol" Out.print "if (argc>#{i} && TYPE(argv[#{i}])!=T_SYMBOL) "+ error[arg.type,"argv[#{i}]"]+";" when "Array" Out.print "if (argc>#{i} && TYPE(argv[#{i}])!=T_ARRAY) "+ error[arg.type,"argv[#{i}]"]+";" when "String" Out.print "if (argc>#{i} && TYPE(argv[#{i}])==T_SYMBOL) "+ "argv[#{i}]=rb_funcall(argv[#{i}],SI(to_s),0);" Out.print "if (argc>#{i} && TYPE(argv[#{i}])!=T_STRING) "+ error[arg.type,"argv[#{i}]"]+";" end }
# Out.print "return " if m.rettype!="void" Out.print "VALUE foo = " if m.rettype!="void" ###
Out.print " self->#{m.selector}(argc,argv" m.arglist.each_with_index{|arg,i| if arg.default then Out.print ",argc<#{i+1}?#{arg.default}:convert(argv[#{i}],(#{arg.type}*)0)" else Out.print ",convert(argv[#{i}],(#{arg.type}*)0)" end } Out.print ");" Out.print "self->check_magic();" Out.print "return Qnil;" if m.rettype=="void" Out.print "return foo;" if m.rettype!="void" ### Out.print "} #{m.rettype} #{classname}::#{m.selector}(int argc, Ruby *argv" Out.print "," if m.arglist.length>0 Out.puts "#{unparse_arglist m.arglist, false})#{term}//FCS" end
def handle_classinfo(line) frame = $stack[-1] cl = frame.name line="{}" if /^\s*$/ =~ line Out.puts "static void #{cl}_startup (Ruby rself);" Out.puts "static void *#{cl}_allocator () {return new #{cl};}" Out.puts "static MethodDecl #{cl}_methods[] = {" Out.puts frame.methods.map {|foo,method| c,s = frame.name,method.selector "{ "#{s}",(RMethod)#{c}::#{s}_wrap }" }.join(",") Out.puts "}; FClass ci#{cl} = { #{cl}_allocator, #{cl}_startup," Out.puts "#{cl.inspect}, COUNT(#{cl}_methods), #{cl}_methods };" Out.puts "void #{frame.name}_startup (Ruby rself) "+line end
def handle_grin(line) fields = line.split(/\s+/) i = fields[0].to_i c = $stack[-1].name Out.print "template <class T> void grin_#{i}(GridInlet *in, int n, Pt<T> data);" Out.print "template <class T> static void grinw_#{i} (GridInlet *in, int n, Pt<T> data);" Out.print "static GridHandler grid_#{i}_hand;" handle_decl "Ruby _#{i}_grid(...);" $stack[-1].grins[i] = fields.dup end
def handle_end(line) frame = $stack.pop fields = line.split(/\s+/) n = fields.length if ClassDecl===frame then #handle_classinfo if not frame.info cl = frame.name if fields[0]!="class" or (n>1 and fields[1]!=cl) then raise "end not matching #{where}" end $stack.push frame frame.attrs.each {|name,attr| type,name,default = attr.to_a #STDERR.puts "type=#{type} name=#{name} default=#{default}" handle_def "void _0_#{name}_m (#{type} #{name}) { this->#{name}=#{name}; }" } frame.grins.each {|i,v| k = case v[1] when nil; '4' when 'int32'; '1' when 'int'; '2' when 'float'; 'F' else raise 'BORK BORK BORK' end Out.print "static GridHandler #{cl}_grid_#{i}_hand = GRIN#{k}(#{cl},#{i});" handle_def "Ruby _#{i}_grid(...) {"+ "if (in.size()<=#{i}) in.resize(#{i}+1);"+ "if (!in[#{i}]) in[#{i}]=new GridInlet((GridObject *)this,&#{cl}_grid_#{i}_hand);"+ "return in[#{i}]->begin(argc,argv);}"
} $stack.pop Out.puts "# #{$linenumber}" end if :ruby==frame then if fields[0]!="ruby" then raise "expected \end ruby" end end Out.puts "" end
def handle_startall(line) $classes.each {|q| Out.print "rb_funcall(EVAL("GridFlow"),SI(fclass_install),2,PTR2FIX(&ci#{q.name})," if q.supername then Out.print "EVAL("GridFlow::#{q.supername}"));" else Out.print "Qnil);" end } Out.puts "" end
def handle_ruby(line) Out.puts "" $stack.push :ruby end
$rubymode=false $linenumber=1 loop{ x = In.gets break if not x if /^\s*\(\w+)\s*(.*)$/.match x then begin send("handle_#{$1}",$2) rescue StandardError => e STDERR.puts e.inspect STDERR.puts "at line #{$linenumber}" STDERR.puts e.backtrace File.unlink ARGV[1] exit 1 end else if $stack[-1]==:ruby then x.gsub!(/([\"])/) { "\"+$1 } x=""#{x.chomp}\n"\n" end Out.puts x end $linenumber+=1 }
--- NEW FILE: flow_objects_for_matrix.c --- /* $Id: flow_objects_for_matrix.c,v 1.1 2005/10/04 02:02:13 matju Exp $
GridFlow Copyright (c) 2001,2002,2003 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 <math.h> #include "grid.h.fcs"
// produce an upper triangular matrix with ones on the diagonal // will also affect any additional columns using the same row-operations
void expect_complete_matrix (P<Dim> d) { if (d->n!=2) RAISE("bletch"); if (d->get(0)>d->get(1)) RAISE("argh"); }
\class GridMatrixSolve < GridObject struct GridMatrixSolve : GridObject { Numop *op_sub; Numop *op_mul; Numop *op_div; PtrGrid matrix; GridMatrixSolve() { matrix.constrain(expect_complete_matrix); } \decl void initialize (); \grin 0 float };
GRID_INPUT(GridMatrixSolve,0,matrix) { int n = matrix->dim->get(0); // # rows int m = matrix->dim->get(1); // # columns Pt<T> mat = (Pt<T>)*matrix; for (int j=0; j<n; j++) { op_div->map(m,mat+j*m,mat[j*m+j]); for (int i=j+1; i<n; i++) { STACK_ARRAY(T,row,m); COPY(row,mat+j,m); op_mul->map(m,row,mat[i*m+j]); op_sub->zip(m,mat+i*m,row); } } GridOutlet out(this,0,matrix->dim); out.send(n*m,mat); } GRID_END
\def void initialize () { rb_call_super(argc,argv); this->op_sub = op_sub; this->op_mul = op_mul; this->op_div = op_div; }
\classinfo { IEVAL(rself,"install '#matrix_solve',1,1"); } \end class
void startup_flow_objects_for_matrix () { \startall }
--- NEW FILE: main.rb --- =begin $Id: main.rb,v 1.1 2005/10/04 02:02:13 matju Exp $
GridFlow Copyright (c) 2001,2002 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. =end
# ENV["RUBY_VERBOSE_GC"]="yes"
# this file gets loaded by main.c upon startup # module GridFlow is supposed to be created by main.c # this includes GridFlow.post_string(s)
# because Ruby1.6 has no #object_id and Ruby1.8 warns on #id unless Object.instance_methods(true).include? "object_id" class Object; alias object_id id end end
# in case of bug in Ruby ("Error: Success") module Errno; class E000 < StandardError; end; end
#$post_log = File.open "/tmp/gridflow.log", "w" $post_log = nil
class Array def split(elem) r=[] j=0 for i in 0...length (r<<self[j,i-j]; j=i+1) if self[i]==elem end r<<self[j,length-j] end end
module GridFlow #------------------
def self.post(s,*a) post_string(sprintf("%s"+s,post_header,*a)) ($post_log << sprintf(s,*a); $post_log.flush) if $post_log end
class<<self attr_accessor :data_path attr_accessor :post_header attr_accessor :verbose attr_reader :fobjects attr_reader :fclasses attr_reader :cpu_hertz attr_reader :subprocesses attr_reader :bridge_name alias gfpost post end
@subprocesses={} @verbose=false @data_path=[] if GridFlow.respond_to? :config then @data_path << GridFlow.config["PUREDATA_PATH"]+"/extra/gridflow/images" end
def self.hunt_zombies #STDERR.puts "GridFlow.hunt_zombies" # the $$ value is bogus begin died = [] subprocesses.each {|x,v| Process.waitpid2(x,Process::WNOHANG) and died<<x } rescue Errno::ECHILD end #STDERR.puts died.inspect died.each {|x| subprocesses.delete x } end
def self.packstring_for_nt(nt) case nt when :u, :u8, :uint8; "C*" when :s, :i16, :int16; "s*" when :i, :i32, :int32; "l*" when :f, :f32, :float32; "f*" when :d, :f64, :float64; "d*" else raise "no decoder for #{nt.inspect}" end end
self.post_header = "[gf] "
def self.gfpost2(fmt,s); post("%s",s) end
if GridFlow.bridge_name then post "This is GridFlow #{GridFlow::GF_VERSION} within Ruby version #{RUBY_VERSION}" post "base/main.c was compiled on #{GridFlow::GF_COMPILE_TIME}" post "Please use at least 1.6.6 if you plan to use sockets" if RUBY_VERSION<"1.6.6" end
if not GridFlow.bridge_name then require "gridflow/bridge/placebo" end
Brace1 = "{".intern Brace2 = "}".intern Paren1 = "(".intern Paren2 = ")".intern
def self.parse(m) m = m.gsub(/({|})/," \1 ").split(/\s+/) m.map! {|x| case x when Integer, Symbol; x when /^[+-]?[0-9]+$/; x.to_i when String; x.intern end } m end
def self.stringify_list(argv) argv.map {|x| stringify x }.join(" ") end
def self.stringify(arg) case arg when Integer, Float, Symbol; arg.to_s when Array; "{#{stringify_list arg}}" end end
::Object.module_eval do def FloatOrSymbol(x) Float(x) rescue x.intern end end
# adding some functionality to that: class FObject @broken_ok = false @do_loadbangs = true class<<self # global attr_accessor :broken_ok # per-class attr_reader :ninlets attr_reader :noutlets attr_accessor :do_loadbangs attr_accessor :comment def foreign_name; @foreign_name if defined? @foreign_name end end def post(*a) GridFlow.post(*a) end def self.subclass(*args,&b) qlass = Class.new self qlass.install(*args) qlass.module_eval(&b) end alias :total_time :total_time_get alias :total_time= :total_time_set attr_writer :args # String attr_accessor :argv # Array attr_reader :outlets attr_accessor :parent_patcher attr_accessor :properties attr_accessor :classname def initialize2; end def args if defined? @args @args else "[#{self.class} ...]" end end alias info args def connect outlet, object, inlet @outlets ||= [] @outlets[outlet] ||= [] @outlets[outlet].push [object, inlet] end def self.name_lookup sym qlasses = GridFlow.fclasses qlass = qlasses[sym.to_s] if not qlass return qlasses['broken'] if @broken_ok raise "object class '#{sym}' not found" end qlass end def self.[](*m) o=nil if m.length==1 and m[0] =~ / / o="[#{m[0]}]" m=GridFlow.parse(m[0]) else o=m.inspect end GridFlow.handle_braces!(m) ms = m.split ','.intern m = ms.shift qlass = m.shift qlassname = qlass.to_s qlass = name_lookup qlass.to_s unless Class===qlass r = qlass.new(*m) r.classname = qlassname GridFlow.post "%s",r.args if GridFlow.verbose for x in ms do r.send_in(-2, *x) end if FObject.do_loadbangs r end def inspect if args then "#<#{self.class} #{args}>" else super end end def initialize(*argv) s = GridFlow.stringify_list argv @argv = argv @args = "[" @args << (self.class.foreign_name || self.to_s) @args << " " if s.length>0 @args << s << "]" @parent_patcher = nil @properties = {} @init_messages = [] end end
class FPatcher < FObject class << self attr_reader :fobjects attr_reader :wires end def initialize(*) super fobjects = self.class.fobjects wires = self.class.wires @fobjects = fobjects.map {|x| if String===x then FObject[x] else x.call end } @inlets = [] @ninlets = self.class.ninlets or raise "oops" i=0 @fobjects << self while i<wires.length do a,b,c,d = wires[i,4] if a==-1 then a=self @inlets[b]||=[] @inlets[b] << [@fobjects[c],d] else if c==-1 then @fobjects[a].connect b,self,d+@ninlets else @fobjects[a].connect b,@fobjects[c],d end end i+=4 end end def method_missing(sym,*args) sym=sym.to_s if sym =~ /^_(\d)_(.*)/ then inl = Integer $1 sym = $2.intern if inl<@ninlets then raise "#{inspect} has not @inlets[#{inl}]" if not @inlets[inl] for x in @inlets[inl] do x[0].send_in x[1],sym,*args end else send_out(inl-@ninlets,sym,*args) end else super end end end
def GridFlow.estimate_cpu_clock u0,t0=GridFlow.rdtsc,Time.new.to_f; sleep 0.01 u1,t1=GridFlow.rdtsc,Time.new.to_f; (u1-u0)/(t1-t0) end
begin @cpu_hertz = (0...3).map { GridFlow.estimate_cpu_clock }.sort[1] # median of three tries rescue GridFlow.post $! end
def GridFlow.find_file s s=s.to_s if s==File.basename(s) then dir = GridFlow.data_path.find {|x| File.exist? "#{x}/#{s}" } if dir then "#{dir}/#{s}" else s end elsif GridFlow.respond_to? :find_file_2 GridFlow.find_file_2 s else s end end
def GridFlow.macerr(i) begin f=File.open("/System/Library/Frameworks/CoreServices.framework/"+ "Versions/A/Frameworks/CarbonCore.framework/Versions/A/Headers/"+ "MacErrors.h") while f.gets m = /^\s*(\w+)\s*=\s*(-\d+),\s*/*\s*(.*)\s**/$/.match $_ next if not m if m[2].to_i == i then return "#{m[2]}: "#{m[3]}"" end end return "no error message available for this error number" rescue FileError return "Can't find Apple's precious copyrighted list of error messages on this system." ensure f.close if f end end
end # module GridFlow
class IO def nonblock= flag bit = Fcntl::O_NONBLOCK state = fcntl(Fcntl::F_GETFL, 0) fcntl(Fcntl::F_SETFL, (state & ~bit) | (if flag; bit else 0 end)) end end
def protect yield rescue Exception => e STDERR.puts "#{e.class}: #{e}" STDERR.puts e.backtrace end
def GridFlow.load_user_config require "gridflow/bridge/puredata.rb" if GridFlow.bridge_name == "puredata" user_config_file = ENV["HOME"] + "/.gridflow_startup" begin load user_config_file if File.exist? user_config_file rescue Exception => e GridFlow.post "#{e.class}: #{e}:\n" + e.backtrace.join("\n") GridFlow.post "while loading ~/.gridflow_startup" end end
require "gridflow/base/flow_objects.rb" require "gridflow/format/main.rb"
%w( # #for #finished #type #dim #transpose #perspective #store #outer #grade #redim #import #export #export_list #cast #scale_by #downscale_by #draw_polygon #draw_image #layer #print #pack #export_symbol #rotate #in #out ).each {|k| GridFlow::FObject.name_lookup(k).add_creator k.gsub(/#/,"@") }
END { GridFlow.fobjects.each {|k,v| k.delete if k.respond_to? :delete } GridFlow.fobjects.clear GC.start }
--- NEW FILE: flow_objects_for_image.c --- /* $Id: flow_objects_for_image.c,v 1.1 2005/10/04 02:02:13 matju Exp $
GridFlow Copyright (c) 2001,2002,2003 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 <math.h> #include "grid.h.fcs"
static void expect_picture (P<Dim> d) { if (d->n!=3) RAISE("(height,width,chans) dimensions please");} static void expect_rgb_picture (P<Dim> d) { expect_picture(d); if (d->get(2)!=3) RAISE("(red,green,blue) channels please");} static void expect_rgba_picture (P<Dim> d) { expect_picture(d); if (d->get(2)!=4) RAISE("(red,green,blue,alpha) channels please");} static void expect_max_one_dim (P<Dim> d) { if (d->n>1) { RAISE("expecting Dim[] or Dim[n], got %s",d->to_s()); }}
//**************************************************************** //{ Dim[A,B,*Cs]<T>,Dim[D,E]<T> -> Dim[A,B,*Cs]<T> }
static void expect_convolution_matrix (P<Dim> d) { if (d->n != 2) RAISE("only exactly two dimensions allowed for now (got %d)", d->n); }
// entry in a compiled convolution kernel struct PlanEntry { int y,x; bool neutral; };
\class GridConvolve < GridObject struct GridConvolve : GridObject { \attr Numop *op_para; \attr Numop *op_fold; \attr PtrGrid seed; \attr PtrGrid b; PtrGrid a; int plann; PlanEntry *plan; //Pt? int margx,margy; // margins GridConvolve () : plan(0) { b.constrain(expect_convolution_matrix); plan=0; } \decl void initialize (Grid *r=0); \decl void _0_op (Numop *op); \decl void _0_fold (Numop *op); \decl void _0_seed (Grid *seed); \grin 0 \grin 1 template <class T> void copy_row (Pt<T> buf, int sx, int y, int x); template <class T> void make_plan (T bogus); ~GridConvolve () {if (plan) delete[] plan;} };
template <class T> void GridConvolve::copy_row (Pt<T> buf, int sx, int y, int x) { int day = a->dim->get(0), dax = a->dim->get(1), dac = a->dim->prod(2); y=mod(y,day); x=mod(x,dax); Pt<T> ap = (Pt<T>)*a + y*dax*dac; while (sx) { int sx1 = min(sx,dax-x); COPY(buf,ap+x*dac,sx1*dac); x=0; buf += sx1*dac; sx -= sx1; } }
static Numop *OP(Ruby x) {return FIX2PTR(Numop,rb_hash_aref(op_dict,x));}
template <class T> void GridConvolve::make_plan (T bogus) { P<Dim> da = a->dim, db = b->dim; int dby = db->get(0); int dbx = db->get(1); if (plan) delete[] plan; plan = new PlanEntry[dbx*dby]; int i=0; for (int y=0; y<dby; y++) { for (int x=0; x<dbx; x++) { T rh = ((Pt<T>)*b)[y*dbx+x]; bool neutral = op_para->on(rh)->is_neutral(rh,at_right); bool absorbent = op_para->on(rh)->is_absorbent(rh,at_right); STACK_ARRAY(T,foo,1); if (absorbent) { foo[0] = 0; op_para->map(1,foo,rh); absorbent = op_fold->on(rh)->is_neutral(foo[0],at_right); } if (absorbent) continue; plan[i].y = y; plan[i].x = x; plan[i].neutral = neutral; i++; } } plann = i; }
GRID_INLET(GridConvolve,0) { SAME_TYPE(in,b); SAME_TYPE(in,seed); P<Dim> da = in->dim, db = b->dim; if (!db) RAISE("right inlet has no grid"); if (!seed) RAISE("seed missing"); if (db->n != 2) RAISE("right grid must have two dimensions"); if (da->n < 2) RAISE("left grid has less than two dimensions"); if (seed->dim->n != 0) RAISE("seed must be scalar"); if (da->get(0) < db->get(0)) RAISE("grid too small (y): %d < %d", da->get(0), db->get(0)); if (da->get(1) < db->get(1)) RAISE("grid too small (x): %d < %d", da->get(1), db->get(1)); margy = (db->get(0)-1)/2; margx = (db->get(1)-1)/2; a=new Grid(in->dim,in->nt); out=new GridOutlet(this,0,da,in->nt); } GRID_FLOW { COPY((Pt<T>)*a+in->dex, data, n); } GRID_FINISH { Numop *op_put = OP(SYM(put)); make_plan((T)0); int dbx = b->dim->get(1); int day = a->dim->get(0); int n = a->dim->prod(1); int sx = a->dim->get(1)+dbx-1; int n2 = sx*a->dim->prod(2); STACK_ARRAY(T,buf,n); STACK_ARRAY(T,buf2,n2); T orh=0; for (int iy=0; iy<day; iy++) { op_put->map(n,buf,*(T *)*seed); for (int i=0; i<plann; i++) { int jy = plan[i].y; int jx = plan[i].x; T rh = ((Pt<T>)*b)[jy*dbx+jx]; if (i==0 || plan[i].y!=plan[i-1].y || orh!=rh) { copy_row(buf2,sx,iy+jy-margy,-margx); if (!plan[i].neutral) op_para->map(n2,buf2,rh); } op_fold->zip(n,buf,buf2+jx*a->dim->prod(2)); orh=rh; } out->send(n,buf); } a=0; } GRID_END
GRID_INPUT(GridConvolve,1,b) {} GRID_END
\def void _0_op (Numop *op ) { this->op_para=op; } \def void _0_fold (Numop *op ) { this->op_fold=op; } \def void _0_seed (Grid *seed) { this->seed=seed; }
\def void initialize (Grid *r) { rb_call_super(argc,argv); this->op_para = op_mul; this->op_fold = op_add; this->seed = new Grid(new Dim(),int32_e,true); this->b= r ? r : new Grid(new Dim(1,1),int32_e,true); }
\classinfo { IEVAL(rself,"install '#convolve',2,1"); } \end class GridConvolve
/* ---------------------------------------------------------------- */ /* "#scale_by" does quick scaling of pictures by integer factors */ /*{ Dim[A,B,3]<T> -> Dim[C,D,3]<T> }*/ \class GridScaleBy < GridObject struct GridScaleBy : GridObject { \attr PtrGrid scale; // integer scale factor int scaley; int scalex; \decl void initialize (Grid *factor=0); \grin 0 \grin 1 void prepare_scale_factor () { scaley = ((Pt<int32>)*scale)[0]; scalex = ((Pt<int32>)*scale)[scale->dim->prod()==1 ? 0 : 1]; if (scaley<1) scaley=2; if (scalex<1) scalex=2; } };
GRID_INLET(GridScaleBy,0) { P<Dim> a = in->dim; expect_picture(a); out=new GridOutlet(this,0,new Dim(a->get(0)*scaley,a->get(1)*scalex,a->get(2)),in->nt); in->set_factor(a->get(1)*a->get(2)); } GRID_FLOW { int rowsize = in->dim->prod(1); STACK_ARRAY(T,buf,rowsize*scalex); int chans = in->dim->get(2); #define Z(z) buf[p+z]=data[i+z] for (; n>0; data+=rowsize, n-=rowsize) { int p=0; #define LOOP(z) \ for (int i=0; i<rowsize; i+=z) \ for (int k=0; k<scalex; k++, p+=3) switch (chans) { case 3: LOOP(3) {Z(0);Z(1);Z(2);} break; case 4: LOOP(4) {Z(0);Z(1);Z(2);Z(3);} break; default: LOOP(chans) {for (int c=0; c<chans; c++) Z(c);} } #undef LOOP for (int j=0; j<scaley; j++) out->send(rowsize*scalex,buf); } #undef Z } GRID_END
static void expect_scale_factor (P<Dim> dim) { if (dim->prod()!=1 && dim->prod()!=2) RAISE("expecting only one or two numbers"); }
GRID_INPUT(GridScaleBy,1,scale) { prepare_scale_factor(); } GRID_END
\def void initialize (Grid *factor) { scale.constrain(expect_scale_factor); rb_call_super(argc,argv); scale=new Grid(INT2NUM(2)); if (factor) scale=factor; prepare_scale_factor(); }
\classinfo { IEVAL(rself,"install '#scale_by',2,1"); } \end class GridScaleBy
// ---------------------------------------------------------------- //{ Dim[A,B,3]<T> -> Dim[C,D,3]<T> } \class GridDownscaleBy < GridObject struct GridDownscaleBy : GridObject { \attr PtrGrid scale; \attr bool smoothly; int scaley; int scalex; PtrGrid temp; \decl void initialize (Grid *factor=0, Symbol option=Qnil); \grin 0 \grin 1 void prepare_scale_factor () { scaley = ((Pt<int32>)*scale)[0]; scalex = ((Pt<int32>)*scale)[scale->dim->prod()==1 ? 0 : 1]; if (scaley<1) scaley=2; if (scalex<1) scalex=2; } };
GRID_INLET(GridDownscaleBy,0) {
P<Dim> a = in->dim; if (a->n!=3) RAISE("(height,width,chans) please"); out=new GridOutlet(this,0,new Dim(a->get(0)/scaley,a->get(1)/scalex,a->get(2)),in->nt); in->set_factor(a->get(1)*a->get(2)); // i don't remember why two rows instead of just one. temp=new Grid(new Dim(2,in->dim->get(1)/scalex,in->dim->get(2)),in->nt); } GRID_FLOW { int rowsize = in->dim->prod(1); int rowsize2 = temp->dim->prod(1); Pt<T> buf = (Pt<T>)*temp; //!@#$ maybe should be something else than T ? int xinc = in->dim->get(2)*scalex; int y = in->dex / rowsize; int chans=in->dim->get(2); #define Z(z) buf[p+z]+=data[i+z] if (smoothly) { while (n>0) { if (y%scaley==0) CLEAR(buf,rowsize2); #define LOOP(z) \ for (int i=0,p=0; p<rowsize2; p+=z) \ for (int j=0; j<scalex; j++,i+=z) switch (chans) { case 1: LOOP(1) {Z(0);} break; case 2: LOOP(2) {Z(0);Z(1);} break; case 3: LOOP(3) {Z(0);Z(1);Z(2);} break; case 4: LOOP(4) {Z(0);Z(1);Z(2);Z(3);} break; default:LOOP(chans) {for (int k=0; k<chans; k++) Z(k);} break; } #undef LOOP y++; if (y%scaley==0 && out->dim) { op_div->map(rowsize2,buf,(T)(scalex*scaley)); out->send(rowsize2,buf); CLEAR(buf,rowsize2); } data+=rowsize; n-=rowsize; } #undef Z } else { #define Z(z) buf[p+z]=data[i+z] for (; n>0 && out->dim; data+=rowsize, n-=rowsize,y++) { if (y%scaley!=0) continue; #define LOOP(z) for (int i=0,p=0; p<rowsize2; i+=xinc, p+=z) switch(in->dim->get(2)) { case 1: LOOP(1) {Z(0);} break; case 2: LOOP(2) {Z(0);Z(1);} break; case 3: LOOP(3) {Z(0);Z(1);Z(2);} break; case 4: LOOP(4) {Z(0);Z(1);Z(2);Z(3);} break; default:LOOP(chans) {for (int k=0; k<chans; k++) Z(k);}break; } #undef LOOP out->send(rowsize2,buf); } } #undef Z } GRID_END
GRID_INPUT(GridDownscaleBy,1,scale) { prepare_scale_factor(); } GRID_END
\def void initialize (Grid *factor, Symbol option) { scale.constrain(expect_scale_factor); rb_call_super(argc,argv); scale=new Grid(INT2NUM(2)); if (factor) scale=factor; prepare_scale_factor(); smoothly = option==SYM(smoothly); }
\classinfo { IEVAL(rself,"install '#downscale_by',2,1"); } \end class GridDownscaleBy
//**************************************************************** \class GridLayer < GridObject struct GridLayer : GridObject { PtrGrid r; GridLayer() { r.constrain(expect_rgb_picture); } \grin 0 int \grin 1 int };
GRID_INLET(GridLayer,0) { NOTEMPTY(r); SAME_TYPE(in,r); P<Dim> a = in->dim; expect_rgba_picture(a); if (a->get(1)!=r->dim->get(1)) RAISE("same width please"); if (a->get(0)!=r->dim->get(0)) RAISE("same height please"); in->set_factor(a->prod(2)); out=new GridOutlet(this,0,r->dim); } GRID_FLOW { Pt<T> rr = ((Pt<T>)*r) + in->dex*3/4; STACK_ARRAY(T,foo,n*3/4); #define COMPUTE_ALPHA(c,a) \ foo[j+c] = (data[i+c]*data[i+a] + rr[j+c]*(256-data[i+a])) >> 8 for (int i=0,j=0; i<n; i+=4,j+=3) { COMPUTE_ALPHA(0,3); COMPUTE_ALPHA(1,3); COMPUTE_ALPHA(2,3); } #undef COMPUTE_ALPHA out->send(n*3/4,foo); } GRID_END
GRID_INPUT(GridLayer,1,r) {} GRID_END
\classinfo { IEVAL(rself,"install '#layer',2,1"); } \end class GridLayer
// **************************************************************** // pad1,pad2 only are there for 32-byte alignment struct Line { int32 y1,x1,y2,x2,x,m,pad1,pad2; };
static void expect_polygon (P<Dim> d) { if (d->n!=2 || d->get(1)!=2) RAISE("expecting Dim[n,2] polygon"); }
\class DrawPolygon < GridObject struct DrawPolygon : GridObject { \attr Numop *op; \attr PtrGrid color; \attr PtrGrid polygon; PtrGrid color2; PtrGrid lines; int lines_start; int lines_stop; DrawPolygon() { color.constrain(expect_max_one_dim); polygon.constrain(expect_polygon); } \decl void initialize (Numop *op, Grid *color=0, Grid *polygon=0); \grin 0 \grin 1 \grin 2 int32 void init_lines();
};
void DrawPolygon::init_lines () { int nl = polygon->dim->get(0); lines=new Grid(new Dim(nl,8), int32_e); Pt<Line> ld = Pt<Line>((Line *)(int32 *)*lines,nl); Pt<int32> pd = *polygon; for (int i=0,j=0; i<nl; i++) { ld[i].y1 = pd[j+0]; ld[i].x1 = pd[j+1]; j=(j+2)%(2*nl); ld[i].y2 = pd[j+0]; ld[i].x2 = pd[j+1]; if (ld[i].y1>ld[i].y2) memswap(Pt<int32>(ld+i)+0,Pt<int32>(ld+i)+2,2); } }
static int order_by_starting_scanline (const void *a, const void *b) { return ((Line *)a)->y1 - ((Line *)b)->y1; }
static int order_by_column (const void *a, const void *b) { return ((Line *)a)->x - ((Line *)b)->x; }
GRID_INLET(DrawPolygon,0) { NOTEMPTY(color); NOTEMPTY(polygon); NOTEMPTY(lines); SAME_TYPE(in,color); if (in->dim->n!=3) RAISE("expecting 3 dimensions"); if (in->dim->get(2)!=color->dim->get(0)) RAISE("image does not have same number of channels as stored color"); out=new GridOutlet(this,0,in->dim,in->nt); lines_start = lines_stop = 0; in->set_factor(in->dim->get(1)*in->dim->get(2)); int nl = polygon->dim->get(0); qsort((int32 *)*lines,nl,sizeof(Line),order_by_starting_scanline); int cn = color->dim->prod(); color2=new Grid(new Dim(cn*16), color->nt); for (int i=0; i<16; i++) COPY((Pt<T>)*color2+cn*i,(Pt<T>)*color,cn); } GRID_FLOW { int nl = polygon->dim->get(0); Pt<Line> ld = Pt<Line>((Line *)(int32 *)*lines,nl); int f = in->factor(); int y = in->dex/f; int cn = color->dim->prod(); Pt<T> cd = (Pt<T>)*color2; while (n) { while (lines_stop != nl && ld[lines_stop].y1<=y) lines_stop++; for (int i=lines_start; i<lines_stop; i++) { if (ld[i].y2<=y) { memswap(ld+i,ld+lines_start,1); lines_start++; } } if (lines_start == lines_stop) { out->send(f,data); } else { int32 xl = in->dim->get(1); Pt<T> data2 = ARRAY_NEW(T,f); COPY(data2,data,f); for (int i=lines_start; i<lines_stop; i++) { Line &l = ld[i]; l.x = l.x1 + (y-l.y1)*(l.x2-l.x1+1)/(l.y2-l.y1+1); } qsort(ld+lines_start,lines_stop-lines_start, sizeof(Line),order_by_column); for (int i=lines_start; i<lines_stop-1; i+=2) { int xs = max(ld[i].x,(int32)0), xe = min(ld[i+1].x,xl); if (xs>=xe) continue; /* !@#$ WHAT? */ while (xe-xs>=16) { op->zip(16*cn,data2+cn*xs,cd); xs+=16; } op->zip((xe-xs)*cn,data2+cn*xs,cd); } out->give(f,data2); } n-=f; data+=f; y++; } } GRID_END
GRID_INPUT(DrawPolygon,1,color) {} GRID_END GRID_INPUT(DrawPolygon,2,polygon) {init_lines();} GRID_END
\def void initialize (Numop *op, Grid *color, Grid *polygon) { rb_call_super(argc,argv); this->op = op; if (color) this->color=color; if (polygon) { this->polygon=polygon; init_lines(); } }
\classinfo { IEVAL(rself,"install '#draw_polygon',3,1"); } \end class DrawPolygon
//**************************************************************** static void expect_position(P<Dim> d) { if (d->n!=1) RAISE("position should have 1 dimension, not %d", d->n); if (d->v[0]!=2) RAISE("position dim 0 should have 2 elements, not %d", d->v[0]); }
\class DrawImage < GridObject struct DrawImage : GridObject { \attr Numop *op; \attr PtrGrid image; \attr PtrGrid position; \attr bool alpha; \attr bool tile; DrawImage() : alpha(false), tile(false) { position.constrain(expect_position); image.constrain(expect_picture); }
\decl void initialize (Numop *op, Grid *image=0, Grid *position=0); \decl void _0_alpha (bool v=true); \decl void _0_tile (bool v=true); \grin 0 \grin 1 \grin 2 int32 // draw row # ry of right image in row buffer buf, starting at xs // overflow on both sides has to be handled automatically by this method template <class T> void draw_segment(Pt<T> obuf, Pt<T> ibuf, int ry, int x0); };
#define COMPUTE_ALPHA(c,a) obuf[j+(c)] = ibuf[j+(c)] + (rbuf[a])*(obuf[j+(c)]-ibuf[j+(c)])/256; #define COMPUTE_ALPHA4(b) \ COMPUTE_ALPHA(b+0,b+3); \ COMPUTE_ALPHA(b+1,b+3); \ COMPUTE_ALPHA(b+2,b+3); \ obuf[b+3] = rbuf[b+3] + (255-rbuf[b+3])*(ibuf[j+b+3])/256;
template <class T> void DrawImage::draw_segment(Pt<T> obuf, Pt<T> ibuf, int ry, int x0) { if (ry<0 || ry>=image->dim->get(0)) return; // outside of image int sx = in[0]->dim->get(1), rsx = image->dim->get(1); int sc = in[0]->dim->get(2), rsc = image->dim->get(2); Pt<T> rbuf = (Pt<T>)*image + ry*rsx*rsc; if (x0>sx || x0<=-rsx) return; // outside of buffer int n=rsx; if (x0+n>sx) n=sx-x0; if (x0<0) { rbuf-=rsc*x0; n+=x0; x0=0; } if (alpha && rsc==4 && sc==3) { // RGB by RGBA //!@#$ optimise int j=sc*x0; for (; n; n--, rbuf+=4, j+=3) { op->zip(sc,obuf+j,rbuf); COMPUTE_ALPHA(0,3); COMPUTE_ALPHA(1,3); COMPUTE_ALPHA(2,3); } } else if (alpha && rsc==4 && sc==4) { // RGBA by RGBA op->zip(n*rsc,obuf+x0*rsc,rbuf); int j=sc*x0; for (; n>=4; n-=4, rbuf+=16, j+=16) { COMPUTE_ALPHA4(0);COMPUTE_ALPHA4(4); COMPUTE_ALPHA4(8);COMPUTE_ALPHA4(12); } for (; n; n--, rbuf+=4, j+=4) { COMPUTE_ALPHA4(0); } } else { // RGB by RGB, etc op->zip(n*rsc,obuf+sc*x0,rbuf); } }
GRID_INLET(DrawImage,0) { NOTEMPTY(image); NOTEMPTY(position); SAME_TYPE(in,image); if (in->dim->n!=3) RAISE("expecting 3 dimensions"); int lchan = in->dim->get(2); int rchan = image->dim->get(2); if (alpha && rchan!=4) { RAISE("alpha mode works only with 4 channels in right_hand"); } if (lchan != rchan-(alpha?1:0) && lchan != rchan) { RAISE("right_hand has %d channels, alpha=%d, left_hand has %d, expecting %d or %d", rchan, alpha?1:0, lchan, rchan-(alpha?1:0), rchan); } out=new GridOutlet(this,0,in->dim,in->nt); in->set_factor(in->dim->get(1)*in->dim->get(2)); } GRID_FLOW { int f = in->factor(); int y = in->dex/f; if (position->nt != int32_e) RAISE("position has to be int32"); int py = ((int32*)*position)[0], rsy = image->dim->v[0], sy=in->dim->get(0); int px = ((int32*)*position)[1], rsx = image->dim->v[1], sx=in->dim->get(1); for (; n; y++, n-=f, data+=f) { int ty = div2(y-py,rsy); if (tile || ty==0) { Pt<T> data2 = ARRAY_NEW(T,f); COPY(data2,data,f); if (tile) { for (int x=px-div2(px+rsx-1,rsx)*rsx; x<sx; x+=rsx) { draw_segment(data2,data,mod(y-py,rsy),x); } } else { draw_segment(data2,data,y-py,px); } out->give(f,data2); } else { out->send(f,data); } } } GRID_END
GRID_INPUT(DrawImage,1,image) {} GRID_END GRID_INPUT(DrawImage,2,position) {} GRID_END \def void _0_alpha (bool v=true) { alpha = v; gfpost("ALPHA=%d",v); } \def void _0_tile (bool v=true) { tile = v; }
\def void initialize (Numop *op, Grid *image, Grid *position) { rb_call_super(argc,argv); this->op = op; if (image) this->image=image; if (position) this->position=position; else this->position=new Grid(new Dim(2),int32_e,true); }
\classinfo { IEVAL(rself,"install '#draw_image',3,1"); } \end class DrawImage
void startup_flow_objects_for_image () { \startall }
--- NEW FILE: number.c --- /* $Id: number.c,v 1.1 2005/10/04 02:02:13 matju Exp $
GridFlow Copyright (c) 2001,2002,2003,2004 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 "grid.h.fcs" #include <math.h> #include <stdlib.h> #include <stdio.h> #include <limits.h>
#ifdef PASS1 NumberType number_type_table[] = { #define FOO(_sym_,_size_,_flags_,args...) NumberType( #_sym_, _size_, _flags_, args ), NUMBER_TYPES(FOO) #undef FOO }; const long number_type_table_n = COUNT(number_type_table); #endif
// those are bogus class-templates in the sense that you don't create // objects from those, you just call static functions. The same kind // of pattern is present in STL to overcome some limitations of C++.
template <class T> class Op { public: // I call abort() on those because I can't say they're purevirtual. static T f(T a, T b) {abort();} static bool is_neutral(T x, LeftRight side) {assert(!"Op::is_neutral called?");} static bool is_absorbent(T x, LeftRight side) {assert(!"Op::is_absorbent called?");} };
template <class O> class OpLoops { public: template <class T> static void op_map (int n, T *as, T b) { if (!n) return; #define FOO(I) as[I]=O::f(as[I],b); UNROLL_8(FOO,n,as) #undef FOO } template <class T> static void op_zip (int n, T *as, T *bs) { if (!n) return; int ba=bs-as; // really! #define FOO(I) as[I]=O::f(as[I],as[ba+I]); UNROLL_8(FOO,n,as) #undef FOO } // disabled template <class T> static void op_zip2 (int n, T *as, T *bs, T *cs) { if (!n) return; int ba=bs-as, ca=cs-as; #define FOO(I) as[ca+I]=O::f(as[I],as[ba+I]); UNROLL_8(FOO,n,as) #undef FOO } #define W(i) as[i]=O::f(as[i],bs[i]); #define Z(i,j) as[i]=O::f(O::f(O::f(O::f(as[i],bs[i]),bs[i+j]),bs[i+j+j]),bs[i+j+j+j]); template <class T> static void op_fold (int an, int n, T *as, T *bs) { switch (an) { case 1: for (; (n&3)!=0; bs++, n--) W(0); for (; n; bs+=4, n-=4) { Z(0,1); } break; case 2: for (; (n&3)!=0; bs+=2, n--) { W(0); W(1); } for (; n; bs+=8, n-=4) { Z(0,2); Z(1,2); } break; case 3: for (; (n&3)!=0; bs+=3, n--) { W(0); W(1); W(2); } for (; n; bs+=12, n-=4) { Z(0,3); Z(1,3); Z(2,3); } break; case 4: for (; (n&3)!=0; bs+=4, n--) { W(0); W(1); W(2); W(3); } for (; n; bs+=16, n-=4) { Z(0,4); Z(1,4); Z(2,4); Z(3,4); } break; default:for (; n--; ) { int i=0; for (; i<(an&-4); i+=4, bs+=4) { as[i+0]=O::f(as[i+0],bs[0]); as[i+1]=O::f(as[i+1],bs[1]); as[i+2]=O::f(as[i+2],bs[2]); as[i+3]=O::f(as[i+3],bs[3]); } for (; i<an; i++, bs++) as[i] = O::f(as[i],*bs); } } } template <class T> static void op_scan (int an, int n, T *as, T *bs) { for (; n--; as=bs-an) { for (int i=0; i<an; i++, as++, bs++) *bs=O::f(*as,*bs); } } };
template <class T> static void quick_mod_map (int n, T *as, T b) { if (!b) return; #define FOO(I) as[I]=mod(as[I],b); UNROLL_8(FOO,n,as) #undef FOO }
template <class T> static void quick_ign_map (int n, T *as, T b) {} template <class T> static void quick_ign_zip (int n, T *as, T *bs) {} template <class T> static void quick_put_map (int n, T *as, T b) { #define FOO(I) as[I]=b; UNROLL_8(FOO,n,as) #undef FOO }
#ifdef PASS1 void quick_put_map (int n, int16 *as, int16 b) { if (n&1!=0 && (long)as&4!=0) { *as++=b; n--; } quick_put_map (n>>1, (int32 *)as, (int32)(b<<16)+b); if (n&1!=0) *as++=b; } void quick_put_map (int n, uint8 *as, uint8 b) { while (n&3!=0 && (long)as&4!=0) { *as++=b; n--; } int32 c=(b<<8)+b; c+=c<<16; quick_put_map (n>>2, (int32 *)as, c); while (n&3!=0) *as++=b; } #endif template <class T> static void quick_put_zip (int n, T *as, T *bs) { gfmemcopy((uint8 *)as, (uint8 *)bs, n*sizeof(T)); }
// classic two-input operator #define DEF_OP(op,expr,neutral,absorbent) \ template <class T> class Y##op : Op<T> { public: \ inline static T f(T a, T b) { return expr; } \ inline static bool is_neutral (T x, LeftRight side) { return neutral; } \ inline static bool is_absorbent(T x, LeftRight side) { return absorbent; } }; #define DEF_OPFT(op,expr,neutral,absorbent,T) \ template <> class Y##op<T> : Op<T> { public: \ inline static T f(T a, T b) { return expr; } \ inline static bool is_neutral (T x, LeftRight side) { return neutral; } \ inline static bool is_absorbent(T x, LeftRight side) { return absorbent; } }; \ // this macro is for operators that have different code for the float version #define DEF_OPF(op,expr,expr2,neutral,absorbent) \ DEF_OP( op,expr, neutral,absorbent) \ DEF_OPFT(op,expr2,neutral,absorbent,float32) \ DEF_OPFT(op,expr2,neutral,absorbent,float64)
#define DECL_OPON(base,op,T) NumopOn<T>( \ &base<Y##op<T> >::op_map, &base<Y##op<T> >::op_zip, \ &base<Y##op<T> >::op_fold, &base<Y##op<T> >::op_scan, \ &Y##op<T>::is_neutral, &Y##op<T>::is_absorbent) #define DECL_OPON_NOFOLD(base,op,T) NumopOn<T>( \ &base<Y##op<T> >::op_map, &base<Y##op<T> >::op_zip, 0,0, \ &Y##op<T>::is_neutral, &Y##op<T>::is_absorbent) #define DECL_OP(op,sym,flags) Numop(0, sym, \ DECL_OPON(OpLoops,op,uint8), DECL_OPON(OpLoops,op,int16), \ DECL_OPON(OpLoops,op,int32) NONLITE(, DECL_OPON(OpLoops,op,int64), \ DECL_OPON(OpLoops,op,float32), DECL_OPON(OpLoops,op,float64)), flags) #define DECL_OP_NOFLOAT(op,sym,flags) Numop(0, sym, \ DECL_OPON(OpLoops,op,uint8), DECL_OPON(OpLoops,op,int16), \ DECL_OPON(OpLoops,op,int32) NONLITE(, DECL_OPON(OpLoops,op,int64), \ NumopOn<float32>(0,0,0,0,0,0), NumopOn<float64>(0,0,0,0,0,0)), flags) #define DECL_OP_NOFOLD(op,sym,flags) Numop(0, sym, \ DECL_OPON_NOFOLD(OpLoops,op,uint8), DECL_OPON_NOFOLD(OpLoops,op,int16), \ DECL_OPON_NOFOLD(OpLoops,op,int32) NONLITE(, DECL_OPON_NOFOLD(OpLoops,op,int64), \ DECL_OPON_NOFOLD(OpLoops,op,float32), DECL_OPON_NOFOLD(OpLoops,op,float64)), flags)
template <class T> static inline T gf_floor (T a) { return (T) floor((double)a); } template <class T> static inline T gf_trunc (T a) { return (T) floor(abs((double)a)) * (a<0?-1:1); }
/* uint8 clipadd(uint8 a, uint8 b) { int32 c=a+b; return c<0?0:c>255?255:c; } int16 clipadd(int16 a, int16 b) { int32 c=a+b; return c<-0x8000?-0x8000:c>0x7fff?0x7fff:c; } int32 clipadd(int32 a, int32 b) { int64 c=a+b; return c<-0x80000000?-0x80000000:c>0x7fffffff?0x7fffffff:c; } int64 clipadd(int64 a, int64 b) { int64 c=(a>>1)+(b>>1)+(a&b&1); return c<(nt_smallest(0LL)/2?nt_smallest(0LL):c>nt_greatest(0LL)/2?nt_greatest(0LL):a+b; } uint8 clipsub(uint8 a, uint8 b) { int32 c=a-b; return c<0?0:c>255?255:c; } int16 clipsub(int16 a, int16 b) { int32 c=a-b; return c<-0x8000?-0x8000:c>0x7fff?0x7fff:c; } int32 clipsub(int32 a, int32 b) { int64 c=a-b; return c<-0x80000000?-0x80000000:c>0x7fffffff?0x7fffffff:c; } int64 clipsub(int64 a, int64 b) { int64 c=(a>>1)-(b>>1); //??? return c<(nt_smallest(0LL)/2?nt_smallest(0LL):c>nt_greatest(0LL)/2?nt_greatest(0LL):a-b; } */
#ifdef PASS1 DEF_OP(ignore, a, side==at_right, side==at_left) DEF_OP(put, b, side==at_left, side==at_right) DEF_OP(add, a+b, x==0, false) DEF_OP(sub, a-b, side==at_right && x==0, false) DEF_OP(bus, b-a, side==at_left && x==0, false) DEF_OP(mul, a*b, x==1, x==0) DEF_OP(mulshr8, ((int32)a*(int32)b)>>8, (int64)x==256, x==0) //!@#$ bug with int64 DEF_OP(div, b==0 ? 0 : a/b, side==at_right && x==1, false) DEF_OP(div2, b==0 ? 0 : div2(a,b), side==at_right && x==1, false) DEF_OP(vid, a==0 ? 0 : b/a, side==at_left && x==1, false) DEF_OP(vid2, a==0 ? 0 : div2(b,a), side==at_left && x==1, false) DEF_OPF(mod, b==0 ? 0 : mod(a,b), b==0 ? 0 : a-b*gf_floor(a/b), false, side==at_left && x==0 || side==at_right && x==1) DEF_OPF(dom, a==0 ? 0 : mod(b,a), a==0 ? 0 : b-a*gf_floor(b/a), false, side==at_left && x==0 || side==at_right && x==1) //DEF_OPF(rem, b==0 ? 0 : a%b, b==0 ? 0 : a-b*gf_trunc(a/b)) //DEF_OPF(mer, a==0 ? 0 : b%a, a==0 ? 0 : b-a*gf_trunc(b/a)) DEF_OP(rem, b==0?0:a%b, false, side==at_left&&x==0 || side==at_right&&x==1) DEF_OP(mer, a==0?0:b%a, false, side==at_left&&x==0 || side==at_right&&x==1) #endif #ifdef PASS2 DEF_OP(gcd, gcd(a,b), x==0, x==1) DEF_OP(gcd2, gcd2(a,b), x==0, x==1) // should test those and pick one of the two DEF_OP(lcm, a==0 || b==0 ? 0 : lcm(a,b), x==1, x==0) DEF_OPF(or , a|b, (float32)((int32)a | (int32)b), x==0, x==nt_all_ones(&x)) DEF_OPF(xor, a^b, (float32)((int32)a ^ (int32)b), x==0, false) DEF_OPF(and, a&b, (float32)((int32)a & (int32)b), x==nt_all_ones(&x), x==0) DEF_OPF(shl, a<<b, a*pow(2.0,+b), side==at_right && x==0, false) DEF_OPF(shr, a>>b, a*pow(2.0,-b), side==at_right && x==0, false) DEF_OP(sc_and, a ? b : a, side==at_left && x!=0, side==at_left && x==0) DEF_OP(sc_or, a ? a : b, side==at_left && x==0, side==at_left && x!=0) DEF_OP(min, min(a,b), x==nt_greatest(&x), x==nt_smallest(&x)) DEF_OP(max, max(a,b), x==nt_smallest(&x), x==nt_greatest(&x)) #endif #ifdef PASS3 DEF_OP(cmp, cmp(a,b), false, false) DEF_OP(eq, a == b, false, false) DEF_OP(ne, a != b, false, false) DEF_OP(gt, a > b, false, side==at_left&&x==nt_smallest(&x)||side==at_right&&x==nt_greatest(&x)) DEF_OP(le, a <= b, false, side==at_left&&x==nt_smallest(&x)||side==at_right&&x==nt_greatest(&x)) DEF_OP(lt, a < b, false, side==at_left&&x==nt_greatest(&x)||side==at_right&&x==nt_smallest(&x)) DEF_OP(ge, a >= b, false, side==at_left&&x==nt_greatest(&x)||side==at_right&&x==nt_smallest(&x)) DEF_OP(sin, (T)(b * sin(a * (M_PI / 18000))), false, false) // "LN=9000+36000n RA=0 LA=..." DEF_OP(cos, (T)(b * cos(a * (M_PI / 18000))), false, false) // "LN=36000n RA=0 LA=..." DEF_OP(atan, (T)(atan2(a,b) * (18000 / M_PI)), false, false) // "LA=0" DEF_OP(tanh, (T)(b * tanh(a * (M_PI / 18000))), false, x==0) DEF_OP(gamma, b<=0 ? 0 : (T)(0+floor(pow(a/256.0,256.0/b)*256.0)), false, false) // "RN=256" DEF_OP(pow, ipow(a,b), false, false) // "RN=1" DEF_OP(log, (T)(a==0 ? 0 : b * log(gf_abs(a))), false, false) // "RA=0" // 0.7.8 //DEF_OPF(clipadd, clipadd(a,b), a+b, x==0, false) //DEF_OPF(clipsub, clipsub(a,b), a-b, side==at_right && x==0, false) DEF_OP(abssub, gf_abs(a-b), false, false) DEF_OP(sqsub, (a-b)*(a-b), false, false) DEF_OP(avg, (a+b)/2, false, false) DEF_OP(hypot, (T)(0+floor(sqrt(a*a+b*b))), false, false) DEF_OP(sqrt, (T)(0+floor(sqrt(a))), false, false) DEF_OP(rand, a==0 ? 0 : random()%(int32)a, false, false) //DEF_OP(erf,"erf*", 0) #endif
extern Numop op_table1[], op_table2[], op_table3[]; extern const long op_table1_n, op_table2_n, op_table3_n;
#ifdef PASS1 Numop op_table1[] = { DECL_OP(ignore, "ignore", OP_ASSOC), DECL_OP(put, "put", OP_ASSOC), DECL_OP(add, "+", OP_ASSOC|OP_COMM), // "LINV=sub" DECL_OP(sub, "-", 0), DECL_OP(bus, "inv+", 0), DECL_OP(mul, "*", OP_ASSOC|OP_COMM), DECL_OP_NOFLOAT(mulshr8, "*>>8", OP_ASSOC|OP_COMM), DECL_OP(div, "/", 0), DECL_OP_NOFLOAT(div2, "div", 0), DECL_OP(vid, "inv*", 0), DECL_OP_NOFLOAT(vid2, "swapdiv", 0), DECL_OP_NOFLOAT(mod, "%", 0), DECL_OP_NOFLOAT(dom, "swap%", 0), DECL_OP_NOFLOAT(rem, "rem", 0), DECL_OP_NOFLOAT(mer, "swaprem", 0), }; const long op_table1_n = COUNT(op_table1); #endif #ifdef PASS2 Numop op_table2[] = { DECL_OP_NOFLOAT(gcd, "gcd", OP_ASSOC|OP_COMM), DECL_OP_NOFLOAT(gcd2, "gcd2", OP_ASSOC|OP_COMM), DECL_OP_NOFLOAT(lcm, "lcm", OP_ASSOC|OP_COMM), DECL_OP(or , "|", OP_ASSOC|OP_COMM), DECL_OP(xor, "^", OP_ASSOC|OP_COMM), DECL_OP(and, "&", OP_ASSOC|OP_COMM), DECL_OP_NOFOLD(shl, "<<", 0), DECL_OP_NOFOLD(shr, ">>", 0), DECL_OP_NOFOLD(sc_and,"&&", 0), DECL_OP_NOFOLD(sc_or, "||", 0), DECL_OP(min, "min", OP_ASSOC|OP_COMM), DECL_OP(max, "max", OP_ASSOC|OP_COMM), }; const long op_table2_n = COUNT(op_table2); #endif #ifdef PASS3 Numop op_table3[] = { DECL_OP_NOFOLD(eq, "==", OP_COMM), DECL_OP_NOFOLD(ne, "!=", OP_COMM), DECL_OP_NOFOLD(gt, ">", 0), DECL_OP_NOFOLD(le, "<=", 0), DECL_OP_NOFOLD(lt, "<", 0), DECL_OP_NOFOLD(ge, ">=", 0), DECL_OP_NOFOLD(cmp, "cmp", 0), DECL_OP_NOFOLD(sin, "sin*", 0), DECL_OP_NOFOLD(cos, "cos*", 0), DECL_OP_NOFOLD(atan, "atan", 0), DECL_OP_NOFOLD(tanh, "tanh*", 0), DECL_OP_NOFOLD(gamma, "gamma", 0), DECL_OP_NOFOLD(pow, "**", 0), DECL_OP_NOFOLD(log, "log*", 0), // 0.7.8 // DECL_OP(clipadd,"clip+", OP_ASSOC|OP_COMM), // DECL_OP(clipsub,"clip-", 0), DECL_OP_NOFOLD(abssub,"abs-", OP_COMM), DECL_OP_NOFOLD(sqsub,"sq-", OP_COMM), DECL_OP_NOFOLD(avg,"avg", OP_COMM), DECL_OP_NOFOLD(hypot,"hypot", OP_COMM), DECL_OP_NOFOLD(sqrt,"sqrt", 0), DECL_OP_NOFOLD(rand,"rand", 0), //DECL_OP_NOFOLD(erf,"erf*", 0), }; const long op_table3_n = COUNT(op_table3); #endif
// D=dictionary, A=table, A##_n=table count. #define INIT_TABLE(D,A) { D=IEVAL(mGridFlow,"@"#D" ||= {}"); \ for(int i=0; i<A##_n; i++) { \ A[i].sym = ID2SYM(rb_intern(A[i].name)); \ rb_hash_aset(D,A[i].sym,PTR2FIX((A+i)));}}
#ifdef PASS1 Ruby op_dict = Qnil; Ruby number_type_dict = Qnil; void startup_number () { INIT_TABLE(op_dict,op_table1) INIT_TABLE(op_dict,op_table2) INIT_TABLE(op_dict,op_table3) INIT_TABLE(number_type_dict,number_type_table)
for (int i=0; i<COUNT(number_type_table); i++) { number_type_table[i].index = (NumberTypeE) i; char a[64]; strcpy(a,number_type_table[i].aliases); char *b = strchr(a,','); if (b) { *b=0; rb_hash_aset(number_type_dict, ID2SYM(rb_intern(b+1)), PTR2FIX(&number_type_table[i])); } rb_hash_aset(number_type_dict, ID2SYM(rb_intern(a)), PTR2FIX(&number_type_table[i])); } // S:name; M:mode; F:replacement function; #define OVERRIDE_INT(S,M,F) { \ Numop *foo = FIX2PTR(Numop,rb_hash_aref(op_dict,SYM(S))); \ foo->on_uint8.op_##M=F; \ foo->on_int16.op_##M=F; \ foo->on_int32.op_##M=F; } OVERRIDE_INT(ignore,map,quick_ign_map); OVERRIDE_INT(ignore,zip,quick_ign_zip); //OVERRIDE_INT(put,map,quick_put_map); //OVERRIDE_INT(put,zip,quick_put_zip); //OVERRIDE_INT(%,map,quick_mod_map); // !@#$ does that make an improvement at all? } #endif
--- NEW FILE: flow_objects.rb --- =begin $Id: flow_objects.rb,v 1.1 2005/10/04 02:02:13 matju Exp $
GridFlow Copyright (c) 2001,2002,2003,2004 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 [...1437 lines suppressed...] def _0_float i; @i=i; send_out 0, *@a[@i]; end def _1_list(*l) @a[@i]=l; end def _0_save(filename,format=nil) f=File.open(filename.to_s,"w") if format then @a.each {|x| f.puts(format.to_s%x) } else @a.each {|x| f.puts(x.join(",")) } end f.close end def _0_load(filename) f=File.open(filename.to_s,"r") @a.clear f.each {|x| @a.push x.split(",").map {|y| Float(y) rescue y.intern }} f.close end }
end # module GridFlow
--- NEW FILE: test.rb --- # $Id: test.rb,v 1.1 2005/10/04 02:02:13 matju Exp $
$:.delete_if {|x| x=='.' } require "gridflow"
include GridFlow GridFlow.verbose=true
$imdir = "./images" $animdir = "/opt/mex" srand Time.new.to_i $port = 4200+rand(100)
def pressakey; puts "press return to continue."; readline; end
class Expect < FObject def praise(*a) #raise(*a) puts a [...1047 lines suppressed...] #test_anim "open file #{$imdir}/g001.jpg"#,"loop 0" #test_anim "open ppm file #{$animdir}/b.ppm.cat" #test_anim "open jpeg file #{$imdir}/rgb.jpeg.cat" #test_anim "open quicktime file BLAH" #test_anim "open quicktime file #{$imdir}/rgb_uncompressed.mov" #test_anim "open quicktime file #{$imdir}/test_mjpega.mov" #test_anim "open ppm gzfile motion_tracking.ppm.cat.gz" #test_anim "open videodev /dev/video","channel 1","size 480 640" #test_anim "open videodev /dev/video1 noinit","transfer read" #test_anim "open videodev /dev/video","channel 1","size 120 160" #test_anim "open mpeg file /home/matju/net/Animations/washington_zoom_in.mpeg" #test_anim "open quicktime file /home/matju/Shauna/part_1.mov" #test_anim "open quicktime file #{$imdir}/gt.mov" #test_anim "open quicktime file /home/matju/pics/domopers_hi.mov" #test_anim "open quicktime file /home/matju/net/c.mov" #test_formats #test_tcp #test_sound #test_metro #$mainloop.loop
--- NEW FILE: flow_objects.c --- /* $Id: flow_objects.c,v 1.1 2005/10/04 02:02:13 matju Exp $
GridFlow Copyright (c) 2001,2002,2003,2004 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 [...1150 lines suppressed...] } } GRID_END
\def void initialize (int32 z) {rb_call_super(argc,argv); this->z=z; } \classinfo { IEVAL(rself,"install '#perspective',1,1"); } \end class GridPerspective
static Numop *OP(Ruby x) { return FIX2PTR(Numop,rb_hash_aref(op_dict,x)); }
void startup_flow_objects () { op_add = OP(SYM(+)); op_sub = OP(SYM(-)); op_mul = OP(SYM(*)); op_shl = OP(SYM(<<)); op_mod = OP(SYM(%)); op_and = OP(SYM(&)); op_div = OP(SYM(/)); op_put = OP(SYM(put)); \startall }
--- NEW FILE: bitpacking.c --- /* $Id: bitpacking.c,v 1.1 2005/10/04 02:02:13 matju Exp $
GridFlow Copyright (c) 2001,2002,2003,2004 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 "grid.h.fcs" #include <math.h> #include <stdlib.h> #include <stdio.h>
#define CONVERT1 t = \ (((in[0] << hb[0]) >> 7) & mask[0]) | \ (((in[1] << hb[1]) >> 7) & mask[1]) | \ (((in[2] << hb[2]) >> 7) & mask[2])
#define CONVERT2 \ for (t=0,i=0; i<self->size; i++) t |= (((in[i] << hb[i]) >> 7) & mask[i]);
#define CONVERT3 \ for (t=0,i=0; i<self->size; i++) { \ t |= ((in[i]>>(7-hb[i]))|(in[i]<<(hb[i]-7))) & mask[i]; \ }
#define WRITE_LE \ for (int bytes = self->bytes; bytes; bytes--, t>>=8) *out++ = t;
#define WRITE_BE { int bytes; \ bytes = self->bytes; \ while (bytes--) { out[bytes] = t; t>>=8; }\ out += self->bytes; }
/* this macro would be faster if the _increment_ was done only once every loop. or maybe gcc does it, i dunno */ #define NTIMES(_x_) \ for (; n>=4; n-=4) { _x_ _x_ _x_ _x_ } \ for (; n; n--) { _x_ }
/* this could be faster (use asm) */ void swap32 (int n, Pt<uint32> data) { NTIMES({ uint32 x = *data; x = (x<<16) | (x>>16); x = ((x&0xff00ff)<<8) | ((x>>8)&0xff00ff); *data++ = x; }) }
/* this could be faster (use asm or do it in int32 chunks) */ void swap16 (int n, Pt<uint16> data) { NTIMES({ uint16 x = *data; *data++ = (x<<8) | (x>>8); }) }
/* **************************************************************** */
template <class T> static void default_pack(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) { uint32 t; int i; int hb[4]; uint32 mask[4]; int sameorder = self->endian==2 || self->endian==::is_le(); int size = self->size;
for (i=0; i<self->size; i++) hb[i] = highest_bit(self->mask[i]); memcpy(mask,self->mask,size*sizeof(uint32));
if (sameorder && size==3) { switch(self->bytes) { case 2: NTIMES(t=CONVERT1; *((int16 *)out)=t; out+=2; in+=3;) return; case 4: NTIMES(t=CONVERT1; *((int32 *)out)=t; out+=4; in+=3;) return; } } if (self->is_le()) { switch (size) { case 3: for (; n--; in+=3) {CONVERT1; WRITE_LE;} break; case 4: for (; n--; in+=4) {CONVERT3; WRITE_LE;} break; default:for (; n--; in+=size) {CONVERT2; WRITE_LE;}} } else { switch (size) { case 3: for (; n--; in+=3) {CONVERT1; WRITE_BE;} break; case 4: for (; n--; in+=4) {CONVERT3; WRITE_BE;} break; default:for (; n--; in+=size) {CONVERT2; WRITE_BE;}} } }
#define LOOP_UNPACK(_reader_) \ for (; n; n--) { \ int bytes=0; uint32 temp=0; _reader_; \ for (int i=0; i<self->size; i++, out++) { \ uint32 t=temp&self->mask[i]; \ *out = (t<<(7-hb[i]))|(t>>(hb[i]-7)); \ } \ } // *out++ = ((temp & self->mask[i]) << 7) >> hb[i];
template <class T> static void default_unpack(BitPacking *self, int n, Pt<uint8> in, Pt<T> out) { int hb[4]; for (int i=0; i<self->size; i++) hb[i] = highest_bit(self->mask[i]); if (is_le()) { // smallest byte first LOOP_UNPACK( for(; self->bytes>bytes; bytes++, in++) temp |= *in<<(8*bytes); ) } else { // biggest byte first LOOP_UNPACK( bytes=self->bytes; for (; bytes; bytes--, in++) temp=(temp<<8)|*in; ) } }
/* **************************************************************** */
template <class T> static void pack2_565(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) { const int hb[3] = {15,10,4}; const uint32 mask[3] = {0x0000f800,0x000007e0,0x0000001f}; uint32 t; NTIMES( t=CONVERT1; *((short *)out)=t; out+=2; in+=3; ) }
template <class T> static void pack3_888(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) { Pt<int32> o32 = (Pt<int32>)out; while (n>=4) { o32[0] = (in[5]<<24) | (in[ 0]<<16) | (in[ 1]<<8) | in[2]; o32[1] = (in[7]<<24) | (in[ 8]<<16) | (in[ 3]<<8) | in[4]; o32[2] = (in[9]<<24) | (in[10]<<16) | (in[11]<<8) | in[6]; o32+=3; in+=12; n-=4; } out = (Pt<uint8>)o32; NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out+=3; in+=3; ) }
/* template <> static void pack3_888(BitPacking *self, int n, Pt<uint8> in, Pt<uint8> out) { Pt<uint32> o32 = Pt<uint32>((uint32 *)out.p,n*3/4); Pt<uint32> i32 = Pt<uint32>((uint32 *)in.p,n*3/4); while (n>=4) { #define Z(w,i) ((word##w>>(i*8))&255) uint32 word0 = i32[0]; uint32 word1 = i32[1]; uint32 word2 = i32[2]; o32[0] = (Z(1,1)<<24) | (Z(0,0)<<16) | (Z(0,1)<<8) | Z(0,2); o32[1] = (Z(1,3)<<24) | (Z(2,0)<<16) | (Z(0,3)<<8) | Z(1,0); o32[2] = (Z(2,1)<<24) | (Z(2,2)<<16) | (Z(2,3)<<8) | Z(1,2); o32+=3; i32+=3; n-=4; } #undef Z out = (Pt<uint8>)o32; in = (Pt<uint8>)i32; NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out+=3; in+=3; ) } */
template <class T> static void pack3_888b(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) { Pt<int32> o32 = (Pt<int32>)out; while (n>=4) { o32[0] = (in[0]<<16) | (in[1]<<8) | in[2]; o32[1] = (in[3]<<16) | (in[4]<<8) | in[5]; o32[2] = (in[6]<<16) | (in[7]<<8) | in[8]; o32[3] = (in[9]<<16) | (in[10]<<8) | in[11]; o32+=4; in+=12; n-=4; } NTIMES( o32[0] = (in[0]<<16) | (in[1]<<8) | in[2]; o32++; in+=3; ) }
/* (R,G,B,?) -> B:8,G:8,R:8,0:8 */ template <class T> static void pack3_bgrn8888(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) { /* NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out+=4; in+=4; ) */ Pt<int32> i32 = (Pt<int32>)in; Pt<int32> o32 = (Pt<int32>)out; while (n>=4) { o32[0] = ((i32[0]&0xff)<<16) | (i32[0]&0xff00) | ((i32[0]>>16)&0xff); o32[1] = ((i32[1]&0xff)<<16) | (i32[1]&0xff00) | ((i32[1]>>16)&0xff); o32[2] = ((i32[2]&0xff)<<16) | (i32[2]&0xff00) | ((i32[2]>>16)&0xff); o32[3] = ((i32[3]&0xff)<<16) | (i32[3]&0xff00) | ((i32[3]>>16)&0xff); o32+=4; i32+=4; n-=4; } NTIMES( o32[0] = ((i32[0]&0xff)<<16) | (i32[0]&0xff00) | ((i32[0]>>16)&0xff); o32++; i32++; ) }
static uint32 bp_masks[][4] = { {0x0000f800,0x000007e0,0x0000001f,0}, {0x00ff0000,0x0000ff00,0x000000ff,0}, };
static Packer bp_packers[] = { {default_pack, default_pack, default_pack}, {pack2_565, pack2_565, pack2_565}, {pack3_888, pack3_888, pack3_888}, {pack3_888b, default_pack, default_pack}, {pack3_bgrn8888, default_pack, default_pack}, };
static Unpacker bp_unpackers[] = { {default_unpack, default_unpack, default_unpack}, };
static BitPacking builtin_bitpackers[] = { BitPacking(2, 2, 3, bp_masks[0], &bp_packers[1], &bp_unpackers[0]), BitPacking(1, 3, 3, bp_masks[1], &bp_packers[2], &bp_unpackers[0]), BitPacking(1, 4, 3, bp_masks[1], &bp_packers[3], &bp_unpackers[0]), BitPacking(1, 4, 4, bp_masks[1], &bp_packers[4], &bp_unpackers[0]), };
/* **************************************************************** */
bool BitPacking::eq(BitPacking *o) { if (!(bytes == o->bytes)) return false; if (!(size == o->size)) return false; for (int i=0; i<size; i++) { if (!(mask[i] == o->mask[i])) return false; } if (endian==o->endian) return true; /* same==little on a little-endian; same==big on a big-endian */ return (endian ^ o->endian ^ ::is_le()) == 2; }
BitPacking::BitPacking(int endian, int bytes, int size, uint32 *mask, Packer *packer, Unpacker *unpacker) { this->endian = endian; this->bytes = bytes; this->size = size; for (int i=0; i<size; i++) this->mask[i] = mask[i]; if (packer) { this->packer = packer; this->unpacker = unpacker; return; } int packeri=-1; this->packer = &bp_packers[0]; this->unpacker = &bp_unpackers[0];
for (int i=0; i<(int)(sizeof(builtin_bitpackers)/sizeof(BitPacking)); i++) { BitPacking *bp = &builtin_bitpackers[i]; if (this->eq(bp)) { this->packer = bp->packer; this->unpacker = bp->unpacker; packeri=i; goto end; } } end:; /* ::gfpost("Bitpacking: endian=%d bytes=%d size=%d packeri=%d", endian, bytes, size, packeri); ::gfpost(" packer=0x%08x unpacker=0x%08x",this->packer,this->unpacker); ::gfpost(" mask=[0x%08x,0x%08x,0x%08x,0x%08x]",mask[0],mask[1],mask[2],mask[3]); */ }
bool BitPacking::is_le() { return endian==1 || (endian ^ ::is_le())==3; }
template <class T> void BitPacking::pack(int n, Pt<T> in, Pt<uint8> out) { switch (NumberTypeE_type_of(*in)) { case uint8_e: packer->as_uint8(this,n,(Pt<uint8>)in,out); break; case int16_e: packer->as_int16(this,n,(Pt<int16>)in,out); break; case int32_e: packer->as_int32(this,n,(Pt<int32>)in,out); break; default: RAISE("argh"); } }
template <class T> void BitPacking::unpack(int n, Pt<uint8> in, Pt<T> out) { switch (NumberTypeE_type_of(*out)) { case uint8_e: unpacker->as_uint8(this,n,in,(Pt<uint8>)out); break; case int16_e: unpacker->as_int16(this,n,in,(Pt<int16>)out); break; case int32_e: unpacker->as_int32(this,n,in,(Pt<int32>)out); break; default: RAISE("argh"); } }
// i'm sorry... see the end of grid.c for an explanation... //static void make_hocus_pocus () { // exit(1); #define FOO(S) \ ((BitPacking*)0)->pack(0,Pt<S>(),Pt<uint8>()); \ ((BitPacking*)0)->unpack(0,Pt<uint8>(),Pt<S>()); EACH_NUMBER_TYPE(FOO) #undef FOO }
--- NEW FILE: grid.h --- /* $Id: grid.h,v 1.1 2005/10/04 02:02:13 matju Exp $
GridFlow Copyright (c) 2001,2002,2003,2004 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 [...1070 lines suppressed...] GFStackFrame s[GF_STACK_MAX]; int n; GFStack() { n = 0; } void push (FObject *o) __attribute__((noinline)); void pop () __attribute__((noinline)); }; 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
--- NEW FILE: main.c --- /* $Id: main.c,v 1.1 2005/10/04 02:02:13 matju Exp $
GridFlow Copyright (c) 2001,2002,2003,2004 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 <sys/stat.h> #include <sys/time.h> #include <time.h> #include <stdarg.h> #include <string.h> #include <signal.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <stdarg.h>
#include "grid.h.fcs" #include "../config.h" #include <assert.h> #include <limits.h>
BuiltinSymbols bsym; GFStack gf_stack; Ruby mGridFlow; Ruby cFObject;
extern "C"{ void rb_raise0( const char *file, int line, const char *func, VALUE exc, const char *fmt, ...) { va_list args; char buf[BUFSIZ]; va_start(args,fmt); vsnprintf(buf, BUFSIZ, fmt, args); buf[BUFSIZ-1]=0; va_end(args); VALUE e = rb_exc_new2(exc, buf); char buf2[BUFSIZ]; snprintf(buf2, BUFSIZ, "%s:%d:in `%s'", file, line, func); buf2[BUFSIZ-1]=0; VALUE ary = rb_funcall(e,SI(caller),0); if (gf_stack.n) { rb_funcall(ary,SI(unshift),2,rb_str_new2(buf2), rb_str_new2(INFO(gf_stack.s[gf_stack.n-1].o))); } else { rb_funcall(ary,SI(unshift),1,rb_str_new2(buf2)); } rb_funcall(e,SI(set_backtrace),1,ary); rb_exc_raise(e); }};
Ruby rb_ary_fetch(Ruby rself, int i) { Ruby argv[] = { INT2NUM(i) }; return rb_ary_aref(COUNT(argv),argv,rself); }
//---------------------------------------------------------------- // CObject
static void CObject_mark (void *z) {} void CObject_free (void *foo) { CObject *self = (CObject *)foo; self->check_magic(); if (!self->rself) { fprintf(stderr,"attempt to free object that has no rself\n"); abort(); } self->rself = 0; /* paranoia */ delete self; }
//---------------------------------------------------------------- // Dim
void Dim::check() { if (n>MAX_DIMENSIONS) RAISE("too many dimensions"); for (int i=0; i<n; i++) if (v[i]<0) RAISE("Dim: negative dimension"); }
// !@#$ big leak machine? // returns a string like "Dim[240,320,3]" char *Dim::to_s() { // if you blow 256 chars it's your own fault char buf[256]; char *s = buf; s += sprintf(s,"Dim["); for(int i=0; i<n; i++) s += sprintf(s,"%s%d", ","+!i, v[i]); s += sprintf(s,"]"); return strdup(buf); }
//---------------------------------------------------------------- \class FObject < CObject
static void FObject_prepare_message(int &argc, Ruby *&argv, Ruby &sym, FObject *foo=0) { if (argc<1) { sym = bsym._bang; } else if (argc>1 && !SYMBOL_P(*argv)) { sym = bsym._list; } else if (INTEGER_P(*argv)||FLOAT_P(*argv)) { sym = bsym._float; } else if (SYMBOL_P(*argv)) { sym = *argv; argc--, argv++; } else if (argc==1 && TYPE(*argv)==T_ARRAY) { sym = bsym._list; argc = rb_ary_len(*argv); argv = rb_ary_ptr(*argv); } else { RAISE("%s received bad message: argc=%d; argv[0]=%s",foo?INFO(foo):"", argc, argc ? rb_str_ptr(rb_inspect(argv[0])) : ""); } }
struct Helper { int argc; Ruby *argv; FObject *self; Ruby rself; int n; // stack level };
static Ruby GridFlow_handle_braces(Ruby rself, Ruby argv);
// inlet #-1 is reserved for SystemInlet messages // inlet #-2 is for inlet #0 messages that happen at start time static void send_in_2 (Helper *h) { PROF(h->self) { int argc = h->argc; Ruby *argv = h->argv; if (h->argc<1) RAISE("not enough args"); int inlet = INT(argv[0]); argc--, argv++; Ruby foo; if (argc==1 && TYPE(argv[0])==T_STRING /* && argv[0] =~ / / */) { foo = rb_funcall(mGridFlow,SI(parse),1,argv[0]); argc = rb_ary_len(foo); argv = rb_ary_ptr(foo); } if (argc>1) { foo = rb_ary_new4(argc,argv); GridFlow_handle_braces(0,foo); argc = rb_ary_len(foo); argv = rb_ary_ptr(foo); } if (inlet==-2) { Array init_messages = rb_ivar_get(h->rself,SI(@init_messages)); rb_ary_push(init_messages, rb_ary_new4(argc,argv)); inlet=0; } if (inlet<0 || inlet>9 /*|| inlet>real_inlet_max*/) if (inlet!=-3 && inlet!=-1) RAISE("invalid inlet number: %d", inlet); Ruby sym; FObject_prepare_message(argc,argv,sym,h->self); // if (rb_const_get(mGridFlow,SI(@verbose))==Qtrue) gfpost m.inspect char buf[256]; if (inlet==-1) sprintf(buf,"_sys_%s",rb_sym_name(sym)); else sprintf(buf,"_%d_%s",inlet,rb_sym_name(sym)); rb_funcall2(h->rself,rb_intern(buf),argc,argv); } /* PROF */ }
static void send_in_3 (Helper *h) { while (gf_stack.n > h->n) gf_stack.pop(); }
\def void send_in (...) { Helper h = {argc,argv,this,rself,gf_stack.n}; rb_ensure( (RMethod)send_in_2,(Ruby)&h, (RMethod)send_in_3,(Ruby)&h); }
\def void send_out (...) { int n=0; if (argc<1) RAISE("not enough args"); int outlet = INT(*argv); if (outlet<0 || outlet>9 /*|| outlet>real_outlet_max*/) RAISE("invalid outlet number: %d",outlet); argc--, argv++; Ruby sym; FObject_prepare_message(argc,argv,sym,this); Ruby noutlets2 = rb_ivar_get(rb_obj_class(rself),SYM2ID(SYM(@noutlets))); if (TYPE(noutlets2)!=T_FIXNUM) { IEVAL(rself,"STDERR.puts inspect"); RAISE("don't know how many outlets this has"); } int noutlets = INT(noutlets2); //if (outlet<0 || outlet>=noutlets) RAISE("outlet %d does not exist",outlet); // was PROF(0) a hack because of exception-handling problems? PROF(0) { Ruby argv2[argc+2]; for (int i=0; i<argc; i++) argv2[2+i] = argv[i]; argv2[0] = INT2NUM(outlet); argv2[1] = sym; rb_funcall2(rself,SI(send_out2), argc+2, argv2);
Ruby ary = rb_ivar_defined(rself,SYM2ID(bsym.iv_outlets)) ? rb_ivar_get(rself,SYM2ID(bsym.iv_outlets)) : Qnil; if (ary==Qnil) goto end; if (TYPE(ary)!=T_ARRAY) RAISE("send_out: expected array"); ary = rb_ary_fetch(ary,outlet); if (ary==Qnil) goto end; if (TYPE(ary)!=T_ARRAY) RAISE("send_out: expected array"); n = rb_ary_len(ary);
for (int i=0; i<n; i++) { Ruby conn = rb_ary_fetch(ary,i); Ruby rec = rb_ary_fetch(conn,0); int inl = INT(rb_ary_fetch(conn,1)); argv2[0] = INT2NUM(inl); rb_funcall2(rec,SI(send_in),argc+2,argv2); } } /* PROF */ end:; }
Ruby FObject_s_new(Ruby argc, Ruby *argv, Ruby qlass) { Ruby allocator = rb_ivar_defined(qlass,SI(@allocator)) ? rb_ivar_get(qlass,SI(@allocator)) : Qnil; FObject *self; if (allocator==Qnil) { // this is a pure-ruby FObject/GridObject // !@#$ GridObject is in FObject constructor (ugly) self = new GridObject; } else { // this is a C++ FObject/GridObject void*(*alloc)() = (void*(*)())FIX2PTR(void,allocator); self = (FObject *)alloc(); } self->check_magic(); Ruby keep = rb_ivar_get(mGridFlow, SI(@fobjects)); self->bself = 0; Ruby rself = Data_Wrap_Struct(qlass, CObject_mark, CObject_free, self); self->rself = rself; rb_hash_aset(keep,rself,Qtrue); // prevent sweeping rb_funcall2(rself,SI(initialize),argc,argv); return rself; }
Ruby FObject_s_install(Ruby rself, Ruby name, Ruby inlets2, Ruby outlets2) { int inlets, outlets; Ruby name2; if (SYMBOL_P(name)) { name2 = rb_funcall(name,SI(to_str),0); } else if (TYPE(name) == T_STRING) { name2 = rb_funcall(name,SI(dup),0); } else { RAISE("expect symbol or string"); } inlets = INT(inlets2); if ( inlets<0 || inlets>9) RAISE("..."); outlets = INT(outlets2); if (outlets<0 || outlets>9) RAISE("..."); rb_ivar_set(rself,SI(@ninlets),INT2NUM(inlets)); rb_ivar_set(rself,SI(@noutlets),INT2NUM(outlets)); rb_ivar_set(rself,SI(@foreign_name),name2); rb_hash_aset(rb_ivar_get(mGridFlow,SI(@fclasses)), name2, rself); rb_funcall(rself, SI(install2), 1, name2); return Qnil; }
\def Ruby total_time_get () {return gf_ull2num(total_time);}
\def Ruby total_time_set (Ruby x) { if (argc<1) RAISE("muh"); total_time = TO(uint64,x); return argv[0]; }
\def void delete_m () { Ruby keep = rb_ivar_get(mGridFlow, SI(@fobjects)); rb_funcall(keep,SI(delete),1,rself); }
\classinfo \end class FObject
/* ---------------------------------------------------------------- */ /* C++<->Ruby bridge for classes/functions in base/number.c */
static Ruby String_swap32_f (Ruby rself) { int n = rb_str_len(rself)/4; swap32(n,Pt<uint32>((uint32 *)rb_str_ptr(rself),n)); return rself; }
static Ruby String_swap16_f (Ruby rself) { int n = rb_str_len(rself)/2; swap16(n,Pt<uint16>((uint16 *)rb_str_ptr(rself),n)); return rself; }
NumberTypeE NumberTypeE_find (Ruby sym) { if (TYPE(sym)!=T_SYMBOL) RAISE("expected symbol (not %s)", rb_str_ptr(rb_inspect(rb_obj_class(sym)))); Ruby nt_dict = rb_ivar_get(mGridFlow,SI(@number_type_dict)); Ruby v = rb_hash_aref(nt_dict,sym); if (v!=Qnil) return FIX2PTR(NumberType,v)->index; RAISE("unknown number type "%s"", rb_sym_name(sym)); }
/* **************************************************************** */ \class BitPacking < CObject
\def void initialize(Ruby foo1, Ruby foo2, Ruby foo3) {}
// !@#$ doesn't support number types \def String pack2 (String ins, String outs=Qnil) { int n = rb_str_len(ins) / sizeof(int32) / size; Pt<int32> in = Pt<int32>((int32 *)rb_str_ptr(ins),rb_str_len(ins)); int bytes2 = n*bytes; Ruby out = outs!=Qnil ? rb_str_resize(outs,bytes2) : rb_str_new("",bytes2); rb_str_modify(out); pack(n,Pt<int32>(in,n),Pt<uint8>((uint8 *)rb_str_ptr(out),bytes2)); return out; }
// !@#$ doesn't support number types \def String unpack2 (String ins, String outs=Qnil) { int n = rb_str_len(argv[0]) / bytes; Pt<uint8> in = Pt<uint8>((uint8 *)rb_str_ptr(ins),rb_str_len(ins)); int bytes2 = n*size*sizeof(int32); Ruby out = outs!=Qnil ? rb_str_resize(outs,bytes2) : rb_str_new("",bytes2); rb_str_modify(out); unpack(n,Pt<uint8>((uint8 *)in,bytes2),Pt<int32>((int32 *)rb_str_ptr(out),n)); return out; }
static Ruby BitPacking_s_new(Ruby argc, Ruby *argv, Ruby qlass) { Ruby keep = rb_ivar_get(mGridFlow, rb_intern("@fobjects")); if (argc!=3) RAISE("bad args"); if (TYPE(argv[2])!=T_ARRAY) RAISE("bad mask"); int endian = INT(argv[0]); int bytes = INT(argv[1]); Ruby *masks = rb_ary_ptr(argv[2]); uint32 masks2[4]; int size = rb_ary_len(argv[2]); if (size<1) RAISE("not enough masks"); if (size>4) RAISE("too many masks (%d)",size); for (int i=0; i<size; i++) masks2[i] = NUM2UINT(masks[i]); BitPacking *self = new BitPacking(endian,bytes,size,masks2); Ruby rself = Data_Wrap_Struct(qlass, 0, CObject_free, self); self->rself = rself; rb_hash_aset(keep,rself,Qtrue); // prevent sweeping (leak) (!@#$ WHAT???) rb_funcall2(rself,SI(initialize),argc,argv); return rself; }
\classinfo \end class BitPacking
void gfpost(const char *fmt, ...) { va_list args; int length; va_start(args,fmt); const int n=256; char post_s[n]; length = vsnprintf(post_s,n,fmt,args); if (length<0 || length>=n) sprintf(post_s+n-6,"[...]"); /* safety */ va_end(args); rb_funcall(mGridFlow,SI(gfpost2),2,rb_str_new2(fmt),rb_str_new2(post_s)); }
void define_many_methods(Ruby rself, int n, MethodDecl *methods) { for (int i=0; i<n; i++) { MethodDecl *md = &methods[i]; char *buf = strdup(md->selector); if (strlen(buf)>2 && strcmp(buf+strlen(buf)-2,"_m")==0) buf[strlen(buf)-2]=0; rb_define_method(rself,buf,(RMethod)md->method,-1); rb_enable_super(rself,buf); free(buf); } }
static Ruby GridFlow_fclass_install(Ruby rself_, Ruby fc_, Ruby super) { FClass *fc = FIX2PTR(FClass,fc_); Ruby rself = super!=Qnil ? rb_define_class_under(mGridFlow, fc->name, super) : rb_funcall(mGridFlow,SI(const_get),1,rb_str_new2(fc->name)); define_many_methods(rself,fc->methodsn,fc->methods); rb_ivar_set(rself,SI(@allocator),PTR2FIX((void*)(fc->allocator))); //#!@$?? if (fc->startup) fc->startup(rself); return Qnil; }
//---------------------------------------------------------------- // GridFlow.class //\class GridFlow_s < patate
typedef void (*Callback)(void*); static Ruby GridFlow_exec (Ruby rself, Ruby data, Ruby func) { void *data2 = FIX2PTR(void,data); Callback func2 = (Callback) FIX2PTR(void,func); func2(data2); return Qnil; }
static Ruby GridFlow_get_id (Ruby rself, Ruby arg) { fprintf(stderr,"%ld\n",arg); return INT2NUM((int)arg); }
Ruby GridFlow_rdtsc (Ruby rself) { return gf_ull2num(rdtsc()); }
/* This code handles nested lists because PureData (0.38) doesn't do it */ static Ruby GridFlow_handle_braces(Ruby rself, Ruby argv) { int stack[16]; int stackn=0; Ruby *av = rb_ary_ptr(argv); int ac = rb_ary_len(argv); int j=0; for (int i=0; i<ac; ) { int close=0; if (SYMBOL_P(av[i])) { const char *s = rb_sym_name(av[i]); while (*s=='(' || *s=='{') { if (stackn==16) RAISE("too many nested lists (>16)"); stack[stackn++]=j; s++; } const char *se = s+strlen(s); while (se[-1]==')' || se[-1]=='}') { se--; close++; } if (s!=se) { Ruby u = rb_str_new(s,se-s); av[j++] = rb_funcall(rself,SI(FloatOrSymbol),1,u); } } else { av[j++]=av[i]; } i++; while (close--) { if (!stackn) RAISE("unbalanced '}' or ')'",av[i]); Ruby a2 = rb_ary_new(); int j2 = stack[--stackn]; for (int k=j2; k<j; k++) rb_ary_push(a2,av[k]); j=j2; av[j++] = a2; } } if (stackn) RAISE("unbalanced '{' or '(' (stackn=%d)",stackn); RARRAY(argv)->len = j; return rself; }
/* ---------------------------------------------------------------- */
static uint32 memcpy_calls = 0; static uint64 memcpy_bytes = 0; static uint64 memcpy_time = 0; static uint32 malloc_calls = 0; /* only new not delete */ static uint64 malloc_bytes = 0; /* only new not delete */ static uint64 malloc_time = 0; /* in cpu ticks */
// don't touch. static void gfmemcopy32(int32 *as, int32 *bs, int n) { int32 ba = bs-as; #define FOO(I) as[I] = (as+ba)[I]; UNROLL_8(FOO,n,as) #undef FOO
}
void gfmemcopy(uint8 *out, const uint8 *in, int n) { uint64 t = rdtsc(); memcpy_calls++; memcpy_bytes+=n; for (; n>16; in+=16, out+=16, n-=16) { ((int32*)out)[0] = ((int32*)in)[0]; ((int32*)out)[1] = ((int32*)in)[1]; ((int32*)out)[2] = ((int32*)in)[2]; ((int32*)out)[3] = ((int32*)in)[3]; } for (; n>4; in+=4, out+=4, n-=4) { *(int32*)out = *(int32*)in; } for (; n; in++, out++, n--) { *out = *in; } t=rdtsc()-t; memcpy_time+=t; }
extern "C" { void *gfmalloc(size_t n) { uint64 t = rdtsc(); void *p = malloc(n); long align = (long)p & 7; if (align) fprintf(stderr,"malloc alignment = %ld mod 8\n",align); t=rdtsc()-t; malloc_time+=t; malloc_calls++; malloc_bytes+=n; return p; } void gffree(void *p) { uint64 t = rdtsc(); free(p); t=rdtsc()-t; malloc_time+=t; }};
Ruby GridFlow_memcpy_calls (Ruby rself) { return LONG2NUM(memcpy_calls); } Ruby GridFlow_memcpy_bytes (Ruby rself) { return gf_ull2num(memcpy_bytes); } Ruby GridFlow_memcpy_time (Ruby rself) { return gf_ull2num(memcpy_time); } Ruby GridFlow_malloc_calls (Ruby rself) { return LONG2NUM(malloc_calls); } Ruby GridFlow_malloc_bytes (Ruby rself) { return gf_ull2num(malloc_bytes); } Ruby GridFlow_malloc_time (Ruby rself) { return gf_ull2num(malloc_time); }
Ruby GridFlow_profiler_reset2 (Ruby rself) { memcpy_calls = memcpy_bytes = memcpy_time = 0; malloc_calls = malloc_bytes = malloc_time = 0; return Qnil; }
/* ---------------------------------------------------------------- */
void startup_number(); void startup_grid(); void startup_flow_objects(); void startup_flow_objects_for_image(); void startup_flow_objects_for_matrix();
Ruby cFormat;
#define SDEF(_class_,_name_,_argc_) \ rb_define_singleton_method(c##_class_,#_name_,(RMethod)_class_##_s_##_name_,_argc_) #define SDEF2(_name1_,_name2_,_argc_) \ rb_define_singleton_method(mGridFlow,_name1_,(RMethod)_name2_,_argc_)
STARTUP_LIST(void)
// Ruby's entrypoint. void Init_gridflow () { #define FOO(_sym_,_name_) bsym._sym_ = ID2SYM(rb_intern(_name_)); BUILTIN_SYMBOLS(FOO) #undef FOO signal(11,SIG_DFL); // paranoia mGridFlow = EVAL("module GridFlow; CObject = ::Object; " "class<<self; attr_reader :bridge_name; end; " "def post_string(s) STDERR.puts s end; " "self end"); SDEF2("exec",GridFlow_exec,2); SDEF2("get_id",GridFlow_get_id,1); SDEF2("rdtsc",GridFlow_rdtsc,0); SDEF2("profiler_reset2",GridFlow_profiler_reset2,0); SDEF2("memcpy_calls",GridFlow_memcpy_calls,0); SDEF2("memcpy_bytes",GridFlow_memcpy_bytes,0); SDEF2("memcpy_time", GridFlow_memcpy_time,0); SDEF2("malloc_calls",GridFlow_malloc_calls,0); SDEF2("malloc_bytes",GridFlow_malloc_bytes,0); SDEF2("malloc_time", GridFlow_malloc_time,0); SDEF2("handle_braces!",GridFlow_handle_braces,1); SDEF2("fclass_install",GridFlow_fclass_install,2);
//#define FOO(A) fprintf(stderr,"sizeof("#A")=%d\n",sizeof(A)); //FOO(Dim) FOO(BitPacking) FOO(GridHandler) FOO(GridInlet) FOO(GridOutlet) FOO(GridObject) //#undef FOO
rb_ivar_set(mGridFlow, SI(@fobjects), rb_hash_new()); rb_ivar_set(mGridFlow, SI(@fclasses), rb_hash_new()); rb_ivar_set(mGridFlow, SI(@bsym), PTR2FIX(&bsym)); rb_define_const(mGridFlow, "GF_VERSION", rb_str_new2(GF_VERSION)); rb_define_const(mGridFlow, "GF_COMPILE_TIME", rb_str_new2(GF_COMPILE_TIME));
cFObject = rb_define_class_under(mGridFlow, "FObject", rb_cObject); EVAL( \ruby module GridFlow class FObject def send_out2(*) end def self.install2(*) end def self.add_creator(name) name=name.to_str.dup GridFlow.fclasses[name]=self GridFlow.add_creator_2 name end end end \end ruby ); define_many_methods(cFObject,COUNT(FObject_methods),FObject_methods); SDEF(FObject, install, 3); SDEF(FObject, new, -1); ID gbi = SI(gf_bridge_init); if (rb_respond_to(rb_cData,gbi)) rb_funcall(rb_cData,gbi,0); Ruby cBitPacking = rb_define_class_under(mGridFlow, "BitPacking", rb_cObject); define_many_methods(cBitPacking, ciBitPacking.methodsn, ciBitPacking.methods); SDEF(BitPacking,new,-1); rb_define_method(rb_cString, "swap32!", (RMethod)String_swap32_f, 0); rb_define_method(rb_cString, "swap16!", (RMethod)String_swap16_f, 0);
startup_number(); startup_grid(); startup_flow_objects(); startup_flow_objects_for_image(); startup_flow_objects_for_matrix(); if (!EVAL("begin require 'gridflow/base/main.rb'; true\n" "rescue Exception => e; " "STDERR.puts "can't load: #{$!}\n" "backtrace: #{$!.backtrace.join"\n"}\n" "$: = #{$:.inspect}"\n; false end")) return; cFormat = EVAL("GridFlow::Format"); STARTUP_LIST() EVAL("h=GridFlow.fclasses; h['#io:window'] = h['#io:quartz']||h['#io:x11']||h['#io:sdl']"); EVAL("GridFlow.load_user_config"); signal(11,SIG_DFL); // paranoia }
void GFStack::push (FObject *o) { void *bp = &o; // really. just finding our position on the stack. if (n>=GF_STACK_MAX) RAISE("stack overflow (maximum %d FObject activations at once)", GF_STACK_MAX); uint64 t = rdtsc(); if (n) s[n-1].time = t - s[n-1].time; s[n].o = o; s[n].bp = bp; s[n].time = t; n++; }
void GFStack::pop () { uint64 t = rdtsc(); if (!n) RAISE("stack underflow (WHAT?)"); n--; if (s[n].o) s[n].o->total_time += t - s[n].time; if (n) s[n-1].time = t - s[n-1].time; }
uint64 gf_timeofday () { timeval t; gettimeofday(&t,0); return t.tv_sec*1000000+t.tv_usec; }
--- NEW FILE: grid.c --- /* $Id: grid.c,v 1.1 2005/10/04 02:02:13 matju Exp $
GridFlow Copyright (c) 2001,2002,2003,2004 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 "grid.h.fcs" #include <ctype.h>
/* copied from bridge/puredata.c (sorry: linkage issue) */ struct Pointer : CObject { void *p; Pointer(void *_p) : p(_p) {}}; Ruby Pointer_s_noo (void *ptr) { return Data_Wrap_Struct(EVAL("GridFlow::Pointer"), 0, 0, new Pointer(ptr));} static void *Pointer_gut (Ruby rself) {DGS(Pointer); return self->p;}
//#define TRACE fprintf(stderr,"%s %s [%s:%d]\n",INFO(parent),__PRETTY_FUNCTION__,__FILE__,__LINE__);assert(this); #define TRACE assert(this);
#define CHECK_TYPE(d) \ if (NumberTypeE_type_of(d)!=this->nt) RAISE("%s(%s): " \ "type mismatch during transmission (got %s expecting %s)", \ INFO(parent), \ __PRETTY_FUNCTION__, \ number_type_table[NumberTypeE_type_of(d)].name, \ number_type_table[this->nt].name);
#define CHECK_BUSY(s) \ if (!dim) RAISE("%s: " #s " not busy",INFO(parent));
#define CHECK_ALIGN(d) \ {int bytes = number_type_table[nt].size/8; \ int align = ((long)(void*)d)%bytes; \ if (align) {L;gfpost("%s(%s): Alignment Warning: %p is not %d-aligned: %d", \ INFO(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;gfpost("Alignment Warning: %p is not %d-aligned: %d", \ (void*)d,bytes,align);}}
// **************** Grid ******************************************
#define FOO(S) static inline void NUM(Ruby x, S &y) {y=convert(x,(int32*)0);} EACH_INT_TYPE(FOO) #undef FOO
#define FOO(S) \ static inline void NUM(Ruby x, S &y) { \ if (TYPE(x)==T_FLOAT) y = RFLOAT(x)->value; \ else if (INTEGER_P(x)) y = convert(x,(S*)0); \ else RAISE("expected Float (or at least Integer)");} EACH_FLOAT_TYPE(FOO) #undef FOO
void Grid::init_from_ruby_list(int n, Ruby *a, NumberTypeE nt) { Ruby delim = SYM(#); for (int i=0; i<n; i++) { if (a[i] == delim) { STACK_ARRAY(int32,v,i); if (i!=0 && TYPE(a[i-1])==T_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 && TYPE(a[0])==T_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(type) { \ Pt<type> p = (Pt<type>)*this; \ if (n==0) CLEAR(p,nn); \ else { \ for (int i=0; i<n; i++) NUM(a[i],p[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_ruby(Ruby x) { if (TYPE(x)==T_ARRAY) { init_from_ruby_list(rb_ary_len(x),rb_ary_ptr(x)); } else if (INTEGER_P(x) || FLOAT_P(x)) { init(new Dim(),int32_e); CHECK_ALIGN2(this->data,nt); ((Pt<int32>)*this)[0] = INT(x); } else { rb_funcall( EVAL("proc{|x| raise "can't convert to grid: #{x.inspect}"}"), SI(call),1,x); } }
// **************** 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(int factor) { if(!dim) RAISE("huh?"); if(factor<=0) RAISE("%s: factor=%d should be >= 1",INFO(parent),factor); if (dim->prod() && dim->prod() % factor) RAISE("%s: set_factor: expecting divisor",INFO(parent)); if (factor > 1) { buf=new Grid(new Dim(factor), nt); bufi=0; } else { buf=0; } }
static Ruby GridInlet_begin_1(GridInlet *self) { #define FOO(T) self->gh->flow(self,-1,Pt<T>()); break; TYPESWITCH(self->nt,FOO,) #undef FOO return Qnil; }
static Ruby GridInlet_begin_2(GridInlet *self) { self->dim = 0; // hack return (Ruby) 0; }
bool GridInlet::supports_type(NumberTypeE nt) { #define FOO(T) return !! gh->flow_##T; TYPESWITCH(nt,FOO,return false) #undef FOO }
Ruby GridInlet::begin(int argc, Ruby *argv) {TRACE; if (!argc) return PTR2FIX(this); GridOutlet *back_out = (GridOutlet *) Pointer_gut(argv[0]); nt = (NumberTypeE) INT(argv[1]); argc-=2, argv+=2; PROF(parent) { if (dim) { gfpost("%s: grid inlet conflict; aborting %s in favour of %s", INFO(parent), INFO(sender), INFO(back_out->parent)); abort(); } sender = back_out->parent; if ((int)nt<0 || (int)nt>=(int)number_type_table_end) RAISE("%s: inlet: unknown number type",INFO(parent)); if (!supports_type(nt)) RAISE("%s: number type %s not supported here", INFO(parent), number_type_table[nt].name); STACK_ARRAY(int32,v,argc); for (int i=0; i<argc; i++) v[i] = NUM2INT(argv[i]); P<Dim> dim = this->dim = new Dim(argc,v); dex=0; buf=0; int r = rb_ensure( (RMethod)GridInlet_begin_1,(Ruby)this, (RMethod)GridInlet_begin_2,(Ruby)this); if (!r) {abort(); goto hell;} this->dim = dim; back_out->callback(this); hell:;} // PROF return Qnil; }
template <class T> void GridInlet::flow(int mode, int n, Pt<T> data) {TRACE; CHECK_BUSY(inlet); CHECK_TYPE(*data); CHECK_ALIGN(data); PROF(parent) { if (this->mode==0) {dex += n; return;} // ignore data if (n==0) return; // no data switch(mode) { case 4:{ int d = dex + bufi; if (d+n > dim->prod()) { gfpost("grid input overflow: %d of %d from [%s] to [%s]", d+n, dim->prod(), INFO(sender), 0); n = dim->prod() - d; if (n<=0) return; } int bufn = factor(); if (buf && bufi) { Pt<T> bufd = (Pt<T>)*buf; int k = min(n,bufn-bufi); COPY(bufd+bufi,data,k); bufi+=k; data+=k; n-=k; if (bufi==bufn) { int newdex = dex+bufn; if (this->mode==6) { Pt<T> data2 = ARRAY_NEW(T,bufn); COPY(data2,bufd,bufn); CHECK_ALIGN(data2); gh->flow(this,bufn,data2); } else { CHECK_ALIGN(bufd); gh->flow(this,bufn,bufd); } dex = newdex; bufi = 0; } } int m = (n/bufn)*bufn; if (m) { int newdex = dex + m; if (this->mode==6) { Pt<T> data2 = ARRAY_NEW(T,m); COPY(data2,data,m); CHECK_ALIGN(data2); gh->flow(this,m,data2); } else { gh->flow(this,m,data); } dex = newdex; } data += m; n -= m; if (buf && n>0) COPY((Pt<T>)*buf+bufi,data,n), bufi+=n; }break; case 6:{ assert(!buf); int newdex = dex + n; gh->flow(this,n,data); if (this->mode==4) delete[] (T *)data; dex = newdex; }break; case 0: break; // ignore data default: RAISE("%s: unknown inlet mode",INFO(parent)); }} // PROF }
void GridInlet::end() {TRACE; assert(this); if (!dim) RAISE("%s: inlet not busy",INFO(parent)); if (dim->prod() != dex) { gfpost("incomplete grid: %d of %d from [%s] to [%s]", dex, dim->prod(), INFO(sender), INFO(parent)); } PROF(parent) { #define FOO(T) gh->flow(this,-2,Pt<T>()); TYPESWITCH(nt,FOO,) #undef FOO } // PROF 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,Pt<T>()); if (n>0 && this->mode!=0) { Pt<T> data = (Pt<T>)*g; CHECK_ALIGN(data); int size = g->dim->prod(); if (this->mode==6) { Pt<T> d = data; data = ARRAY_NEW(T,size); // problem with int64,float64 here. COPY(data,d,size); CHECK_ALIGN(data); gh->flow(this,n,data); } 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); gh->flow(this,m,data); data+=m; n-=m; dex+=m; } } } gh->flow(this,-2,Pt<T>()); //!@#$ add error handling. // rescue; abort(); end ??? dim = 0; dex = 0; }
void GridInlet::from_grid(Grid *g) {TRACE; if (!supports_type(g->nt)) RAISE("%s: number type %s not supported here", INFO(parent), number_type_table[g->nt].name); #define FOO(T) from_grid2(g,(T)0); TYPESWITCH(g->nt,FOO,) #undef FOO }
/* **************** GridOutlet ************************************ */
void GridOutlet::begin(int woutlet, P<Dim> dim, NumberTypeE nt) {TRACE; int n = dim->count(); this->nt = nt; this->dim = dim; Ruby a[n+4]; a[0] = INT2NUM(woutlet); a[1] = bsym._grid; a[2] = Pointer_s_noo(this); a[3] = INT2NUM(nt); for(int i=0; i<n; i++) a[4+i] = INT2NUM(dim->get(i)); parent->send_out(COUNT(a),a); frozen=true; if (!dim->prod()) {end(); return;} int32 lcm_factor = 1; for (uint32 i=0; i<inlets.size(); i++) lcm_factor = lcm(lcm_factor,inlets[i]->factor()); if (nt != buf->nt) { // 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(int n, Pt<T> data) {TRACE; assert(data); assert(frozen); CHECK_BUSY(outlet); CHECK_TYPE(*data); CHECK_ALIGN(data); for (; n>0; ) { int pn = min(n,MAX_PACKET_SIZE); for (uint32 i=0; i<inlets.size(); i++) inlets[i]->flow(4,pn,data); data+=pn, n-=pn; } }
void GridOutlet::flush() {TRACE; if (!bufi) return; #define FOO(T) send_direct(bufi,(Pt<T>)*buf); TYPESWITCH(buf->nt,FOO,) #undef FOO bufi = 0; }
template <class T, class S> static void convert_number_type(int n, Pt<T> out, Pt<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(int n, Pt<T> data) {TRACE; assert(data); assert(frozen); if (!n) return; CHECK_BUSY(outlet); CHECK_ALIGN(data); if (NumberTypeE_type_of(*data)!=nt) { int bs = MAX_PACKET_SIZE; #define FOO(T) { \ STACK_ARRAY(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; assert(dex <= dim->prod()); if (n > MIN_PACKET_SIZE || bufi + n > MAX_PACKET_SIZE) flush(); if (n > MIN_PACKET_SIZE) { send_direct(n,data); } else { COPY((Pt<T>)*buf+bufi,data,n); bufi += n; } if (dex==dim->prod()) end(); } }
template <class T> void GridOutlet::give(int n, Pt<T> data) {TRACE; assert(data); CHECK_BUSY(outlet); assert(frozen); assert(dex+n <= dim->prod()); CHECK_ALIGN(data); if (NumberTypeE_type_of(*data)!=nt) { send(n,data); delete[] (T *)data; return; } if (inlets.size()==1 && inlets[0]->mode == 6) { // this is the copyless buffer passing flush(); inlets[0]->flow(6,n,data); dex += n; } else { flush(); send_direct(n,data); dex += n; delete[] (T *)data; } if (dex==dim->prod()) end(); }
void GridOutlet::callback(GridInlet *in) {TRACE; CHECK_BUSY(outlet); assert(!frozen); int mode = in->mode; assert(mode==6 || mode==4 || mode==0); inlets.push_back(in); }
\class GridObject < FObject
//!@#$ does not handle types properly //!@#$ most possibly a big hack template <class T> void GridObject_r_flow(GridInlet *in, int n, Pt<T> data) { GridObject *self = in->parent; uint32 i; for (i=0; i<self->in.size(); i++) if (in==self->in[i].p) break; if (i==self->in.size()) RAISE("inlet not found?"); if (n==-1) { rb_funcall(self->rself,SI(send_in),2,INT2NUM(i),SYM(rgrid_begin)); } else if (n>=0) { Ruby buf = rb_str_new((char *)((uint8 *)data),n*sizeof(T)); rb_funcall(self->rself,SI(send_in),3,INT2NUM(i),SYM(rgrid_flow),buf); } else { rb_funcall(self->rself,SI(send_in),2,INT2NUM(i),SYM(rgrid_end)); } }
\def Symbol inlet_nt (int inln) { if (inln<0 || inln>=(int)in.size()) RAISE("bad inlet number"); P<GridInlet> inl = in[inln]; if (!inl) RAISE("no such inlet #%d",inln); if (!inl->dim) return Qnil; return number_type_table[inl->nt].sym; }
\def Array inlet_dim (int inln) { if (inln<0 || inln>=(int)in.size()) RAISE("bad inlet number"); P<GridInlet> inl = in[inln]; if (!inl) RAISE("no such inlet #%d",inln); if (!inl->dim) return Qnil; int n=inl->dim->count(); Ruby a = rb_ary_new2(n); for(int i=0; i<n; i++) rb_ary_push(a,INT2NUM(inl->dim->v[i])); return a; }
\def void inlet_set_factor (int inln, int factor) { if (inln<0 || inln>=(int)in.size()) RAISE("bad inlet number"); P<GridInlet> inl = in[inln]; if (!inl) RAISE("no such inlet #%d",inln); if (!inl->dim) RAISE("inlet #%d not active",inln); inl->set_factor(factor); }
\def void send_out_grid_begin (int outlet, Array buf, NumberTypeE nt=int32_e) { if (outlet<0) RAISE("bad outlet number"); int n = rb_ary_len(buf); Ruby *p = rb_ary_ptr(buf); STACK_ARRAY(int32,v,n); for (int i=0; i<n; i++) v[i] = convert(p[i],(int32*)0); out = new GridOutlet(this,outlet,new Dim(n,v),nt); // valgrind says leak? }
template <class T> void send_out_grid_flow_2(P<GridOutlet> go, Ruby s, T bogus) { int n = rb_str_len(s) / sizeof(T); Pt<T> p = rb_str_pt(s,T); go->send(n,p); }
\def void send_out_grid_flow (int outlet, String buf, NumberTypeE nt=int32_e) { if (outlet<0) RAISE("bad outlet number"); #define FOO(T) send_out_grid_flow_2(out,argv[1],(T)0); TYPESWITCH(nt,FOO,) #undef FOO }
// install_rgrid(Integer inlet, Boolean multi_type? = true) static Ruby GridObject_s_install_rgrid(int argc, Ruby *argv, Ruby rself) { if (argc<1 || argc>2) RAISE("er..."); IEVAL(rself,"@handlers||=[]"); Ruby handlers = rb_ivar_get(rself,SI(@handlers)); GridHandler *gh = new GridHandler; bool mt = argc>1 ? argv[1]==Qtrue : 0; /* multi_type? */ if (mt) { #define FOO(S) gh->flow_##S = GridObject_r_flow; EACH_NUMBER_TYPE(FOO) #undef FOO } else { #define FOO(S) gh->flow_##S = 0; EACH_NUMBER_TYPE(FOO) #undef FOO } gh->flow_int32 = GridObject_r_flow; //IEVAL(rself,"self.class_eval { def _0_grid(*a) ___grid(0,*a) end }"); rb_funcall(handlers,SI([]=),2,INT2NUM(INT(argv[0])),PTR2FIX(gh)); return Qnil; }
static Ruby GridObject_s_instance_methods(int argc, Ruby *argv, Ruby rself) { static const char *names[] = {"grid","list","float"}; Ruby list = rb_class_instance_methods(argc,argv,rself); Ruby handlers = rb_ivar_get(rself,SI(@handlers)); if (handlers==Qnil) return list; for (int i=0; i<rb_ary_len(handlers); i++) { Ruby ghp = rb_ary_ptr(handlers)[i]; if (ghp==Qnil) continue; GridHandler *gh = FIX2PTR(GridHandler,ghp); char buf[256]; for (int j=0; j<COUNT(names); j++) { sprintf(buf,"_%d_%s",i,names[j]); rb_ary_push(list,rb_str_new2(buf)); } } return list; }
// this does auto-conversion of list/float to grid // this also (will) do grid inputs for ruby stuff. \def Ruby method_missing (...) { { if (argc<1) RAISE("not enough arguments"); if (!SYMBOL_P(argv[0])) RAISE("expected symbol"); const char *name = rb_sym_name(argv[0]); char *endp; if (*name++!='_') goto hell; int i = strtol(name,&endp,10); if (name==endp) goto hell; if (*endp++!='_') goto hell; if (strcmp(endp,"grid")==0) { Ruby handlers = rb_ivar_get(rb_obj_class(rself),SI(@handlers)); if (TYPE(handlers)!=T_ARRAY) { rb_p(handlers); RAISE("gridhandler-list missing (maybe forgot install_rgrid ?)" " while trying to receive on inlet %d",i); } if (i>=rb_ary_len(handlers)) RAISE("BORK"); GridHandler *gh = FIX2PTR(GridHandler, rb_ary_ptr(handlers)[i]); if (in.size()<=(uint32)i) in.resize(i+1); if (!in[i]) in[i]=new GridInlet((GridObject *)this,gh); return in[i]->begin(argc-1,argv+1); } // we call the grid method recursively to ask it its GridInlet* // don't do this before checking the missing method is exactly that =) char foo[42]; sprintf(foo,"_%d_grid",i); P<GridInlet> inl = FIX2PTR(GridInlet,rb_funcall(rself,rb_intern(foo),0)); if (strcmp(endp,"list" )==0) return inl->from_ruby_list(argc-1,argv+1), Qnil; if (strcmp(endp,"float")==0) return inl->from_ruby (argc-1,argv+1), Qnil; } hell: return rb_call_super(argc,argv); }
\classinfo { IEVAL(rself,"install 'GridObject',0,0"); // define in Ruby-metaclass rb_define_singleton_method(rself,"instance_methods",(RMethod)GridObject_s_instance_methods,-1); rb_define_singleton_method(rself,"install_rgrid",(RMethod)GridObject_s_install_rgrid,-1); rb_enable_super(rb_singleton_class(rself),"instance_methods"); } \end class GridObject
Ruby cGridObject;
void startup_grid () { \startall cGridObject = rb_const_get(mGridFlow,SI(GridObject)); }
// 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,Pt<S>()); EACH_NUMBER_TYPE(FOO) #undef FOO }