File Coverage

blib/lib/Hessian/Tiny/ConvertorV2.pm
Criterion Covered Total %
statement 30 644 4.6
branch 0 208 0.0
condition 0 21 0.0
subroutine 10 25 40.0
pod 1 1 100.0
total 41 899 4.5


line stmt bran cond sub pod time code
1             package Hessian::Tiny::ConvertorV2;
2              
3 2     2   12 use warnings;
  2         5  
  2         74  
4 2     2   12 use strict;
  2         4  
  2         61  
5              
6 2     2   9 use Encode ();
  2         4  
  2         32  
7 2     2   10 use Switch 'Perl6';
  2         4  
  2         14  
8 2     2   1463424 use List::Util qw(first);
  2         7  
  2         596  
9 2     2   19 use IO::Handle ();
  2         5  
  2         41  
10 2     2   13 use Scalar::Util ();
  2         6  
  2         40  
11 2     2   15 use Math::BigInt ();
  2         4  
  2         37  
12 2     2   14 use Tie::RefHash ();
  2         4  
  2         41  
13              
14 2     2   14 use Hessian::Tiny::Type ();
  2         5  
  2         19998  
15              
16             =head1 NAME
17              
18             Hessian::Tiny::ConvertorV2 - v2 serializer/deserializer
19              
20             =head1 SUBROUTINES/METHODS
21              
22             =head2 write_call
23              
24             write hessian v2 call string
25              
26             =cut
27              
28             sub write_call {
29 0     0 1   my($writer,$method_name,@hessian_params) = @_;
30 0           $writer->("H\x02\x00C");
31 0           my $serializer = _make_serializer_v2($writer);
32 0           $serializer->(Hessian::Type::String->new($method_name));
33 0           $serializer->(scalar @hessian_params);
34 0           $serializer->($_) for(@hessian_params);
35             }
36              
37              
38             sub _make_serializer_v2 {
39 0     0     my($wr) = @_;
40 0           my $refs = [];
41 0           my $f;
42             $f = sub {
43 0     0     my $x = shift;
44 0           my $rf = \$f;
45 0           Scalar::Util::weaken($rf);
46 0 0         unless(defined $x){ $wr->('N'); return}
  0            
  0            
47 0           given(ref $x){
  0            
  0            
  0            
48 0 0         when('Hessian::Type::Null') { $wr->('N') }
  0            
  0            
  0            
  0            
  0            
  0            
49 0 0         when('Hessian::Type::True') { $wr->('T') }
  0            
  0            
  0            
  0            
  0            
  0            
50 0 0         when('Hessian::Type::False') { $wr->('F') }
  0            
  0            
  0            
  0            
  0            
  0            
51              
52 0 0         when('DateTime') { $wr->('J'. Hessian::Tiny::Type::_pack_q($x->epoch . '000')) }
  0            
  0            
  0            
  0            
  0            
  0            
53 0 0         when('Hessian::Type::Date') { $wr->('J'. Hessian::Tiny::Type::_pack_q($$x{data})) }
  0            
  0            
  0            
  0            
  0            
  0            
54              
55 0 0         when('Hessian::Type::Integer') { $wr->('I' . Hessian::Tiny::Type::_l2n(pack 'l', $$x{data})) }
  0            
  0            
  0            
  0            
  0            
  0            
56 0 0         when('Hessian::Type::Long') { $wr->('L' . Hessian::Tiny::Type::_pack_q($$x{data})) }
  0            
  0            
  0            
  0            
  0            
  0            
57 0 0         when('Math::BigInt') { $wr->('L' . Hessian::Tiny::Type::_pack_q($x)) }
  0            
  0            
  0            
  0            
  0            
  0            
58 0 0         when('Hessian::Type::Double') { $wr->('D' . Hessian::Tiny::Type::_l2n(pack 'd', $$x{data})) }
  0            
  0            
  0            
  0            
  0            
  0            
59              
60 0 0         when('Hessian::Type::Binary') { _write_chunks($wr,$$x{data}) }
  0            
  0            
  0            
  0            
  0            
  0            
61 0 0         when('Hessian::Type::String') { _write_chunks($wr,$$x{data},1) }
  0            
  0            
  0            
  0            
  0            
  0            
62 0 0         when('Unicode::String') { _write_chunks($wr,$x->as_string,1) }
  0            
  0            
  0            
  0            
  0            
  0            
63              
64 0 0         when('Hessian::Type::List') { my $idx = _search_ref($refs,$x);
  0            
  0            
65 0 0         if(defined $idx){
66 0           $wr->('QI' . Hessian::Tiny::Type::_l2n(pack 'l', $idx));
67             }else{
68 0           push @$refs,$x;
69 0           _write_list($$rf,$wr,$x);
70             }
71 0           }
  0            
  0            
  0            
72 0 0         when('Hessian::Type::Map') { my $idx = _search_ref($refs,$x);
  0            
  0            
73 0 0         if(defined $idx){
74 0           $wr->('QI' . Hessian::Tiny::Type::_l2n(pack 'l', $idx));
75             }else{
76 0           push @$refs,$x;
77 0           _write_map($$rf,$wr,$x);
78             }
79 0           }
  0            
  0            
  0            
80 0 0         when('Hessian::Type::Object') { my $idx = _search_ref($refs,$x);
  0            
  0            
81 0 0         if(defined $idx){
82 0           $wr->('QI' . Hessian::Tiny::Type::_l2n(pack 'l', $idx));
83             }else{
84 0           push @$refs,$x;
85 0           _write_map($$rf,$wr,$x);
86             }
87 0           }
  0            
  0            
  0            
88              
89             #when('Hessian::Fault') { _write_fault($wr,$x) }
90 0 0         when('REF') { $wr->('QI' . Hessian::Tiny::Type::_l2n(pack'l', first{$$x == $$refs[$_]}(0 .. $#$refs))) }
  0            
  0            
  0            
  0            
  0            
  0            
  0            
91              
92 0 0         when('') { # guessing begins
  0            
93 0           given($x){
  0            
  0            
  0            
94 0 0         when /^[\+\-]?(0x)?\d+$/ { my $bi = Math::BigInt->new($x);
  0            
  0            
95 0 0 0       if(Math::BigInt->new('-0x80000000')->bcmp($bi) <= 0 &&
    0 0        
96             Math::BigInt->new(' 0x7fffffff')->bcmp($bi) >= 0
97             ){ # Integer
98 0           $wr->('I' . Hessian::Tiny::Type::_l2n(pack 'l', $x));
99             }elsif(Math::BigInt->new('-0x8000000000000000')->bcmp($bi) <=0 &&
100             Math::BigInt->new(' 0x7fffffffffffffff')->bcmp($bi) >=0
101             ){ # Long
102 0           $wr->('L' . Hessian::Tiny::Type::_pack_q($x));
103             }else{ # too large to be number
104 0           _write_chunks($wr,$x,Encode::is_utf8($x,1));
105             }
106 0           }
  0            
  0            
  0            
107 0 0         when /^[\+\-]?\d*(\d+\.|\.\d+)\d*$/ { $wr->('D' . Hessian::Tiny::Type::_l2n(pack 'd', $x)) }
  0            
  0            
  0            
  0            
  0            
  0            
108 0 0         when /\D/ { _write_chunks($wr,$x, Encode::is_utf8($x,1)) }
  0            
  0            
  0            
  0            
  0            
  0            
109 0           default { die "unknown x: $x, @{[ref $x]}" }
  0            
  0            
  0            
  0            
  0            
  0            
  0            
110             }
111 0           }
  0            
  0            
  0            
112 0           default { die "_serialize_v2: unrecognized type (@{[ref $x]})" }
  0            
  0            
  0            
  0            
  0            
  0            
  0            
113             } # end given
114 0           };
115 0           return $f;
116             }
117             sub _search_ref { # return index, or undef if not found
118 0     0     my($refs,$r) = @_;
119 0           for my $i (0 .. $#$refs){
120 0 0         return $i if $refs->[$i] == $r;
121             }
122 0           return undef;
123             }
124             sub _write_map {
125 0     0     my($f,$wr,$x) = @_;
126 0 0         $wr->(defined $$x{type} ? 'M' : 'H');
127 0 0         _write_chunks($wr,(ref $$x{type} ? $$x{type}->{data} : $$x{type}),1) if defined $$x{type};
    0          
128 0 0         my @ar = 'HASH' eq ref $$x{data} ? (%{$$x{data}}) : (@{$$x{data}});
  0            
  0            
129 0           $f->($_) for(@ar);
130 0           $wr->('Z');
131             }
132             sub _write_list {
133 0     0     my($f,$wr,$x) = @_;
134 0 0 0       if($$x{type} && defined $$x{length}) {
    0          
    0          
135 0           $wr->('V');
136 0 0         _write_chunks($wr,(ref $$x{type} ? $$x{type}->{data} : $$x{type}),1);
137 0           $wr->('I' . Hessian::Tiny::Type::_l2n(pack 'l', $$x{length}));
138             }elsif(defined $$x{length}) {
139 0           $wr->('XI' . Hessian::Tiny::Type::_l2n(pack 'l', $$x{length}));
140             }elsif($$x{type}) {
141 0           $wr->('U');
142 0           _write_chunks($wr,$$x{type},1);
143             }else{
144 0           $wr->('W');
145             }
146 0           $f->($_) for(@{$$x{data}});
  0            
147 0 0         $wr->('Z') unless defined $$x{length};
148             }
149              
150             sub _write_chunks {
151 0     0     my($wr,$str,$utf8) = @_;
152 0 0         if(length $str > 0x7fff){
153 0 0         $wr->($utf8 ? 'R' : 'A');
154 0           $wr->("\x7f\xff");
155 0           $wr->(substr($str,0,0x7fff), $utf8);
156 0           _write_chunks($wr,substr($str,0x7fff),$utf8);
157             }else{
158 0 0         $wr->($utf8 ? 'S' : 'B');
159 0           $wr->(pack('n',length $str));
160 0           $wr->($str, $utf8);
161             }
162             }
163              
164             # reading 2.0
165             sub _make_object_reader {
166 0     0     my $h_flag=shift; # return hessian or perl data
167 0           my($obj_refs,$cls_refs,$typ_refs) = ([],[],[]);
168 0           my $f;
169             $f = sub {
170 0     0     my($rd,$h_flag_override) = @_;
171 0 0         $h_flag_override = $h_flag unless defined $h_flag_override;
172 0           my $rf = \$f;
173 0           Scalar::Util::weaken($rf);
174 0           my $x = $rd->(1);
175 0           given($x){
  0            
  0            
  0            
176 0 0         when('N') { return $h_flag_override ? Hessian::Type::Null->new() : undef }
  0 0          
  0            
  0            
  0            
  0            
  0            
177 0 0         when('T') { return $h_flag_override ? Hessian::Type::True->new() : 1 }
  0 0          
  0            
  0            
  0            
  0            
  0            
178 0 0         when('F') { return $h_flag_override ? Hessian::Type::False->new() : undef }
  0 0          
  0            
  0            
  0            
  0            
  0            
179              
180 0 0         when /[I\x80-\xd7]/ {
  0            
181 0           my $i;
182 0           given($x) {
  0            
  0            
  0            
183 0 0         when('I') { $i = unpack 'l', Hessian::Tiny::Type::_l2n($rd->(4)) }
  0            
  0            
  0            
  0            
  0            
  0            
184 0 0         when /[\x80-\xbf]/ { $i = -0x90 + ord $x }
  0            
  0            
  0            
  0            
  0            
  0            
185 0 0         when /[\xc0-\xcf]/ { $i = (-0xc8 + ord $x) * 0x100 + ord($rd->(1)) }
  0            
  0            
  0            
  0            
  0            
  0            
186 0 0         when /[\xd0-\xd7]/ { $i = (-0xd4 + ord $x) * 0x10000 +
  0            
  0            
187             ord($rd->(1)) * 0x100 +
188 0           ord($rd->(1)) }
  0            
  0            
  0            
189             }
190 0 0         return $h_flag_override ? Hessian::Type::Integer->new($i) : $i;
191 0           } # int
  0            
  0            
  0            
192              
193 0 0         when /[LY\x38-\x3f\xd8-\xff]/ {
  0            
194 0           my $l;
195 0           given($x) {
  0            
  0            
  0            
196 0 0         when('L') { $l = Hessian::Tiny::Type::_unpack_q($rd->(8)) }
  0            
  0            
  0            
  0            
  0            
  0            
197 0 0         when('Y') { $l = Math::BigInt->new(unpack 'l', Hessian::Tiny::Type::_l2n($rd->(4))) }
  0            
  0            
  0            
  0            
  0            
  0            
198 0 0         when /[\xd8-\xef]/ { $l = Math::BigInt->new(-0xe0 + ord $x) }
  0            
  0            
  0            
  0            
  0            
  0            
199 0 0         when /[\xf0-\xff]/ { $l = Math::BigInt->new((-0xf8 + ord $x) * 0x100 + ord($rd->(1))) }
  0            
  0            
  0            
  0            
  0            
  0            
200 0 0         when /[\x38-\x3f]/ { $l = Math::BigInt->new((-0x3c + ord $x) * 0x10000 +
  0            
  0            
201 0           ord($rd->(1)) * 0x100 + ord($rd->(1))) }
  0            
  0            
  0            
202             }
203 0 0         return $h_flag_override ? $l : $l->bstr;
204 0           } # long
  0            
  0            
  0            
205              
206 0 0         when /[D\[\\\]\^_]/ {
  0            
207 0           my $i;
208 0           given($x){
  0            
  0            
  0            
209 0 0         when('D') { $i = unpack 'd', Hessian::Tiny::Type::_l2n($rd->(8)) }
  0            
  0            
  0            
  0            
  0            
  0            
210 0 0         when('[') { $i = 0.0 }
  0            
  0            
  0            
  0            
  0            
  0            
211 0 0         when('\\') { $i = 1.0 }
  0            
  0            
  0            
  0            
  0            
  0            
212 0 0         when(']') { $i = unpack 'c', $rd->(1) }
  0            
  0            
  0            
  0            
  0            
  0            
213 0 0         when('^') { $i = unpack 's', Hessian::Tiny::Type::_l2n($rd->(2)) }
  0            
  0            
  0            
  0            
  0            
  0            
214 0 0         when('_') { $i = unpack('l', Hessian::Tiny::Type::_l2n($rd->(4)))/1000 }
  0            
  0            
  0            
  0            
  0            
  0            
215             }
216 0 0         return $h_flag_override ? Hessian::Type::Double->new($i) : $i;
217 0           } # double
  0            
  0            
  0            
218              
219 0 0         when('J') { my$msec = Hessian::Tiny::Type::_unpack_q($rd->(8));
  0            
  0            
220 0 0         return $h_flag_override
221             ? Hessian::Type::Date->new($msec) #milli seconds
222             : $msec->bdiv(1000)->bstr #seconds
223             ;
224 0           } # date
  0            
  0            
  0            
225 0 0         when('K') { my$ms = Math::BigInt->new(unpack 'l', Hessian::Tiny::Type::_l2n($rd->(4)));
  0            
  0            
226 0           $ms->bmul(60*1000); # min to milli sec
227 0 0         return $h_flag_override
228             ? Hessian::Type::Date->new($ms) #milli seconds
229             : $ms->bdiv(1000)->bstr #seconds
230             ;
231 0           } # date (compact)
  0            
  0            
  0            
232              
233 0 0         when /[RS\x00-\x1f\x30-\x33]/ { $rd->(-1);
  0            
  0            
234 0 0         return $h_flag_override
235             ? Hessian::Type::String->new(_read_string($rd))
236             : _read_string($rd)
237             ;
238 0           } # string
  0            
  0            
  0            
239 0 0         when /[AB\x20-\x2f\x34-\x37]/ { $rd->(-1);
  0            
  0            
240 0 0         return $h_flag_override
241             ? Hessian::Type::Binary->new(_read_binary($rd))
242             : _read_binary($rd)
243             ;
244 0           } # binary
  0            
  0            
  0            
245 0 0         when /[U-X\x70-\x7f]/ { my $v = Hessian::Type::List->new([]);
  0            
  0            
246 0 0         my $res = $h_flag_override ? $v : $v->{data};
247 0           push @$obj_refs,$res;
248 0           _read_list($$rf,$rd,$v,$x,$typ_refs);
249 0           return $res;
250 0           } # list
  0            
  0            
  0            
251 0 0         when /[MH]/ { tie my %h,'Tie::RefHash::Nestable';
  0            
  0            
252 0           my $m = Hessian::Type::Map->new(\%h);
253 0 0         my $res = $h_flag_override ? $m : $m->{data};
254 0           push @$obj_refs,$res;
255 0           _read_map($$rf,$rd,$m,$x,$typ_refs,$h_flag_override);
256 0           return $res;
257 0           } # map
  0            
  0            
  0            
258 0 0         when('C') { my $c = [];
  0            
  0            
259 0           push @$cls_refs, $c;
260 0           _read_class($$rf,$rd,$c);
261 0           return $$rf->($rd); # keep reading after class-def
262 0           } # class def
  0            
  0            
  0            
263 0 0         when /[O\x60a-o]/ { tie my %h,'Tie::RefHash::Nestable';
  0            
  0            
264 0           my $o = Hessian::Type::Object->new(\%h);
265 0 0         my $res = $h_flag_override ? $o : $o->{data};
266 0           push @$obj_refs,$res;
267 0           _read_object($$rf,$rd,$o,$cls_refs,$x);
268 0           return $res;
269 0           } # object
  0            
  0            
  0            
270              
271 0 0         when('Q') { return $obj_refs->[$$rf->($rd,0)] }
  0            
  0            
  0            
  0            
  0            
  0            
272 0           default { die "hessian v2 reader: unknown type ($x)" }
  0            
  0            
  0            
  0            
  0            
  0            
273             }
274 0           };
275 0           return $f;
276             }
277             sub _read_object {
278 0     0     my($rf,$rd,$o,$c_refs,$x) = @_;
279 0           my $idx;
280 0 0         if($x eq 'O') { $idx = $rf->($rd,0) }
  0            
281 0           else { $idx = -0x60 + unpack 'C', $x }
282 0 0         die "object class index out of bound $idx: $#$c_refs" if $idx > $#$c_refs;
283 0           my @fields = @{$c_refs->[$idx]};
  0            
284 0           $o->{type} = shift @fields;
285 0           $o->{data}->{$_} = $rf->($rd) for @fields;
286             }
287             sub _read_class {
288 0     0     my($rf,$rd,$c) = @_;
289 0           my $c_name = $rf->($rd,0);
290 0           my $len = $rf->($rd,0);
291 0           push @$c, $c_name;
292 0           push @$c, $rf->($rd,0) for(1 .. $len);
293             }
294             sub _read_map {
295 0     0     my($rf,$rd,$v,$x,$typ_refs,$hflg) = @_;
296 0 0         if($x eq 'M'){ # typed map
297 0           $v->{type} = $rf->($rd,0);
298 0 0 0       if(defined $v->{type} and Scalar::Util::looks_like_number($v->{type}) ){
    0          
299 0           $v->{type} = $typ_refs->[$v->{type}];
300             }elsif(0 < length $v->{type}){
301 0           push @$typ_refs, $v->{type};
302             }
303             }
304 0           while('Z' ne $rd->(1)){
305 0           $rd->(-1);
306 0           my $k = $rf->($rd,$hflg);
307 0           $v->{data}->{$k} = $rf->($rd,$hflg);
308             }
309             }
310             sub _read_list {
311 0     0     my($rf,$rd,$v,$x,$typ_refs) = @_;
312 0 0         $v->{type} = $rf->($rd,1) if $x =~ /[MUV\x70-\x77]/;
313 0 0 0       if(defined $v->{type} and 'Hessian::Type::Integer' eq ref $v->{type} ){
    0 0        
314 0           $v->{type} = $typ_refs->[$v->{type}];
315             }elsif(defined $v->{type} and 'Hessian::Type::String' eq ref $v->{type}){
316 0           push @$typ_refs, $v->{type}->{data};
317             }
318              
319 0 0         $v->{length} = $rf->($rd,0) if $x =~ /[VX]/;
320 0 0         $v->{length} = -0x70 + unpack 'C', $x if $x =~ /[\x70-\x77]/;
321 0 0         $v->{length} = -0x78 + unpack 'C', $x if $x =~ /[\x78-\x7f]/;
322              
323 0 0 0       if(defined $v->{length} and $v->{length} > 0){
    0          
324 0           push @{$v->{data}}, $rf->($rd) for(1 .. $v->{length});
  0            
325             }elsif(not defined $v->{length}){
326 0           while('Z' ne $rd->(1)){
327 0           $rd->(-1);
328 0           push @{$v->{data}}, $rf->($rd);
  0            
329             }
330             }
331             }
332             sub _read_binary {
333 0     0     my $rd = shift;
334 0           my $m = $rd->(1);
335 0           my $len;
336 0           given($m){
  0            
  0            
  0            
337 0 0         when /[AB]/ { $len = unpack 'n', $rd->(2) }
  0            
  0            
  0            
  0            
  0            
  0            
338 0 0         when /[\x20-\x2f]/ { $len = -0x20 + unpack 'C', $m }
  0            
  0            
  0            
  0            
  0            
  0            
339 0 0         when /[\x34-\x37]/ { $len = (-0x34 + unpack'C',$m) * 0x100 + unpack('C',$rd->(1)) }
  0            
  0            
  0            
  0            
  0            
  0            
340 0           default { die "unknown Binary marker: $m" }
  0            
  0            
  0            
  0            
  0            
  0            
341             } # end given $x
342 0           my $buf = $rd->($len,0);
343 0 0         return $buf . _read_binary($rd) if $m eq 'A';
344 0           return $buf;
345             }
346             sub _read_string {
347 0     0     my $rd = shift;
348 0           my $m = $rd->(1);
349 0           my $len;
350 0           given($m){
  0            
  0            
  0            
351 0 0         when /[RS]/ { $len = unpack 'n', $rd->(2) }
  0            
  0            
  0            
  0            
  0            
  0            
352 0 0         when /[\x00-\x1f]/ { $len = unpack 'C', $m }
  0            
  0            
  0            
  0            
  0            
  0            
353 0 0         when /[\x30-\x33]/ { $len = (unpack('C',$m) - 0x30 ) * 0x100 + unpack('C',$rd->(1)) }
  0            
  0            
  0            
  0            
  0            
  0            
354 0           default { die "unknown String marker: $m" }
  0            
  0            
  0            
  0            
  0            
  0            
355             } # end given $x
356 0           my $buf = $rd->($len,1);
357 0 0         return $buf . _read_string($rd) if $m eq 'R';
358 0           return $buf;
359             }
360             =head1 AUTHOR
361              
362             Ling Du, C<< >>
363              
364             =head1 BUGS
365              
366             Please report any bugs or feature requests to C, or through
367             the web interface at L. I will be notified, and then you'll
368             automatically be notified of progress on your bug as I make changes.
369              
370              
371              
372              
373             =head1 SUPPORT
374              
375             You can find documentation for this module with the perldoc command.
376              
377             perldoc Hessian::Tiny::ConvertorV2
378              
379              
380             You can also look for information at:
381              
382             =over 4
383              
384             =item * RT: CPAN's request tracker
385              
386             L
387              
388             =item * AnnoCPAN: Annotated CPAN documentation
389              
390             L
391              
392             =item * CPAN Ratings
393              
394             L
395              
396             =item * Search CPAN
397              
398             L
399              
400             =back
401              
402              
403             =head1 ACKNOWLEDGEMENTS
404              
405              
406             =head1 LICENSE AND COPYRIGHT
407              
408             Copyright 2010 Ling Du.
409              
410             This program is free software; you can redistribute it and/or modify it
411             under the terms of either: the GNU General Public License as published
412             by the Free Software Foundation; or the Artistic License.
413              
414             See http://dev.perl.org/licenses/ for more information.
415              
416              
417             =cut
418              
419             1; # End of Hessian::Tiny::ConvertorV2