| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 4 |  |  | 4 |  | 2185 | use strict; | 
|  | 4 |  |  |  |  | 4 |  | 
|  | 4 |  |  |  |  | 86 |  | 
| 2 | 4 |  |  | 4 |  | 10 | use warnings; | 
|  | 4 |  |  |  |  | 4 |  | 
|  | 4 |  |  |  |  | 105 |  | 
| 3 | 4 |  |  | 4 |  | 1471 | no if $] >= 5.017011, warnings => 'experimental::smartmatch'; | 
|  | 4 |  |  |  |  | 22 |  | 
|  | 4 |  |  |  |  | 19 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | ############################################################################ | 
| 6 |  |  |  |  |  |  | # BEWARE! complex stuff! | 
| 7 |  |  |  |  |  |  | # to aid with debugging problems: | 
| 8 |  |  |  |  |  |  | # - switch on debug mode | 
| 9 |  |  |  |  |  |  | # - to see whats going on in direction dir part p: | 
| 10 |  |  |  |  |  |  | #   grep for '[dir][p]' | 
| 11 |  |  |  |  |  |  | # - to see whats transfering out of direction dir part p into next part/up: | 
| 12 |  |  |  |  |  |  | #   grep for '[dir][p>' | 
| 13 |  |  |  |  |  |  | # | 
| 14 |  |  |  |  |  |  | # basic design | 
| 15 |  |  |  |  |  |  | # - we do not have lots of member variables, instead we put everything into | 
| 16 |  |  |  |  |  |  | #   new_analyzer as normal variables and declare various $sub = sub ... which | 
| 17 |  |  |  |  |  |  | #   use these variables. Thus the variables get bound once to the sub and we | 
| 18 |  |  |  |  |  |  | #   don't need to access it with $self->{field}... or so all the time | 
| 19 |  |  |  |  |  |  | # - subs and data structures are described in new_analyzer, the most important | 
| 20 |  |  |  |  |  |  | #   are | 
| 21 |  |  |  |  |  |  | #   - $global_in - this is the sub data method | 
| 22 |  |  |  |  |  |  | #   - $part_in   - called from global_in, itself and callbacks to put data | 
| 23 |  |  |  |  |  |  | #     into a specific part. Feeds the data in the associated analyzer | 
| 24 |  |  |  |  |  |  | #   - $imp_callback - callback for the analyzer of a specific part | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | ############################################################################ | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | package Net::IMP::Cascade; | 
| 29 | 4 |  |  | 4 |  | 263 | use base 'Net::IMP::Base'; | 
|  | 4 |  |  |  |  | 3 |  | 
|  | 4 |  |  |  |  | 1098 |  | 
| 30 |  |  |  |  |  |  | use fields ( | 
| 31 | 4 |  |  |  |  | 15 | 'parts',    # analyzer objects | 
| 32 |  |  |  |  |  |  | # we do everything with closures inside new_analyzer here, so that the | 
| 33 |  |  |  |  |  |  | # object has only fields for accessing some closures from subs | 
| 34 |  |  |  |  |  |  | 'dataf',    # called from sub data | 
| 35 |  |  |  |  |  |  | 'closef',   # called from DESTROY | 
| 36 | 4 |  |  | 4 |  | 18 | ); | 
|  | 4 |  |  |  |  | 5 |  | 
| 37 |  |  |  |  |  |  |  | 
| 38 | 4 |  |  | 4 |  | 186 | use Net::IMP; # constants | 
|  | 4 |  |  |  |  | 5 |  | 
|  | 4 |  |  |  |  | 309 |  | 
| 39 | 4 |  |  | 4 |  | 13 | use Carp 'croak'; | 
|  | 4 |  |  |  |  | 4 |  | 
|  | 4 |  |  |  |  | 129 |  | 
| 40 | 4 |  |  | 4 |  | 12 | use Scalar::Util 'weaken'; | 
|  | 4 |  |  |  |  | 4 |  | 
|  | 4 |  |  |  |  | 152 |  | 
| 41 | 4 |  |  | 4 |  | 1300 | use Hash::Util qw(lock_ref_keys); | 
|  | 4 |  |  |  |  | 6061 |  | 
|  | 4 |  |  |  |  | 21 |  | 
| 42 | 4 |  |  | 4 |  | 277 | use Net::IMP::Debug; | 
|  | 4 |  |  |  |  | 6 |  | 
|  | 4 |  |  |  |  | 20 |  | 
| 43 | 4 |  |  | 4 |  | 1812 | use Data::Dumper; | 
|  | 4 |  |  |  |  | 24303 |  | 
|  | 4 |  |  |  |  | 16655 |  | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | my %rtypes_implemented_myself = map { $_ => 1 } ( | 
| 46 |  |  |  |  |  |  | IMP_PASS, | 
| 47 |  |  |  |  |  |  | IMP_PREPASS, | 
| 48 |  |  |  |  |  |  | IMP_REPLACE, | 
| 49 |  |  |  |  |  |  | IMP_REPLACE_LATER, | 
| 50 |  |  |  |  |  |  | IMP_DENY, | 
| 51 |  |  |  |  |  |  | IMP_DROP, | 
| 52 |  |  |  |  |  |  | #IMP_TOSENDER, # not supported yet | 
| 53 |  |  |  |  |  |  | IMP_LOG, | 
| 54 |  |  |  |  |  |  | IMP_ACCTFIELD, | 
| 55 |  |  |  |  |  |  | IMP_FATAL, | 
| 56 |  |  |  |  |  |  | ); | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | sub get_interface { | 
| 59 | 0 |  |  | 0 | 1 | 0 | my Net::IMP::Cascade $factory = shift; | 
| 60 | 0 |  |  |  |  | 0 | my $parts = $factory->{factory_args}{parts}; | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | # collect interfaces by part | 
| 63 | 0 |  |  |  |  | 0 | my @if4part; | 
| 64 | 0 |  |  |  |  | 0 | for my $p ( @$parts ) { | 
| 65 | 0 |  |  |  |  | 0 | my @if; | 
| 66 | 0 |  |  |  |  | 0 | for my $if ( $p->get_interface(@_)) { | 
| 67 |  |  |  |  |  |  | # $if should require only return types I support | 
| 68 |  |  |  |  |  |  | push @if,$if | 
| 69 | 0 | 0 |  |  |  | 0 | if ! grep { ! $rtypes_implemented_myself{$_} } @{ $if->[1] }; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 70 |  |  |  |  |  |  | } | 
| 71 | 0 | 0 |  |  |  | 0 | @if or return; # nothing in common | 
| 72 | 0 |  |  |  |  | 0 | push @if4part,\@if | 
| 73 |  |  |  |  |  |  | } | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | # find interfaces which are supported by all parts | 
| 76 | 0 |  |  |  |  | 0 | my @common; | 
| 77 | 0 |  |  |  |  | 0 | for( my $i=0;$i<@if4part;$i++ ) { | 
| 78 | 0 |  |  |  |  | 0 | for my $if_i ( @{ $if4part[$i] } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 79 | 0 |  |  |  |  | 0 | my ($in_i,$out_i) = @$if_i; | 
| 80 |  |  |  |  |  |  | # check if $if_i matches at least on interface description in | 
| 81 |  |  |  |  |  |  | # all other parts, e.g. $if_i is same or included in $if_k | 
| 82 |  |  |  |  |  |  | # - data type/proto: $in_k should be undef or same as $in_i | 
| 83 |  |  |  |  |  |  | # - return types: $out_i should include $out_k | 
| 84 | 0 |  |  |  |  | 0 | for( my $k=0;$k<@if4part;$k++ ) { | 
| 85 | 0 | 0 |  |  |  | 0 | next if $i == $k; # same | 
| 86 | 0 |  |  |  |  | 0 | for my $if_k ( @{  $if4part[$k] } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 87 | 0 |  |  |  |  | 0 | my ($in_k,$out_k) = @$if_k; | 
| 88 |  |  |  |  |  |  | # should be same data type or $in_k undef | 
| 89 | 0 | 0 | 0 |  |  | 0 | next if $in_k and ( ! $in_i or $in_k != $in_i ); | 
|  |  |  | 0 |  |  |  |  | 
| 90 |  |  |  |  |  |  | # $out_i should include all of $out_k | 
| 91 | 0 |  |  |  |  | 0 | my %out_k = map { $_ => 1 } @$out_k; | 
|  | 0 |  |  |  |  | 0 |  | 
| 92 | 0 |  |  |  |  | 0 | delete @out_k{ @$out_i }; | 
| 93 | 0 | 0 |  |  |  | 0 | next if %out_k; # some in k are not in i | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | # junction if i and k | 
| 96 | 0 |  |  |  |  | 0 | push @common,[ $in_k,$out_i ]; | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  | } | 
| 99 |  |  |  |  |  |  | } | 
| 100 |  |  |  |  |  |  | } | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | # remove duplicates from match | 
| 103 | 0 |  |  |  |  | 0 | my (@uniq,%m); | 
| 104 | 0 |  |  |  |  | 0 | for( @common ) { | 
| 105 | 0 |  | 0 |  |  | 0 | my $key = ( $_->[0] // '' )."\0".join("\0",sort @{$_->[1]}); | 
|  | 0 |  |  |  |  | 0 |  | 
| 106 | 0 | 0 |  |  |  | 0 | push @uniq,$_ if ! $m{$key}++; | 
| 107 |  |  |  |  |  |  | } | 
| 108 | 0 |  |  |  |  | 0 | return @uniq; | 
| 109 |  |  |  |  |  |  | } | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | sub set_interface { | 
| 112 | 0 |  |  | 0 | 1 | 0 | my Net::IMP::Cascade $factory = shift; | 
| 113 | 0 |  |  |  |  | 0 | my @if = @_; | 
| 114 | 0 |  |  |  |  | 0 | my $parts = $factory->{factory_args}{parts}; | 
| 115 |  |  |  |  |  |  |  | 
| 116 | 0 |  |  |  |  | 0 | my @new_parts; | 
| 117 | 0 |  |  |  |  | 0 | for(my $i=0;$i<@$parts;$i++) { | 
| 118 | 0 | 0 |  |  |  | 0 | my $np = $parts->[$i]->set_interface(@if) | 
| 119 |  |  |  |  |  |  | or return; # cannot use interface | 
| 120 | 0 | 0 |  |  |  | 0 | $np == $parts->[$i] and next; # no change of part | 
| 121 | 0 |  |  |  |  | 0 | $new_parts[$i] = $np; # got new factory for part | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 0 | 0 |  |  |  | 0 | return $factory if ! @new_parts; # interface supported by original factory | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | # some parts changed, create new factory for this cascade | 
| 127 | 0 |  |  |  |  | 0 | for(my $i=0;$i<@$parts;$i++) { | 
| 128 | 0 |  | 0 |  |  | 0 | $new_parts[$i] ||= $parts->[$i]; # copy parts which did not change | 
| 129 |  |  |  |  |  |  | } | 
| 130 |  |  |  |  |  |  |  | 
| 131 | 0 |  |  |  |  | 0 | return ref($factory)->new_factory( parts => \@new_parts ); | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | sub new_analyzer { | 
| 135 | 11 |  |  | 11 | 1 | 30 | my ($factory,%args) = @_; | 
| 136 |  |  |  |  |  |  |  | 
| 137 | 11 |  |  |  |  | 15 | my $p     = $factory->{factory_args}{parts}; | 
| 138 | 11 |  |  |  |  | 29 | my $self  = $factory->SUPER::new_analyzer(%args); | 
| 139 | 11 |  |  |  |  | 13 | my @imp = map { $_->new_analyzer(%args) } @$p; | 
|  | 21 |  |  |  |  | 43 |  | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | # $parts[$dir][$pi] is the part for direction $dir, analyzer $pi | 
| 142 |  |  |  |  |  |  | # if part is optimized away due to IMP_PASS with IMP_MAXOFFSET | 
| 143 |  |  |  |  |  |  | # $parts[$dir][$pi] contains instead an integer for adjustments | 
| 144 |  |  |  |  |  |  | # from this part | 
| 145 | 11 |  |  |  |  | 18 | my @parts; | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | # pause/continue handling | 
| 148 |  |  |  |  |  |  | # maintains pause status per part | 
| 149 |  |  |  |  |  |  | my @pause; | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | # to make sure we don't leak due to cross-references | 
| 152 | 11 |  |  |  |  | 21 | weaken( my $wself = $self ); | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | my $new_buf = sub { | 
| 155 | 160 |  |  | 160 |  | 691 | lock_ref_keys( my $buf = { | 
| 156 |  |  |  |  |  |  | start   => 0,  # start of buf relativ to part | 
| 157 |  |  |  |  |  |  | end     => 0,  # end of buf relativ to part | 
| 158 |  |  |  |  |  |  | data    => '', # data or undef for replace_later | 
| 159 |  |  |  |  |  |  | dtype   => 0,  # data type | 
| 160 |  |  |  |  |  |  | rtype   => IMP_PASS,  # IMP_PASS < IMP_PREPASS < IMP_REPLACE | 
| 161 |  |  |  |  |  |  | gap     => 0,  # size of gap before buf? | 
| 162 |  |  |  |  |  |  | gstart  => 0,  # start of buf relativ to cascade | 
| 163 |  |  |  |  |  |  | gend    => 0,  # end of buf relativ to cascade | 
| 164 |  |  |  |  |  |  | eof     => 0   # flag if last buf in this direction | 
| 165 |  |  |  |  |  |  | }); | 
| 166 | 160 | 100 |  |  |  | 1566 | %$buf = ( %$buf, @_ ) if @_; | 
| 167 | 160 |  |  |  |  | 399 | return $buf; | 
| 168 | 11 |  |  |  |  | 28 | }; | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | my $new_part = sub { | 
| 171 | 42 |  |  | 42 |  | 45 | lock_ref_keys( my $p = { | 
| 172 |  |  |  |  |  |  | ibuf => [ &$new_buf ], # buffers, at least one | 
| 173 |  |  |  |  |  |  | pass          => 0,    # can pass up to .. | 
| 174 |  |  |  |  |  |  | prepass       => 0,    # can prepass up to .. | 
| 175 |  |  |  |  |  |  | replace_later => 0,    # will replace_later up to .. | 
| 176 |  |  |  |  |  |  | adjust        => 0,    # total local adjustments from forwarded bufs | 
| 177 |  |  |  |  |  |  | }); | 
| 178 | 42 |  |  |  |  | 202 | return $p; | 
| 179 | 11 |  |  |  |  | 24 | }; | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | # initialize @parts | 
| 182 | 11 |  |  |  |  | 27 | for( my $i=0;$i<@imp;$i++ ) { | 
| 183 | 21 |  |  |  |  | 23 | $parts[0][$i] = $new_part->();       # client -> server, flow 0>1>2>.. | 
| 184 | 21 |  |  |  |  | 23 | $parts[1][$#imp-$i] = $new_part->(); # server -> client, flow 9>8>7>.. | 
| 185 |  |  |  |  |  |  | } | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | my $dump_bufs = sub { | 
| 188 | 0 |  |  | 0 |  | 0 | my $bufs = shift; | 
| 189 | 0 |  |  |  |  | 0 | my @out; | 
| 190 | 0 | 0 |  |  |  | 0 | for my $i (@_ ? @_: 0..$#$bufs) { | 
| 191 | 0 |  |  |  |  | 0 | my $buf = $bufs->[$i]; | 
| 192 | 0 | 0 |  |  |  | 0 | my $str = ! defined( $buf->{data} ) ? '' : do { | 
| 193 | 0 |  |  |  |  | 0 | local $_ = $buf->{data}; | 
| 194 | 0 | 0 |  |  |  | 0 | $_ = substr($_,0,27).'...' if length($_)>30; | 
| 195 | 0 |  |  |  |  | 0 | s{([\\\n\r\t[^:print:]])}{ sprintf("\\%03o",ord($1)) }esg; | 
|  | 0 |  |  |  |  | 0 |  | 
| 196 | 0 |  |  |  |  | 0 | $_ | 
| 197 |  |  |  |  |  |  | }; | 
| 198 |  |  |  |  |  |  | push @out, sprintf("#%02d %d..%d%s%s%s %s %s [%d,%d] '%s'", | 
| 199 |  |  |  |  |  |  | $i, | 
| 200 |  |  |  |  |  |  | $buf->{start},$buf->{end}, $buf->{eof} ? '$':'', | 
| 201 |  |  |  |  |  |  | $buf->{gap} ? " +$buf->{gap}":"", | 
| 202 |  |  |  |  |  |  | defined($buf->{data}) ? '':' RL', | 
| 203 |  |  |  |  |  |  | $buf->{dtype},$buf->{rtype}, | 
| 204 |  |  |  |  |  |  | $buf->{gstart},$buf->{gend}, | 
| 205 | 0 | 0 |  |  |  | 0 | $str | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | ); | 
| 207 |  |  |  |  |  |  | } | 
| 208 | 0 |  |  |  |  | 0 | return join("\n",@out); | 
| 209 | 11 |  |  |  |  | 41 | }; | 
| 210 |  |  |  |  |  |  | my $dump_parts = sub { | 
| 211 | 0 |  |  | 0 |  | 0 | my $dir = shift; | 
| 212 | 0 |  |  |  |  | 0 | my $out = ''; | 
| 213 | 0 | 0 |  |  |  | 0 | for my $pi (@_ ? @_ : 0..$#imp) { | 
| 214 | 0 |  |  |  |  | 0 | my $part = $parts[$dir][$pi]; | 
| 215 | 0 | 0 |  |  |  | 0 | if ( ! $part ) { | 
| 216 | 0 |  |  |  |  | 0 | $out .= "part[$dir][$pi] - skip\n"; | 
| 217 | 0 |  |  |  |  | 0 | next; | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  | $out .= sprintf("part[%d][%d] p|pp|rl=%d|%d|%d ibuf:\n", | 
| 220 | 0 |  |  |  |  | 0 | $dir,$pi,$part->{pass},$part->{prepass},$part->{replace_later}); | 
| 221 | 0 |  |  |  |  | 0 | my $ib = $part->{ibuf}; | 
| 222 | 0 |  |  |  |  | 0 | $out .= $dump_bufs->( $part->{ibuf}); | 
| 223 |  |  |  |  |  |  | } | 
| 224 | 0 |  |  |  |  | 0 | return $out; | 
| 225 | 11 |  |  |  |  | 40 | }; | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | my $split_buf = sub { | 
| 228 | 58 |  |  | 58 |  | 52 | my ($ibuf,$i,$fwd) = @_; | 
| 229 | 58 |  |  |  |  | 44 | my $buf = $ibuf->[$i]; | 
| 230 | 58 | 50 |  |  |  | 81 | die "no split for packet types" if $buf->{dtype}>0; | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | my $buf_before = $new_buf->( | 
| 233 |  |  |  |  |  |  | %$buf, | 
| 234 |  |  |  |  |  |  | eof => 0, | 
| 235 |  |  |  |  |  |  | end => $buf->{start} + $fwd,  # adjust end | 
| 236 |  |  |  |  |  |  | defined($buf->{data}) | 
| 237 | 58 | 50 |  |  |  | 250 | ? ( data => substr($buf->{data},0,$fwd,'') )  # real data | 
| 238 |  |  |  |  |  |  | : (),  # replacement promise | 
| 239 |  |  |  |  |  |  | ); | 
| 240 |  |  |  |  |  |  | # gap in buf_before | 
| 241 | 58 |  |  |  |  | 74 | $buf->{gap} = 0; | 
| 242 | 58 |  |  |  |  | 45 | $buf->{start} = $buf_before->{end}; | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | # if buf was not changed gend..gstart should reflect the | 
| 245 |  |  |  |  |  |  | # original length of the data | 
| 246 | 58 | 100 |  |  |  | 66 | if ( $buf->{rtype} != IMP_REPLACE ) { | 
| 247 | 54 |  |  |  |  | 56 | $buf_before->{gend} = ( $buf->{gstart} += $fwd ); | 
| 248 |  |  |  |  |  |  | } else { | 
| 249 |  |  |  |  |  |  | # split gstart..gend into full|0 per convention | 
| 250 | 4 |  |  |  |  | 4 | $buf->{gstart} = $buf->{gend}; | 
| 251 |  |  |  |  |  |  | } | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | # put buf_before before buf in ibuf | 
| 254 | 58 |  |  |  |  | 90 | splice(@$ibuf,$i,0,$buf_before); | 
| 255 | 11 |  |  |  |  | 24 | }; | 
| 256 |  |  |  |  |  |  |  | 
| 257 | 11 |  |  |  |  | 11 | my $fwd_collect; # collect bufs which can be forwarded | 
| 258 |  |  |  |  |  |  | my $fwd_up;      # collect what can be passed up | 
| 259 | 0 |  |  |  |  | 0 | my $exec_fwd;    # do the collected forwarding to next part or up | 
| 260 |  |  |  |  |  |  |  | 
| 261 | 0 |  |  |  |  | 0 | my $global_in;  # function where data gets fed into from outside (sub data) | 
| 262 | 0 |  |  |  |  | 0 | my $part_in;    # internal feed into each part | 
| 263 |  |  |  |  |  |  |  | 
| 264 | 0 |  |  |  |  | 0 | my $imp_callback;   # synchronization wrapper around callback for analyzers | 
| 265 | 0 |  |  |  |  | 0 | my $_imp_callback;  # real callback for the analyzers | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | # pass passable bufs in part starting with ibuf[i] | 
| 268 |  |  |  |  |  |  | # returns all bufs which can be passed and strips them from part.ibuf | 
| 269 |  |  |  |  |  |  | $fwd_collect = sub { | 
| 270 | 148 |  |  | 148 |  | 154 | my ($dir,$pi,$i,$r_passed) = @_; | 
| 271 | 148 |  |  |  |  | 138 | my $part = $parts[$dir][$pi]; | 
| 272 | 148 |  |  |  |  | 113 | my $ibuf = $part->{ibuf}; | 
| 273 | 148 | 50 |  |  |  | 193 | $DEBUG && debug( | 
| 274 |  |  |  |  |  |  | "fwd_collect[$dir][$pi]: p=$part->{pass} pp=$part->{prepass} " | 
| 275 |  |  |  |  |  |  | .$dump_bufs->($ibuf)); | 
| 276 | 148 |  |  |  |  | 87 | my @fwd; | 
| 277 | 148 |  |  |  |  | 155 | for my $pp (qw(pass prepass)) { | 
| 278 | 296 | 100 |  |  |  | 476 | my $pass = $part->{$pp} or next; | 
| 279 | 71 |  |  |  |  | 106 | for( ;$i<@$ibuf;$i++ ) { | 
| 280 | 174 |  |  |  |  | 141 | my $buf = $ibuf->[$i]; | 
| 281 | 174 | 50 |  |  |  | 205 | last if ! $buf->{dtype}; # dummy buf | 
| 282 | 174 | 100 | 100 |  |  | 427 | if ( $pass != IMP_MAXOFFSET and $buf->{start} >= $pass ) { | 
| 283 | 43 | 50 |  |  |  | 54 | $DEBUG && debug( | 
| 284 |  |  |  |  |  |  | "fwd_collect[$dir][$pi]: reset $pp due to start[$i]($buf->{start})>=$pp($pass)"); | 
| 285 | 43 |  |  |  |  | 33 | $part->{$pp} = 0; | 
| 286 | 43 |  |  |  |  | 84 | last; | 
| 287 |  |  |  |  |  |  | } | 
| 288 |  |  |  |  |  |  | die "cannot pass bufs with replace_later" | 
| 289 | 131 | 50 |  |  |  | 162 | if ! defined $buf->{data}; | 
| 290 | 131 | 100 | 100 |  |  | 276 | if ( $pass == IMP_MAXOFFSET or $buf->{end} <= $pass ) { | 
| 291 |  |  |  |  |  |  | # whole buffer can be passed | 
| 292 | 95 | 50 |  |  |  | 123 | $DEBUG && debug( | 
| 293 |  |  |  |  |  |  | "fwd_collect[$dir][$pi]: pass whole buffer[$i] $buf->{start}..$buf->{end}"); | 
| 294 |  |  |  |  |  |  | $buf->{rtype} = IMP_PREPASS if $pp eq 'prepass' | 
| 295 | 95 | 100 | 100 |  |  | 159 | and $buf->{rtype} == IMP_PASS; | 
| 296 | 95 |  |  |  |  | 118 | push @fwd,[ $pi,$dir,$buf ]; | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | # r_passed is set from part_in to track position if data | 
| 299 |  |  |  |  |  |  | # are passed. In case of prepass we don't pass data but | 
| 300 |  |  |  |  |  |  | # only put them into fwd | 
| 301 | 95 | 100 | 66 |  |  | 146 | next if $r_passed && $pp eq 'prepass'; | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | # track what got passed for part_in | 
| 304 | 91 | 50 |  |  |  | 101 | $$r_passed = $buf->{end} if $r_passed; | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | # remove passed data from ibuf, if ! r_passed also prepassed | 
| 307 |  |  |  |  |  |  | # data (called from imp_callback) | 
| 308 | 91 |  |  |  |  | 67 | shift(@$ibuf); | 
| 309 | 91 |  |  |  |  | 73 | $i--; | 
| 310 |  |  |  |  |  |  |  | 
| 311 | 91 | 100 |  |  |  | 179 | if ( ! @$ibuf ) { | 
| 312 | 24 | 100 | 66 |  |  | 54 | if ( $part->{pass} == IMP_MAXOFFSET || $buf->{eof} ) { | 
| 313 |  |  |  |  |  |  | # part done, skip it in the future | 
| 314 | 22 |  |  |  |  | 27 | push @fwd,[$pi,$dir,undef]; # buf = undef is special | 
| 315 |  |  |  |  |  |  | } | 
| 316 |  |  |  |  |  |  | # insert dummy | 
| 317 |  |  |  |  |  |  | @$ibuf = $new_buf->( | 
| 318 |  |  |  |  |  |  | start  => $buf->{end}, | 
| 319 |  |  |  |  |  |  | end    => $buf->{end}, | 
| 320 |  |  |  |  |  |  | gstart => $buf->{gend}, | 
| 321 |  |  |  |  |  |  | gend   => $buf->{gend}, | 
| 322 |  |  |  |  |  |  | # keep type for streaming data | 
| 323 | 24 | 50 |  |  |  | 61 | $buf->{dtype} < 0 ? ( dtype => $buf->{dtype} ):(), | 
| 324 |  |  |  |  |  |  | ); | 
| 325 | 24 |  |  |  |  | 33 | last; | 
| 326 |  |  |  |  |  |  | } | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | } else { | 
| 329 |  |  |  |  |  |  | # only part of buffer can be passed | 
| 330 |  |  |  |  |  |  | # split buffer and re-enter loop, this will foreward the | 
| 331 |  |  |  |  |  |  | # first part and keep the later part | 
| 332 | 36 | 50 |  |  |  | 50 | $DEBUG && debug( | 
| 333 |  |  |  |  |  |  | "fwd_collect[$dir][$pi]: need to split buffer[$i]: $buf->{start}..$pass..$buf->{end}"); | 
| 334 | 36 |  |  |  |  | 56 | $split_buf->($ibuf,$i,$pass - $buf->{start}); | 
| 335 | 36 |  |  |  |  | 33 | redo; # don't increase $i! | 
| 336 |  |  |  |  |  |  | } | 
| 337 |  |  |  |  |  |  | } | 
| 338 |  |  |  |  |  |  | } | 
| 339 | 148 |  |  |  |  | 254 | return @fwd; | 
| 340 | 11 |  |  |  |  | 52 | }; | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | $fwd_up = sub { | 
| 343 | 71 |  |  | 71 |  | 47 | my ($dir,$buf) = @_; | 
| 344 | 71 | 100 | 66 |  |  | 162 | if ( $buf->{gstart} == $buf->{gend} && ! $buf->{gap} | 
|  |  |  | 100 |  |  |  |  | 
| 345 |  |  |  |  |  |  | && $buf->{rtype} ~~ [ IMP_PASS, IMP_PREPASS ]) { | 
| 346 |  |  |  |  |  |  | # don't repeat last (pre)pass because of empty buffer | 
| 347 | 3 |  |  |  |  | 8 | return; | 
| 348 |  |  |  |  |  |  | } | 
| 349 |  |  |  |  |  |  |  | 
| 350 |  |  |  |  |  |  | return [ | 
| 351 |  |  |  |  |  |  | $buf->{rtype}, | 
| 352 |  |  |  |  |  |  | $dir, | 
| 353 |  |  |  |  |  |  | $buf->{gend}, | 
| 354 | 68 | 100 |  |  |  | 202 | ($buf->{rtype} == IMP_REPLACE) ? ( $buf->{data} ):() | 
| 355 |  |  |  |  |  |  | ]; | 
| 356 | 11 |  |  |  |  | 26 | }; | 
| 357 |  |  |  |  |  |  |  | 
| 358 |  |  |  |  |  |  | $exec_fwd = sub { | 
| 359 | 70 |  |  | 70 |  | 77 | my @fwd = @_; | 
| 360 | 70 | 100 |  |  |  | 113 | if (@fwd>1) { | 
| 361 |  |  |  |  |  |  | $DEBUG && debug("trying to merge\n".join("\n", map { | 
| 362 | 50 | 0 |  |  |  | 66 | ! defined $_->[0] | 
|  | 0 | 0 |  |  |  | 0 |  | 
|  |  | 50 |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | ? "" | 
| 364 |  |  |  |  |  |  | : "fwd[$_->[1]][$_->[0]] " . | 
| 365 |  |  |  |  |  |  | ( $_->[2] ? $dump_bufs->([$_->[2]]) : '') | 
| 366 |  |  |  |  |  |  | } @fwd)); | 
| 367 |  |  |  |  |  |  | # try to compress | 
| 368 | 50 |  |  |  |  | 33 | my ($lpi,$ldir,$lbuf); | 
| 369 | 50 |  |  |  |  | 95 | for( my $i=0;$i<@fwd;$i++ ) { | 
| 370 | 146 | 100 | 33 |  |  | 306 | if ( ! defined $fwd[$i][0] || ! defined $fwd[$i][2]) { | 
| 371 | 22 |  |  |  |  | 19 | $lpi = undef; | 
| 372 | 22 |  |  |  |  | 40 | next; | 
| 373 |  |  |  |  |  |  | } | 
| 374 | 124 | 50 | 66 |  |  | 375 | if ( ! defined $lpi | 
|  |  |  | 66 |  |  |  |  | 
| 375 |  |  |  |  |  |  | or $lpi  != $fwd[$i][0] | 
| 376 |  |  |  |  |  |  | or $ldir != $fwd[$i][1] ) { | 
| 377 | 50 |  |  |  |  | 32 | ($lpi,$ldir,$lbuf) = @{$fwd[$i]}; | 
|  | 50 |  |  |  |  | 61 |  | 
| 378 | 50 |  |  |  |  | 89 | next; | 
| 379 |  |  |  |  |  |  | } | 
| 380 |  |  |  |  |  |  |  | 
| 381 | 74 |  |  |  |  | 56 | my $buf = $fwd[$i][2]; | 
| 382 |  |  |  |  |  |  |  | 
| 383 | 74 | 100 | 33 |  |  | 474 | if ( not $buf->{gap} | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 384 |  |  |  |  |  |  | and $buf->{dtype} < 0 | 
| 385 |  |  |  |  |  |  | and $buf->{start} == $lbuf->{end} | 
| 386 |  |  |  |  |  |  | and $buf->{rtype} == $lbuf->{rtype} | 
| 387 |  |  |  |  |  |  | and $buf->{dtype} == $lbuf->{dtype} | 
| 388 |  |  |  |  |  |  | ) { | 
| 389 | 28 | 100 |  |  |  | 34 | if ( $buf->{rtype} == IMP_REPLACE ) { | 
| 390 | 17 | 100 | 100 |  |  | 70 | if ( $lbuf->{gend} == $buf->{gend} ) { | 
|  |  | 100 |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | # same global end, merge data | 
| 392 | 2 |  |  |  |  | 44 | $lbuf->{data} .= $buf->{data}; | 
| 393 |  |  |  |  |  |  | } elsif ( $buf->{data} ne '' or $lbuf->{data} ne '' ) { | 
| 394 |  |  |  |  |  |  | # either one not empty, no merge | 
| 395 | 11 |  |  |  |  | 22 | next; | 
| 396 |  |  |  |  |  |  | } | 
| 397 |  |  |  |  |  |  | } else { | 
| 398 |  |  |  |  |  |  | # unchanged, append | 
| 399 | 11 |  |  |  |  | 14 | $lbuf->{data} .= $buf->{data}; | 
| 400 |  |  |  |  |  |  | } | 
| 401 | 17 | 50 |  |  |  | 24 | $DEBUG && debug("merge bufs ".$dump_bufs->([$lbuf,$buf])); | 
| 402 | 17 |  |  |  |  | 14 | $lbuf->{gend} = $buf->{gend}; | 
| 403 | 17 |  |  |  |  | 12 | $lbuf->{end}  = $buf->{end}; | 
| 404 | 17 |  |  |  |  | 14 | $lbuf->{eof}  = $buf->{eof}; | 
| 405 | 17 |  |  |  |  | 18 | splice(@fwd,$i,1,()); | 
| 406 | 17 |  |  |  |  | 18 | $i--; | 
| 407 | 17 |  |  |  |  | 27 | next; | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | } else { | 
| 410 | 46 |  |  |  |  | 34 | ($lpi,$ldir,$lbuf) = @{$fwd[$i]}; | 
|  | 46 |  |  |  |  | 52 |  | 
| 411 | 46 |  |  |  |  | 75 | next; | 
| 412 |  |  |  |  |  |  | } | 
| 413 |  |  |  |  |  |  | } | 
| 414 |  |  |  |  |  |  | } | 
| 415 | 70 |  |  |  |  | 115 | while ( my $fwd = shift(@fwd)) { | 
| 416 | 171 |  |  |  |  | 437 | my $npi = my $pi = shift(@$fwd); | 
| 417 | 171 | 50 |  |  |  | 208 | if ( ! defined $npi ) { | 
| 418 |  |  |  |  |  |  | # propagate prepared IMP callback | 
| 419 | 0 |  |  |  |  | 0 | $wself->run_callback($fwd); | 
| 420 | 0 |  |  |  |  | 0 | next; | 
| 421 |  |  |  |  |  |  | } | 
| 422 |  |  |  |  |  |  |  | 
| 423 | 171 |  |  |  |  | 145 | my ($dir,$buf) = @$fwd; | 
| 424 |  |  |  |  |  |  |  | 
| 425 | 171 | 100 |  |  |  | 166 | if ( $buf ) { | 
| 426 | 127 |  |  |  |  | 70 | my $np; | 
| 427 | 127 |  |  |  |  | 86 | my $adjust = 0; | 
| 428 | 127 |  |  |  |  | 75 | while (1) { | 
| 429 | 127 | 100 |  |  |  | 137 | $npi += $dir?-1:+1; | 
| 430 | 127 | 100 | 100 |  |  | 356 | last if $npi<0 or $npi>=@imp; | 
| 431 | 58 | 50 |  |  |  | 105 | last if ref( $np = $parts[$dir][$npi] ); | 
| 432 | 0 |  |  |  |  | 0 | $adjust += $np; | 
| 433 | 0 | 0 |  |  |  | 0 | $DEBUG && debug("skipping pi=$npi"); | 
| 434 |  |  |  |  |  |  | } | 
| 435 |  |  |  |  |  |  |  | 
| 436 | 127 | 100 |  |  |  | 182 | if ( $buf->{eof} ) { | 
| 437 |  |  |  |  |  |  | # add pass infinite to fwd to propagate eof | 
| 438 | 22 |  |  |  |  | 30 | push @fwd,[ $pi,$dir,undef ]; | 
| 439 |  |  |  |  |  |  | } | 
| 440 | 127 | 100 |  |  |  | 128 | if ( $np ) { | 
| 441 |  |  |  |  |  |  | # feed into next part | 
| 442 | 58 |  |  |  |  | 43 | my $nib = $np->{ibuf}; | 
| 443 |  |  |  |  |  |  | # adjust start,end based on end of npi and gap | 
| 444 | 58 |  |  |  |  | 66 | $buf->{start} = $nib->[-1]{end} + $buf->{gap} + $adjust; | 
| 445 | 58 |  |  |  |  | 58 | $buf->{end} = $buf->{start} + length($buf->{data}); | 
| 446 | 58 | 50 |  |  |  | 69 | $DEBUG && debug( | 
| 447 |  |  |  |  |  |  | "fwd_next[$dir][$pi>$npi] ".$dump_bufs->([$buf])); | 
| 448 | 58 |  |  |  |  | 70 | $part_in->($npi,$dir,$buf); | 
| 449 |  |  |  |  |  |  | } else { | 
| 450 |  |  |  |  |  |  | # output from cascade | 
| 451 | 69 | 100 |  |  |  | 91 | my $cb = $fwd_up->($dir,$buf) or next; | 
| 452 | 68 | 50 |  |  |  | 91 | $DEBUG && debug( | 
| 453 |  |  |  |  |  |  | "fwd_up[$dir][$pi>>] ".$dump_bufs->([$buf])); | 
| 454 | 68 |  |  |  |  | 122 | $wself->run_callback($cb); | 
| 455 |  |  |  |  |  |  | } | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | # special - part is done with IMP_PASS IMP_MAXOFFSET | 
| 458 |  |  |  |  |  |  | } else { | 
| 459 |  |  |  |  |  |  | # skip if we had a pass infinite already | 
| 460 | 44 | 100 |  |  |  | 153 | next if ! ref $parts[$dir][$pi]; | 
| 461 |  |  |  |  |  |  |  | 
| 462 | 24 |  |  |  |  | 26 | $parts[$dir][$pi] = $parts[$dir][$pi]->{adjust}; | 
| 463 | 24 | 100 |  |  |  | 39 | if ( grep { ref($_) } @{ $parts[$dir] } ) { | 
|  | 54 |  |  |  |  | 77 |  | 
|  | 24 |  |  |  |  | 34 |  | 
| 464 |  |  |  |  |  |  | # we have other unfinished parts, skip only this part | 
| 465 | 12 | 50 |  |  |  | 42 | $DEBUG && debug( | 
| 466 |  |  |  |  |  |  | "part[$dir][$pi>$npi] will be skipped in future, adjust=$parts[$dir][$pi]"); | 
| 467 |  |  |  |  |  |  | } else { | 
| 468 |  |  |  |  |  |  | # everything can be skipped | 
| 469 | 12 | 50 |  |  |  | 16 | $DEBUG && debug( | 
| 470 |  |  |  |  |  |  | "part[$dir][$pi>>] all parts will be skipped in future"); | 
| 471 |  |  |  |  |  |  | # pass rest | 
| 472 | 12 |  |  |  |  | 29 | $wself->run_callback([ IMP_PASS,$dir,IMP_MAXOFFSET ]); | 
| 473 |  |  |  |  |  |  | } | 
| 474 |  |  |  |  |  |  | } | 
| 475 |  |  |  |  |  |  | } | 
| 476 | 11 |  |  |  |  | 68 | }; | 
| 477 |  |  |  |  |  |  |  | 
| 478 |  |  |  |  |  |  | # the data function | 
| 479 |  |  |  |  |  |  | # called from sub data on new data and from $process when data are finished | 
| 480 |  |  |  |  |  |  | # in on part and should be transferred into the next part | 
| 481 |  |  |  |  |  |  | #  $pi    - index into parts | 
| 482 |  |  |  |  |  |  | #  $dir   - direction (e.g. target part is $parts[$dir][$pi]) | 
| 483 |  |  |  |  |  |  | #  $buf   - new buffer from $new_buf->() which might be merged with existing | 
| 484 |  |  |  |  |  |  | $part_in = sub { | 
| 485 | 87 |  |  | 87 |  | 72 | my ($pi,$dir,$buf) = @_; | 
| 486 | 87 | 50 |  |  |  | 112 | $DEBUG && debug( "part_in[$dir][$pi]: ".$dump_bufs->([$buf])); | 
| 487 |  |  |  |  |  |  |  | 
| 488 | 87 |  |  |  |  | 69 | my $part = $parts[$dir][$pi]; | 
| 489 | 87 |  |  |  |  | 69 | my $ibuf = $part->{ibuf}; | 
| 490 | 87 |  |  |  |  | 62 | my $lbuf = $ibuf->[-1]; | 
| 491 | 87 |  |  |  |  | 65 | my $lend = $lbuf->{end}; | 
| 492 |  |  |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | # some sanity checks | 
| 494 | 87 |  |  |  |  | 59 | if(1) { | 
| 495 |  |  |  |  |  |  | die "data after eof [$dir][$pi] ".$dump_bufs->([$lbuf,$buf]) | 
| 496 | 87 | 50 |  |  |  | 106 | if $lbuf->{eof}; | 
| 497 | 87 | 50 |  |  |  | 154 | if ( $buf->{start} != $lend ) { | 
|  |  | 50 |  |  |  |  |  | 
| 498 | 0 | 0 |  |  |  | 0 | if ( $buf->{start} < $lend ) { | 
|  |  | 0 |  |  |  |  |  | 
| 499 | 0 |  |  |  |  | 0 | die "overlapping data off($buf->{start}) | 
| 500 |  |  |  |  |  |  | } elsif ( ! $buf->{gap} ) { | 
| 501 | 0 |  |  |  |  | 0 | die "gap should be set because off($buf->{start})>last.end($lend) in part[$dir][$pi]" | 
| 502 |  |  |  |  |  |  | } | 
| 503 |  |  |  |  |  |  | } elsif ( $buf->{gap} ) { | 
| 504 | 0 |  |  |  |  | 0 | die "gap specified even if off($buf->{start}) == last.end" | 
| 505 |  |  |  |  |  |  | } | 
| 506 | 87 | 50 |  |  |  | 117 | $part->{pass} == IMP_MAXOFFSET and die | 
| 507 |  |  |  |  |  |  | "pass infinite should have been optimized by removing part[$dir][$pi]"; | 
| 508 |  |  |  |  |  |  | } | 
| 509 |  |  |  |  |  |  |  | 
| 510 |  |  |  |  |  |  | # add data to buf | 
| 511 | 87 | 100 | 100 |  |  | 483 | if ( $lbuf->{data} eq '' and $lbuf->{rtype} == IMP_PASS ) { | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 512 |  |  |  |  |  |  | # empty dummy buffer | 
| 513 | 26 | 50 |  |  |  | 31 | $DEBUG && debug("part_in[$dir][$pi]: replace dummy buffer"); | 
| 514 | 26 | 50 |  |  |  | 39 | @$ibuf == 1 or die "empty dummy buffer should only be at beginning"; | 
| 515 | 26 |  |  |  |  | 30 | @$ibuf = $buf; | 
| 516 |  |  |  |  |  |  |  | 
| 517 |  |  |  |  |  |  | } elsif ( ! $buf->{gap} | 
| 518 |  |  |  |  |  |  | and $buf->{data} eq '' | 
| 519 |  |  |  |  |  |  | and $buf->{rtype} == $lbuf->{rtype} | 
| 520 |  |  |  |  |  |  | and $buf->{dtype} == $lbuf->{dtype} | 
| 521 |  |  |  |  |  |  | and $buf->{dtype} < 0 | 
| 522 |  |  |  |  |  |  | and ! $buf->{eof} | 
| 523 |  |  |  |  |  |  | ) { | 
| 524 |  |  |  |  |  |  | # just update eof,[g]end of lbuf | 
| 525 | 6 | 50 |  |  |  | 10 | $DEBUG && debug( | 
| 526 |  |  |  |  |  |  | "part_in[$dir][$pi]: set lbuf end=$buf->{end} gend=$buf->{gend}"); | 
| 527 | 6 |  |  |  |  | 5 | $lbuf->{end}  = $buf->{end}; | 
| 528 | 6 |  |  |  |  | 8 | $lbuf->{gend} = $buf->{gend}; | 
| 529 |  |  |  |  |  |  | # nothing to do with these empty data | 
| 530 | 6 | 50 |  |  |  | 9 | $DEBUG && debug("part_in[$dir][$pi] nothing to do on empty buffer"); | 
| 531 | 6 |  |  |  |  | 24 | return; | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | } else { | 
| 534 |  |  |  |  |  |  | # add new buf | 
| 535 | 55 | 50 |  |  |  | 66 | $DEBUG && debug("part_in[$dir][$pi]: add new buffer"); | 
| 536 | 55 |  |  |  |  | 63 | push @$ibuf,$buf; | 
| 537 |  |  |  |  |  |  | } | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | # determine what can be forwarded immediatly | 
| 540 | 81 |  |  |  |  | 121 | my @fwd = $fwd_collect->($dir,$pi,$#$ibuf,\$lend); | 
| 541 |  |  |  |  |  |  |  | 
| 542 | 81 | 100 |  |  |  | 137 | if ( $buf->{eof} ? $lend <= $buf->{end} : $lend < $buf->{end} ) { | 
|  |  | 100 |  |  |  |  |  | 
| 543 |  |  |  |  |  |  | # send new data to the analyzer | 
| 544 | 80 |  |  |  |  | 66 | my $rl = $part->{replace_later}; | 
| 545 | 80 |  |  |  |  | 89 | for(@$ibuf) { | 
| 546 | 173 | 100 |  |  |  | 227 | next if $_->{start} < $lend; | 
| 547 |  |  |  |  |  |  | die "last_end should be on buffer boundary" | 
| 548 | 84 | 50 |  |  |  | 102 | if $_->{start} > $lend; | 
| 549 | 84 |  |  |  |  | 61 | $lend = $_->{end}; | 
| 550 |  |  |  |  |  |  | $DEBUG && debug( | 
| 551 |  |  |  |  |  |  | "analyzer[$dir][$pi] << %d bytes %s \@%d%s -> last_end=%d", | 
| 552 |  |  |  |  |  |  | $_->{end} - $_->{start}, | 
| 553 |  |  |  |  |  |  | $_->{dtype}, | 
| 554 | 84 | 0 |  |  |  | 99 | $_->{start},$_->{gap} ? "(+$_->{gap})":'', | 
|  |  | 50 |  |  |  |  |  | 
| 555 |  |  |  |  |  |  | $lend | 
| 556 |  |  |  |  |  |  | ); | 
| 557 |  |  |  |  |  |  | $imp[$pi]->data($dir, | 
| 558 |  |  |  |  |  |  | $_->{data}, | 
| 559 |  |  |  |  |  |  | $_->{gap} ? $_->{start}:0, | 
| 560 |  |  |  |  |  |  | $_->{dtype} | 
| 561 | 84 | 50 |  |  |  | 215 | ); | 
| 562 |  |  |  |  |  |  | $imp[$pi]->data($dir,'',0, $_->{dtype}) | 
| 563 | 84 | 100 | 100 |  |  | 221 | if $buf->{eof} && $_->{data} ne ''; | 
| 564 | 84 | 50 |  |  |  | 155 | $rl or next; | 
| 565 | 0 | 0 | 0 |  |  | 0 | if ( $rl == IMP_MAXOFFSET or $rl>= $lend ) { | 
| 566 | 0 |  |  |  |  | 0 | $buf->{data} = undef; | 
| 567 |  |  |  |  |  |  | } else { | 
| 568 | 0 |  |  |  |  | 0 | $rl = $part->{replace_later} = 0; # reset | 
| 569 |  |  |  |  |  |  | } | 
| 570 |  |  |  |  |  |  | } | 
| 571 |  |  |  |  |  |  | } else { | 
| 572 | 1 | 50 |  |  |  | 3 | $DEBUG && debug( | 
| 573 |  |  |  |  |  |  | "nothing to analyze[$dir][$pi]: last_end($lend) < end($buf->{end})"); | 
| 574 |  |  |  |  |  |  | } | 
| 575 |  |  |  |  |  |  |  | 
| 576 |  |  |  |  |  |  | # forward data which can be (pre)passed | 
| 577 | 81 | 100 |  |  |  | 270 | $exec_fwd->(@fwd) if @fwd; | 
| 578 | 11 |  |  |  |  | 79 | }; | 
| 579 |  |  |  |  |  |  |  | 
| 580 |  |  |  |  |  |  | $_imp_callback = sub { | 
| 581 | 75 |  |  | 75 |  | 57 | my $pi = shift; | 
| 582 |  |  |  |  |  |  |  | 
| 583 | 75 |  |  |  |  | 91 | my @fwd; | 
| 584 | 75 |  |  |  |  | 81 | for my $rv (@_) { | 
| 585 | 107 |  |  |  |  | 92 | my $rtype = shift(@$rv); | 
| 586 |  |  |  |  |  |  |  | 
| 587 | 107 | 50 |  |  |  | 481 | if ( $rtype ~~ [ IMP_FATAL, IMP_DENY, IMP_DROP, IMP_ACCTFIELD ]) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 588 | 0 | 0 |  |  |  | 0 | $DEBUG && debug("callback[.][$pi] $rtype @$rv"); | 
| 589 | 0 |  |  |  |  | 0 | $wself->run_callback([ $rtype, @$rv ]); | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | } elsif ( $rtype == IMP_LOG ) { | 
| 592 | 0 |  |  |  |  | 0 | my ($dir,$offset,$len,$level,$msg,@extmsg) = @$rv; | 
| 593 | 0 | 0 |  |  |  | 0 | $DEBUG && debug( | 
| 594 |  |  |  |  |  |  | "callback[$dir][$pi] $rtype '$msg' off=$offset len=$len lvl=$level"); | 
| 595 |  |  |  |  |  |  | # approximate offset to real position | 
| 596 | 0 |  |  |  |  | 0 | my $newoff = 0; | 
| 597 | 0 |  |  |  |  | 0 | my $part = $parts[$dir][$pi]; | 
| 598 | 0 |  |  |  |  | 0 | for ( @{$part->{ibuf}} ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 599 | 0 | 0 |  |  |  | 0 | if ( $_->{start} <= $offset ) { | 
| 600 |  |  |  |  |  |  | $offset = ( $_->{rtype} == IMP_REPLACE ) | 
| 601 |  |  |  |  |  |  | ? $_->{gstart} | 
| 602 | 0 | 0 |  |  |  | 0 | : $_->{gstart} - $_->{start} + $offset; | 
| 603 |  |  |  |  |  |  | } else { | 
| 604 |  |  |  |  |  |  | last | 
| 605 | 0 |  |  |  |  | 0 | } | 
| 606 |  |  |  |  |  |  | } | 
| 607 | 0 |  |  |  |  | 0 | $wself->run_callback([ IMP_LOG,$dir,$offset,$len,$level,$msg,@extmsg ]); | 
| 608 |  |  |  |  |  |  |  | 
| 609 |  |  |  |  |  |  | } elsif ( $rtype == IMP_PAUSE ) { | 
| 610 | 0 |  |  |  |  | 0 | my $dir = shift; | 
| 611 | 0 | 0 |  |  |  | 0 | $DEBUG && debug("callback[$dir][$pi] $rtype"); | 
| 612 | 0 | 0 |  |  |  | 0 | next if $pause[$pi]; | 
| 613 | 0 |  |  |  |  | 0 | $pause[$dir][$pi] = 1; | 
| 614 | 0 | 0 |  |  |  | 0 | $wself->run_callback([ IMP_PAUSE ]) if grep { $_ } @pause > 1; | 
|  | 0 |  |  |  |  | 0 |  | 
| 615 |  |  |  |  |  |  |  | 
| 616 |  |  |  |  |  |  | } elsif ( $rtype == IMP_CONTINUE ) { | 
| 617 | 0 |  |  |  |  | 0 | my $dir = shift; | 
| 618 | 0 | 0 |  |  |  | 0 | $DEBUG && debug("callback[$dir][$pi] $rtype"); | 
| 619 | 0 |  |  |  |  | 0 | delete $pause[$dir][$pi]; | 
| 620 |  |  |  |  |  |  | $wself->run_callback([ IMP_CONTINUE ]) | 
| 621 | 0 | 0 |  |  |  | 0 | if not grep { $_ } @{$pause[$dir]}; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 622 |  |  |  |  |  |  |  | 
| 623 |  |  |  |  |  |  | } elsif ( $rtype ~~ [ IMP_PASS, IMP_PREPASS ] ) { | 
| 624 | 76 |  |  |  |  | 77 | my ($dir,$offset) = @$rv; | 
| 625 | 76 | 50 |  |  |  | 107 | $DEBUG && debug("callback[$dir][$pi] $rtype $offset"); | 
| 626 | 76 | 100 |  |  |  | 143 | ref(my $part = $parts[$dir][$pi]) or next; # part skippable? | 
| 627 | 69 | 100 |  |  |  | 71 | if ( $rtype == IMP_PASS ) { | 
| 628 | 65 | 50 |  |  |  | 100 | next if $part->{pass} == IMP_MAXOFFSET; # no change | 
| 629 | 65 | 100 |  |  |  | 97 | if ( $offset == IMP_MAXOFFSET ) { | 
|  |  | 50 |  |  |  |  |  | 
| 630 | 22 |  |  |  |  | 19 | $part->{pass} = IMP_MAXOFFSET; | 
| 631 | 22 |  |  |  |  | 20 | $part->{prepass} = 0; # pass >= prepass | 
| 632 |  |  |  |  |  |  | } elsif ( $offset > $part->{pass} ) { | 
| 633 | 43 |  |  |  |  | 32 | $part->{pass} = $offset; | 
| 634 | 43 | 50 | 33 |  |  | 131 | if ( $part->{prepass} != IMP_MAXOFFSET | 
| 635 |  |  |  |  |  |  | and $part->{prepass} <= $offset ) { | 
| 636 | 43 |  |  |  |  | 43 | $part->{prepass} = 0; # pass >= prepass | 
| 637 |  |  |  |  |  |  | } | 
| 638 |  |  |  |  |  |  | } else { | 
| 639 | 0 |  |  |  |  | 0 | next; # no change | 
| 640 |  |  |  |  |  |  | } | 
| 641 |  |  |  |  |  |  | } else {  # IMP_PREPASS | 
| 642 | 4 | 100 |  |  |  | 10 | next if $part->{prepass} == IMP_MAXOFFSET; # no change | 
| 643 | 2 | 50 |  |  |  | 4 | if ( $offset == IMP_MAXOFFSET ) { | 
|  |  | 0 |  |  |  |  |  | 
| 644 | 2 |  |  |  |  | 3 | $part->{prepass} = IMP_MAXOFFSET; | 
| 645 |  |  |  |  |  |  | } elsif ( $offset > $part->{prepass} ) { | 
| 646 | 0 |  |  |  |  | 0 | $part->{prepass} = $offset; | 
| 647 |  |  |  |  |  |  | } else { | 
| 648 | 0 |  |  |  |  | 0 | next; # no change | 
| 649 |  |  |  |  |  |  | } | 
| 650 |  |  |  |  |  |  | } | 
| 651 |  |  |  |  |  |  |  | 
| 652 |  |  |  |  |  |  | # pass/prepass got updated, so we might pass some more data | 
| 653 | 67 |  |  |  |  | 72 | push @fwd, $fwd_collect->($dir,$pi,0); | 
| 654 |  |  |  |  |  |  |  | 
| 655 |  |  |  |  |  |  | } elsif ( $rtype == IMP_REPLACE ) { | 
| 656 | 31 |  |  |  |  | 43 | my ($dir,$offset,$newdata) = @$rv; | 
| 657 | 31 | 50 |  |  |  | 44 | $DEBUG && debug( | 
| 658 |  |  |  |  |  |  | "callback[$dir][$pi] $rtype $dir $offset len=%d", | 
| 659 |  |  |  |  |  |  | length($newdata)); | 
| 660 | 31 | 50 |  |  |  | 54 | ref(my $part = $parts[$dir][$pi]) | 
| 661 |  |  |  |  |  |  | or die "called replace for passed data"; | 
| 662 | 31 |  |  |  |  | 28 | my $ibuf = $part->{ibuf}; | 
| 663 |  |  |  |  |  |  |  | 
| 664 |  |  |  |  |  |  | # sanity checks | 
| 665 | 31 | 50 |  |  |  | 59 | die "called replace although pass=IMP_MAXOFFSET" if ! $part; | 
| 666 | 31 | 50 |  |  |  | 44 | die "no replace with IMP_MAXOFFSET" if $offset == IMP_MAXOFFSET; | 
| 667 |  |  |  |  |  |  | die "called replace for already passed data" | 
| 668 | 31 | 50 |  |  |  | 43 | if $ibuf->[0]{start} > $offset; | 
| 669 |  |  |  |  |  |  |  | 
| 670 | 31 |  |  |  |  | 45 | while (@$ibuf) { | 
| 671 | 71 |  |  |  |  | 60 | my $buf = $ibuf->[0]; | 
| 672 | 71 | 100 |  |  |  | 79 | if ( $offset >= $buf->{end} ) { | 
| 673 |  |  |  |  |  |  | # replace complete buffer | 
| 674 | 49 | 50 |  |  |  | 61 | $DEBUG && debug( | 
| 675 |  |  |  |  |  |  | "replace complete buf $buf->{start}..$buf->{end}"); | 
| 676 | 49 | 100 | 66 |  |  | 166 | if ( ! defined($buf->{data}) | 
| 677 |  |  |  |  |  |  | or $buf->{data} ne $newdata ) { | 
| 678 | 48 |  |  |  |  | 41 | $buf->{rtype} = IMP_REPLACE; | 
| 679 | 48 |  |  |  |  | 39 | $buf->{data} = $newdata; | 
| 680 |  |  |  |  |  |  | $part->{adjust} += | 
| 681 | 48 |  |  |  |  | 55 | length($newdata) - $buf->{end} + $buf->{start}; | 
| 682 | 48 |  |  |  |  | 41 | $newdata = ''; # in the next buffers replace with '' | 
| 683 |  |  |  |  |  |  | } | 
| 684 | 49 |  |  |  |  | 60 | push @fwd,[ $pi,$dir,$buf ]; | 
| 685 | 49 |  |  |  |  | 39 | shift(@$ibuf); | 
| 686 | 49 | 100 |  |  |  | 176 | if ( ! @$ibuf ) { | 
| 687 |  |  |  |  |  |  | # all bufs eaten | 
| 688 |  |  |  |  |  |  | die "called replace for future data" | 
| 689 | 5 | 50 |  |  |  | 13 | if $buf->{end}<$offset; | 
| 690 |  |  |  |  |  |  | @$ibuf = $new_buf->( %$buf, | 
| 691 |  |  |  |  |  |  | data   => '', | 
| 692 |  |  |  |  |  |  | start  => $buf->{end}, | 
| 693 |  |  |  |  |  |  | end    => $buf->{end}, | 
| 694 |  |  |  |  |  |  | gstart => $buf->{gend}, | 
| 695 |  |  |  |  |  |  | # packet types cannot get partial replacement | 
| 696 |  |  |  |  |  |  | # at end | 
| 697 | 5 | 50 |  |  |  | 25 | $buf->{dtype} > 0 ? ( dtype => 0 ):() | 
| 698 |  |  |  |  |  |  | ); | 
| 699 |  |  |  |  |  |  | # remove eof from buf in @fwd because we added | 
| 700 |  |  |  |  |  |  | # new one | 
| 701 | 5 |  |  |  |  | 9 | $fwd[-1][2]{eof} = 0; | 
| 702 | 5 |  |  |  |  | 12 | last; | 
| 703 |  |  |  |  |  |  | } | 
| 704 | 44 | 100 |  |  |  | 111 | last if $buf->{end} == $offset; | 
| 705 |  |  |  |  |  |  | } else { | 
| 706 |  |  |  |  |  |  | # split buffer and replace first part | 
| 707 | 22 | 50 |  |  |  | 27 | $DEBUG && debug( | 
| 708 |  |  |  |  |  |  | "replace - split buf $buf->{start}..$offset..$buf->{end}"); | 
| 709 | 22 |  |  |  |  | 35 | $split_buf->($ibuf,0,$offset-$buf->{start}); | 
| 710 | 22 |  |  |  |  | 21 | redo; | 
| 711 |  |  |  |  |  |  | } | 
| 712 |  |  |  |  |  |  | } | 
| 713 |  |  |  |  |  |  |  | 
| 714 |  |  |  |  |  |  | } elsif ( $rtype == IMP_REPLACE_LATER ) { | 
| 715 | 0 |  |  |  |  | 0 | my ($dir,$offset) = @$rv; | 
| 716 | 0 | 0 |  |  |  | 0 | $DEBUG && debug("callback[$dir][$pi] $rtype $offset"); | 
| 717 | 0 | 0 |  |  |  | 0 | ref(my $part = $parts[$dir][$pi]) | 
| 718 |  |  |  |  |  |  | or die "called replace for passed data"; | 
| 719 | 0 |  |  |  |  | 0 | my $ibuf = $part->{ibuf}; | 
| 720 | 0 | 0 |  |  |  | 0 | $_->{replace_later} == IMP_MAXOFFSET and next; # no change | 
| 721 |  |  |  |  |  |  |  | 
| 722 |  |  |  |  |  |  | # sanity checks | 
| 723 | 0 | 0 |  |  |  | 0 | die "called replace_later although pass=IMP_MAX_OFFSET" | 
| 724 |  |  |  |  |  |  | if ! $part; | 
| 725 |  |  |  |  |  |  | die "called replace for already passed data" if | 
| 726 |  |  |  |  |  |  | $offset != IMP_MAXOFFSET and | 
| 727 | 0 | 0 | 0 |  |  | 0 | $ibuf->[0]{start} > $offset; | 
| 728 |  |  |  |  |  |  |  | 
| 729 | 0 | 0 |  |  |  | 0 | if ( $offset == IMP_MAXOFFSET ) { | 
|  |  | 0 |  |  |  |  |  | 
| 730 | 0 |  |  |  |  | 0 | $_->{replace_later} = IMP_MAXOFFSET; | 
| 731 |  |  |  |  |  |  | # change all to replace_later | 
| 732 | 0 |  |  |  |  | 0 | $_->{data} = undef for(@$ibuf); | 
| 733 | 0 |  |  |  |  | 0 | next; | 
| 734 |  |  |  |  |  |  | } elsif ( $offset <= $part->{replace_later} ) { | 
| 735 |  |  |  |  |  |  | # no change | 
| 736 |  |  |  |  |  |  | } else { | 
| 737 | 0 |  |  |  |  | 0 | $part->{replace_later} = $offset; | 
| 738 | 0 |  |  |  |  | 0 | for(@$ibuf) { | 
| 739 | 0 | 0 |  |  |  | 0 | defined($_->{data}) or next; # already replace_later | 
| 740 | 0 | 0 |  |  |  | 0 | my $len = length($_->{data}) or last; # dummy buffer | 
| 741 | 0 | 0 |  |  |  | 0 | if ( $_->{start} + $len <= $offset ) { | 
| 742 | 0 |  |  |  |  | 0 | $_->{data} = undef; | 
| 743 |  |  |  |  |  |  | } else { | 
| 744 | 0 |  |  |  |  | 0 | $part->{replace_later} = 0; | 
| 745 | 0 |  |  |  |  | 0 | last; | 
| 746 |  |  |  |  |  |  | } | 
| 747 |  |  |  |  |  |  | } | 
| 748 |  |  |  |  |  |  | } | 
| 749 |  |  |  |  |  |  | } else { | 
| 750 | 0 | 0 |  |  |  | 0 | $DEBUG && debug("callback[.][$pi] $rtype @$rv"); | 
| 751 | 0 |  |  |  |  | 0 | die "don't know how to handle rtype $rtype"; | 
| 752 |  |  |  |  |  |  | } | 
| 753 |  |  |  |  |  |  | } | 
| 754 |  |  |  |  |  |  |  | 
| 755 |  |  |  |  |  |  | # pass to next part/output | 
| 756 | 75 | 100 |  |  |  | 153 | $exec_fwd->(@fwd) if @fwd; | 
| 757 | 11 |  |  |  |  | 97 | }; | 
| 758 |  |  |  |  |  |  |  | 
| 759 |  |  |  |  |  |  | # While we are in $part_in function we will only spool callbacks and process | 
| 760 |  |  |  |  |  |  | # them at the end. Otherwise $dataf might cause call of callback which then | 
| 761 |  |  |  |  |  |  | # causes call of dataf etc - which makes debugging a nightmare. | 
| 762 |  |  |  |  |  |  |  | 
| 763 | 11 |  |  |  |  | 7 | my $collect_callbacks; | 
| 764 |  |  |  |  |  |  | $global_in = sub { | 
| 765 | 31 |  |  | 31 |  | 32 | my ($dir,$data,$offset,$dtype) = @_; | 
| 766 |  |  |  |  |  |  |  | 
| 767 | 31 |  | 100 |  |  | 126 | my %buf = ( | 
| 768 |  |  |  |  |  |  | data  => $data, | 
| 769 |  |  |  |  |  |  | dtype => $dtype // IMP_DATA_STREAM, | 
| 770 |  |  |  |  |  |  | rtype => IMP_PASS, | 
| 771 |  |  |  |  |  |  | eof   => $data eq '', | 
| 772 |  |  |  |  |  |  | ); | 
| 773 |  |  |  |  |  |  |  | 
| 774 | 31 |  |  |  |  | 25 | my $adjust = 0; | 
| 775 | 31 | 100 |  |  |  | 36 | my $pi = $dir ? $#imp:0; # enter into first or last part | 
| 776 | 31 |  |  |  |  | 22 | my $np; | 
| 777 | 31 |  |  |  |  | 25 | while (1) { | 
| 778 | 34 | 100 |  |  |  | 68 | ref( $np = $parts[$dir][$pi] ) and last; | 
| 779 | 5 |  |  |  |  | 5 | $adjust += $np; | 
| 780 | 5 | 50 |  |  |  | 6 | $pi += $dir?-1:1; | 
| 781 | 5 | 100 | 66 |  |  | 17 | if ( $pi<0 or $pi>$#imp ) { | 
| 782 | 2 | 50 |  |  |  | 3 | $DEBUG && debug("all skipped"); | 
| 783 | 2 | 50 |  |  |  | 4 | if ( my $cb = $fwd_up->($dir,$new_buf->(%buf))) { | 
| 784 | 0 |  |  |  |  | 0 | $self->run_callback($cb); | 
| 785 |  |  |  |  |  |  | } | 
| 786 | 2 |  |  |  |  | 7 | return; | 
| 787 |  |  |  |  |  |  | } | 
| 788 |  |  |  |  |  |  | } | 
| 789 |  |  |  |  |  |  |  | 
| 790 | 29 | 50 |  |  |  | 44 | return if ! ref $np; # got IMP_PASS IMP_MAXOFFSET for all | 
| 791 |  |  |  |  |  |  |  | 
| 792 | 29 |  |  |  |  | 38 | my $ibuf_end = $np->{ibuf}[-1]{gend}; | 
| 793 | 29 | 50 |  |  |  | 33 | if ( ! $offset ) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 794 |  |  |  |  |  |  | # no gap between data | 
| 795 | 29 |  |  |  |  | 30 | $buf{gstart} = $ibuf_end; | 
| 796 |  |  |  |  |  |  | } elsif ( $offset < $ibuf_end ) { | 
| 797 | 0 |  |  |  |  | 0 | die "overlapping data"; | 
| 798 |  |  |  |  |  |  | } elsif ( $offset > $ibuf_end ) { | 
| 799 |  |  |  |  |  |  | # gap between data | 
| 800 | 0 |  |  |  |  | 0 | $buf{gstart} = $offset; | 
| 801 | 0 |  |  |  |  | 0 | $buf{gap} = $offset - $ibuf_end; | 
| 802 |  |  |  |  |  |  | } else { | 
| 803 |  |  |  |  |  |  | # there was no need for giving offset | 
| 804 | 0 |  |  |  |  | 0 | $buf{gstart} = $ibuf_end; | 
| 805 |  |  |  |  |  |  | } | 
| 806 | 29 |  |  |  |  | 36 | $buf{gend}  = $buf{gstart} + length($data); | 
| 807 | 29 |  |  |  |  | 29 | $buf{start} = $buf{gstart} + $adjust; | 
| 808 | 29 |  |  |  |  | 33 | $buf{end}   = $buf{gend} + $adjust; | 
| 809 |  |  |  |  |  |  |  | 
| 810 | 29 |  | 50 |  |  | 81 | $collect_callbacks ||= []; | 
| 811 | 29 |  |  |  |  | 79 | $part_in->( $pi,$dir, $new_buf->(%buf)); | 
| 812 |  |  |  |  |  |  |  | 
| 813 | 29 |  |  |  |  | 62 | while ( my $cb = shift(@$collect_callbacks)) { | 
| 814 | 75 |  |  |  |  | 207 | $_imp_callback->(@$cb); | 
| 815 |  |  |  |  |  |  | } | 
| 816 |  |  |  |  |  |  | $collect_callbacks = undef | 
| 817 | 11 |  |  |  |  | 46 | }; | 
|  | 29 |  |  |  |  | 227 |  | 
| 818 |  |  |  |  |  |  |  | 
| 819 |  |  |  |  |  |  | # wrapper which spools callbacks if within dataf | 
| 820 |  |  |  |  |  |  | $imp_callback = sub { | 
| 821 | 75 | 50 |  | 75 |  | 95 | if ( $collect_callbacks ) { | 
| 822 |  |  |  |  |  |  | # only spool and execute later | 
| 823 | 75 |  |  |  |  | 89 | push @$collect_callbacks, [ @_ ]; | 
| 824 | 75 |  |  |  |  | 151 | return; | 
| 825 |  |  |  |  |  |  | } | 
| 826 | 0 |  |  |  |  | 0 | return $_imp_callback->(@_) | 
| 827 | 11 |  |  |  |  | 25 | }; | 
| 828 |  |  |  |  |  |  |  | 
| 829 |  |  |  |  |  |  | # setup callbacks | 
| 830 | 11 |  |  |  |  | 57 | $imp[$_]->set_callback( $imp_callback,$_ ) for (0..$#imp); | 
| 831 |  |  |  |  |  |  |  | 
| 832 |  |  |  |  |  |  | # make some closures available within methods | 
| 833 | 11 |  |  |  |  | 15 | $self->{dataf} = $global_in; | 
| 834 |  |  |  |  |  |  | $self->{closef} = sub { | 
| 835 | 0 |  |  | 0 |  | 0 | $global_in = $part_in = $imp_callback = $_imp_callback = undef; | 
| 836 | 0 |  |  |  |  | 0 | @parts = (); | 
| 837 | 11 |  |  |  |  | 30 | }; | 
| 838 | 11 |  |  |  |  | 104 | return $self; | 
| 839 |  |  |  |  |  |  | } | 
| 840 |  |  |  |  |  |  |  | 
| 841 |  |  |  |  |  |  | sub data { | 
| 842 | 31 |  |  | 31 | 1 | 44 | my $self = shift; | 
| 843 | 31 |  |  |  |  | 43 | $self->{dataf}(@_); | 
| 844 |  |  |  |  |  |  | } | 
| 845 |  |  |  |  |  |  |  | 
| 846 |  |  |  |  |  |  | sub DESTROY { | 
| 847 | 11 |  |  | 11 |  | 2750 | my $closef = shift->{closef}; | 
| 848 | 11 | 50 |  |  |  | 56 | $closef->() if $closef; | 
| 849 |  |  |  |  |  |  | } | 
| 850 |  |  |  |  |  |  |  | 
| 851 |  |  |  |  |  |  |  | 
| 852 |  |  |  |  |  |  | 1; | 
| 853 |  |  |  |  |  |  |  | 
| 854 |  |  |  |  |  |  | __END__ |