File Coverage

blib/lib/SWF/BinStream.pm
Criterion Covered Total %
statement 176 227 77.5
branch 41 72 56.9
condition 10 15 66.6
subroutine 44 62 70.9
pod n/a
total 271 376 72.0


line stmt bran cond sub pod time code
1             package SWF::BinStream;
2            
3 1     1   8 use strict;
  1         2  
  1         58  
4 1     1   8 use vars qw($VERSION);
  1         3  
  1         74  
5            
6             $VERSION="0.11";
7            
8             ##
9            
10             package SWF::BinStream::Read;
11            
12 1     1   6 use Carp;
  1         2  
  1         75  
13 1     1   958 use Data::TemporaryBag;
  1         58852  
  1         1522  
14            
15            
16             sub new {
17 1     1   4 my ($class, $initialdata, $shortsub, $version) = @_;
18 0     0   0 my $self = bless {
19             '_bits' => '',
20             '_stream' =>Data::TemporaryBag->new,
21             '_shortsub' =>$shortsub||sub{0},
22 1   50     6 '_pos' => 0,
      50        
23             '_codec' => [],
24             '_version' => $version||5,
25             '_lock_version' => 0,
26             }, $class;
27 1 50       53 $self->add_stream($initialdata) if $initialdata ne '';
28 1         12 $self;
29             }
30            
31             sub Version {
32 1     1   23 my ($self, $ver) = @_;
33            
34 1 50       4 if (defined $ver) {
35 1 50       14 croak "Can't change SWF version " if $self->{_lock_version};
36 1         3 $self->{_version} = $ver;
37             }
38 1         4 $self->{_version};
39             }
40            
41             sub _lock_version {
42 0     0   0 shift->{_lock_version} = 1;
43             }
44            
45             sub add_stream {
46 2     2   4 my ($self, $data) = @_;
47            
48 2         3 for my $codec ( @{$self->{'_codec'}} ) {
  2         9  
49 1         8 $data = $codec->decode($data);
50             }
51 2         13 $self->{'_stream'}->add($data);
52             }
53            
54             sub _require {
55 98     98   177 my ($self, $bytes) = @_;
56             {
57 98         4519 my $len=$self->{'_stream'}->length;
  98         264  
58            
59 98 50       454 if ($len < $bytes) {
60 0 0       0 $self->{'_shortsub'}->($self, $bytes-$len) and redo;
61 0         0 croak "Stream ran short ";
62             }
63             }
64            
65             }
66            
67             sub Length {
68 9     9   37 return $_[0]->{'_stream'}->length;
69             }
70            
71 24     24   156 sub tell {$_[0]->{'_pos'}};
72            
73             sub get_string {
74 92     92   264 my ($self, $bytes, $fNoFlush) = @_;
75            
76 92 100       189 flush_bits($self) unless $fNoFlush;
77 92         141 _require($self, $bytes);
78 92         249 $self->{'_pos'}+=$bytes;
79 92         259 $self->{'_stream'}->substr(0, $bytes, '');
80             }
81            
82             sub lookahead_string {
83 0     0   0 my ($self, $offset, $bytes) = @_;
84            
85 0         0 _require($self, $offset);
86 0         0 $self->{'_stream'}->substr($offset, $bytes);
87             }
88            
89             sub get_UI8 {
90 12     12   28 unpack 'C', get_string(shift, 1);
91             }
92            
93             sub lookahead_UI8 {
94 0     0   0 unpack 'C', lookahead_string(@_[0, 1], 1);
95             }
96            
97             sub get_SI8 {
98 0     0   0 unpack 'c', get_string(shift, 1);
99             }
100            
101             sub lookahead_SI8 {
102 0     0   0 unpack 'c', lookahead_string(@_[0, 1], 1);
103             }
104            
105             sub get_UI16 {
106 11     11   141 unpack 'v', get_string(shift, 2);
107             }
108            
109             sub lookahead_UI16 {
110 0     0   0 unpack 'v', lookahead_string(@_[0, 1], 2);
111             }
112            
113             sub get_SI16 {
114 0     0   0 my $w = &get_UI16;
115 0 0       0 $w -= (1<<16) if $w>=(1<<15);
116 0         0 $w;
117             }
118            
119             sub lookahead_SI16 {
120 0     0   0 my $w = &lookahead_UI16;
121 0 0       0 $w -= (1<<16) if $w>=(1<<15);
122 0         0 $w;
123             }
124            
125             sub get_UI32 {
126 2     2   10 unpack 'V', get_string(shift, 4);
127             }
128            
129             sub lookahead_UI32 {
130 0     0   0 unpack 'V', lookahead_string(@_[0, 1], 4);
131             }
132            
133             sub get_SI32 {
134 0     0   0 my $ww = &get_UI32;
135 0 0       0 $ww -= (2**32) if $ww>=(2**31);
136 0         0 $ww;
137             }
138            
139             sub lookahead_SI32 {
140 0     0   0 my $ww = &lookahead_UI32;
141 0 0       0 $ww -= (2**32) if $ww>=(2**31);
142 0         0 $ww;
143             }
144            
145             sub flush_bits {
146 44     44   98 $_[0]->{'_bits'}='';
147             }
148            
149             sub get_bits {
150 81     81   100 my ($self, $bits) = @_;
151 81         119 my $len = length($self->{'_bits'});
152            
153 81 100       154 if ( $len < $bits) {
154 57         87 my $slen = (($bits - $len - 1) >>3) + 1;
155 57         145 $self->{'_bits'}.=join '', unpack('B8' x $slen, $self->get_string($slen, 'NoFlush'));
156             }
157 81         3175 unpack('N', pack('B32', '0' x (32-$bits).substr($self->{'_bits'}, 0, $bits, '')));
158             }
159            
160             sub get_sbits {
161 44     44   52 my ($self, $bits) = @_;
162            
163 44         64 my $b = &get_bits;
164 44 100       146 $b -= (2**$bits) if $b>=(2**($bits-1));
165 44         228 $b;
166             }
167            
168             sub close {
169 0     0   0 my $self = shift;
170            
171 0         0 for my $codec ( @{$self->{'_codec'}} ) {
  0         0  
172 0         0 $codec->close;
173             }
174 0         0 $self->{'_stream'}->clear;
175             }
176            
177            
178             sub add_codec {
179 1     1   4 my ($self, $codec) = @_;
180            
181 1 50       1141 require "SWF/BinStream/Codec/${codec}.pm" or croak "Can't find codec '$codec'";
182            
183 1 50       14 my $m = "SWF::BinStream::Codec::${codec}::Read"->new or croak "Can't find codec '$codec' ";
184            
185 1         3 push @{$self->{'_codec'}}, $m;
  1         4  
186            
187 1 50       6 if (( my $old_stream = $self->{'_stream'})->length > 0) {
188 1         12 my $new_stream = Data::TemporaryBag->new;
189            
190 1         13 while ($old_stream->length > 0) {
191 1         9 $new_stream->add($m->decode($old_stream->substr(0, 1024, '')));
192             }
193 1         17 $self->{'_stream'} = $new_stream;
194             }
195             }
196            
197             1;
198            
199             package SWF::BinStream::Write;
200            
201 1     1   12 use Carp;
  1         3  
  1         551  
202 1     1   8 use Data::TemporaryBag;
  1         2  
  1         8018  
203            
204             sub new {
205 9     9   15 my ($class, $version) = @_;
206 9   50     41 bless { '_bits' => '',
207             '_stream' => Data::TemporaryBag->new,
208             '_pos' => 0,
209             '_flushsize' => 0,
210             '_mark' => {},
211             '_codec' => [],
212             '_version' => $version || 5,
213             '_lock_version' => 0,
214             '_framecount' => 0,
215             }, $class;
216             }
217            
218             sub Version {
219 10     10   14 my ($self, $ver) = @_;
220            
221 10 50       24 if (defined $ver) {
222 0 0       0 croak "Can't change SWF version " if $self->{_lock_version};
223 0         0 $self->{_version} = $ver;
224             }
225 10         45 $self->{_version};
226             }
227            
228             sub _lock_version {
229 0     0   0 shift->{_lock_version} = 1;
230             }
231            
232             sub autoflush {
233 1     1   3 my ($self, $size, $flushsub)=@_;
234            
235 1         8 $self->{'_flushsize'}=$size;
236 1         4 $self->{'_flushsub'}=$flushsub;
237             }
238            
239             sub _write_stream {
240 37     37   56 my ($self, $data) = @_;
241            
242 37         43 for my $codec ( @{$self->{'_codec'}} ) {
  37         87  
243 4         98 $data = $codec->encode($data);
244             }
245 37 100       96 return if $data eq '';
246            
247 34         120 $self->{'_stream'}->add($data);
248            
249 34 50 66     590 if ($self->{'_flushsize'}>0 and $self->{'_stream'}->length >= $self->{'_flushsize'}) {
250 0         0 $self->flush_stream($self->{'_flushsize'});
251             }
252             }
253            
254             sub flush_stream {
255 15     15   102 my ($self, $size)=@_;
256 15         19 my $str;
257            
258 15 50 66     53 if ( !$size or $size>$self->Length ) {
259 15         95 $self->flush_bits;
260             }
261            
262 15 100       33 if ($size) {
263 14         53 $str = $self->{'_stream'}->substr( 0, $size, '');
264 14         392 $self->{'_pos'} += length($str);
265             } else {
266 1         7 $str=$self->{'_stream'}->value;
267 1         26 $self->{'_pos'}+=length($str);
268 1         19 $self->{'_stream'}=Data::TemporaryBag->new;
269             }
270            
271 15 100       55 $self->{'_flushsub'}->($self, $str) if defined $self->{'_flushsub'};
272            
273 15         53 $str;
274             }
275            
276             sub flush_bits {
277 52     52   65 my $self = $_[0];
278 52         91 my $bits = $self->{'_bits'};
279 52         60 my $len = length($bits);
280            
281 52 100       130 return if $len<=0;
282 4         10 $self->{'_bits'}='';
283 4         28 $self->_write_stream(pack('B8', $bits.('0'x(8-$len))));
284             }
285            
286             sub Length {
287 30     30   118 return $_[0]->{'_stream'}->length;
288             }
289            
290             sub tell {
291 16     16   23 my $self=shift;
292 16         51 my $pos= $self->{'_pos'} + $self->Length;
293 16 100       319 $pos++ if length($self->{'_bits'})>0;
294 16         43 $pos;
295             }
296            
297             sub mark {
298 8     8   13 my ($self, $key, $obj)=@_;
299            
300 8 50       18 if (not defined $key) {
    0          
301 8         14 return %{$self->{_mark}};
  8         31  
302             } elsif (not defined $obj) {
303 0 0       0 return wantarray ? $self->{_mark}{$key}[0] : @{$self->{_mark}{$key}};
  0         0  
304             } else {
305 0         0 push @{$self->{_mark}{$key}}, $self->tell, $obj;
  0         0  
306             }
307             }
308            
309             sub sub_stream {
310 8     8   15 my $self=shift;
311 8         24 my $sub_stream=SWF::BinStream::Write->new($self->Version);
312 8         161 $sub_stream->{_parent}=$self;
313 8         33 bless $sub_stream, 'SWF::BinStream::Write::SubStream';
314             }
315            
316             sub set_string {
317 33     33   54 my ($self, $str) = @_;
318            
319 33         74 $self->flush_bits;
320 33         75 $self->_write_stream($str);
321             }
322            
323             sub _round {
324 184     184   209 my $a=shift;
325            
326 184 100       414 return 0 unless $a;
327 145         493 return int($a+0.5*($a<=>0));
328             }
329            
330             sub set_UI8 {
331 12     12   29 $_[0]->set_string(pack('C', _round($_[1])));
332             }
333            
334             sub set_SI8 {
335 0     0   0 $_[0]->set_string(pack('c', _round($_[1])));
336             }
337            
338             sub set_UI16 {
339 11     11   34 $_[0]->set_string(pack('v', _round($_[1])));
340             }
341            
342             *set_SI16 = \&set_UI16;
343            
344             #sub set_SI16 {
345             # my ($self, $num) = @_;
346             # $num += (1<<16) if $num<0;
347             # $self->set_UI16($num);
348             #}
349            
350             sub set_UI32 {
351 2     2   6 $_[0]->set_string(pack('V', _round($_[1])));
352             }
353            
354             *set_SI32 = \&set_UI32;
355            
356             #sub set_SI32 {
357             # my ($self, $num) = @_;
358             # $num += (2**32) if $num<0;
359             # $self->set_UI32($num);
360             #}
361            
362             sub set_bits {
363 71     71   101 my ($self, $num, $nbits) = @_;
364 71 50       140 return unless $nbits;
365 71         144 $self->{'_bits'} .= substr(unpack('B*',pack('N', _round($num))), -$nbits);
366 71         115 my $s = '';
367 71         178 while (length($self->{'_bits'})>=8) {
368 73         258 $s .= pack('B8', substr($self->{'_bits'}, 0,8, ''));
369             }
370 71 100       260 $self->{'_stream'}->add($s) if $s ne '';
371             }
372            
373             sub set_sbits {
374 44     44   59 my ($self, $num, $nbits) = @_;
375 44         69 $num=_round($num);
376 44 100       92 $num += (2**$nbits) if $num<0;
377 44         90 $self->set_bits($num, $nbits);
378             }
379            
380             sub set_bits_list {
381 0     0   0 my ($self, $nbitsbit, @param) = @_;
382 0         0 my $nbits=get_maxbits_of_bits_list(@param);
383 0         0 my $i;
384            
385 0         0 $self->set_bits($nbits, $nbitsbit);
386 0         0 foreach $i (@param) {
387 0         0 $self->set_bits($i, $nbits);
388             }
389             }
390            
391             sub set_sbits_list {
392 4     4   9 my ($self, $nbitsbit, @param) = @_;
393 4         11 my $nbits=get_maxbits_of_sbits_list(@param);
394 4         6 my $i;
395            
396 4         15 $self->set_bits($nbits, $nbitsbit);
397 4         19 foreach $i (@param) {
398 12         104 $self->set_sbits($i, $nbits);
399             }
400             }
401            
402             sub get_maxbits_of_bits_list {
403 12     12   25 my (@param)=@_;
404 12         13 my $max=shift;
405 12         14 my $i;
406            
407 12         19 foreach $i(@param) {
408 44 100       93 $max=$i if $max<$i;
409             }
410 12         20 $i = 0;
411 12         1332 $i++ while ($max >= 2**$i);
412 12         38 return $i;
413             }
414            
415             sub get_maxbits_of_sbits_list {
416 12     12   16 my $z = 0;
417 12 100 100     20 return (get_maxbits_of_bits_list(map{my $r=_round($_);$z ||= ($r!=0);($r<0)?(~$r):$r} @_)+$z);
  44         71  
  44         187  
  44         111  
418             }
419            
420             sub close {
421 1     1   3 my $self = shift;
422            
423 1         6 my $data = $self->flush_stream;
424 1         3 my $rest = '';
425 1         2 for my $codec ( @{$self->{'_codec'}} ) {
  1         4  
426 1         7 $rest = $codec->close($rest);
427             }
428 1 50       12 $self->{'_flushsub'}->($self, $rest) if defined $self->{'_flushsub'};
429            
430 1         4 $data .= $rest;
431 1         3 $data;
432             }
433            
434             sub add_codec {
435 1     1   3 my ($self, $codec) = @_;
436            
437 1 50       3021 require "SWF/BinStream/Codec/${codec}.pm" or croak "Can't find codec '$codec'";
438            
439 1 50       14 my $m = "SWF::BinStream::Codec::${codec}::Write"->new or croak "Can't find codec '$codec'";
440            
441 1         4 push @{$self->{'_codec'}}, $m;
  1         11  
442             }
443            
444             package SWF::BinStream::Write::SubStream;
445            
446 1     1   16 use vars qw(@ISA);
  1         2  
  1         422  
447            
448             @ISA=('SWF::BinStream::Write');
449            
450             sub flush_stream {
451 8     8   14 my $self = shift;
452 8         451 my $p_tell = $self->{_parent}->tell;
453            
454 8         33 while ((my $data = $self->SUPER::flush_stream(1024)) ne '') {
455 6         18 $self->{_parent}->set_string($data);
456             }
457            
458 8         38 my @marks=$self->mark;
459 8         24 while (@marks) {
460 0         0 my $key = shift @marks;
461 0         0 my $mark = shift @marks;
462 0         0 $mark->[$_*2] += $p_tell for (0..@$mark/2-1);
463 0         0 push @{$self->{_parent}->{_mark}{$key}}, @$mark;
  0         0  
464             }
465 8         55 undef $self;
466             }
467            
468 0     0     sub autoflush {} # Ignore autoflush.
469 0     0     sub add_codec {warn "Can't add codec to the sub stream"}
470             *SWF::BinStream::Write::SubStream::close = \&flush_stream;
471            
472             1;
473            
474             __END__