[PD-cvs] externals/gridflow/base bitpacking.c, NONE, 1.1 flow_objects.c, NONE, 1.1 flow_objects.rb, NONE, 1.1 flow_objects_for_image.c, NONE, 1.1 flow_objects_for_matrix.c, NONE, 1.1 grid.c, NONE, 1.1 grid.h, NONE, 1.1 main.c, NONE, 1.1 main.rb, NONE, 1.1 number.c, NONE, 1.1 source_filter.rb, NONE, 1.1 test.rb, NONE, 1.1
Mathieu Bouchard
matju at users.sourceforge.net
Tue Oct 4 04:02:15 CEST 2005
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+ at ninlets
else
@fobjects[a].connect b, at 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- at 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
}
More information about the Pd-cvs
mailing list