File Coverage

blib/lib/Erlang/Port.pm
Criterion Covered Total %
statement 6 193 3.1
branch 0 92 0.0
condition 0 39 0.0
subroutine 2 16 12.5
pod 5 5 100.0
total 13 345 3.7


line stmt bran cond sub pod time code
1             ## ----------------------------------------------------------------------------
2             # Erlang::Port.
3             # -----------------------------------------------------------------------------
4             # Mastering programmed by YAMASHINA Hio
5             #
6             # Copyright 2007 YAMASHINA Hio
7             # -----------------------------------------------------------------------------
8             # $Id: /perl/Erlang-Port/lib/Erlang/Port.pm 388 2007-05-22T11:24:11.684354Z hio $
9             # -----------------------------------------------------------------------------
10             package Erlang::Port;
11 1     1   23716 use strict;
  1         2  
  1         45  
12 1     1   6 use warnings;
  1         2  
  1         3728  
13              
14             our $VERSION = '0.04';
15              
16             # constans.
17             our $SMALL_INTEGER_EXT = 'a';
18             our $INTEGER_EXT = 'b';
19             our $FLOAT_EXT = 'c';
20             our $ATOM_EXT = 'd';
21             our $REFERENCE_EXT = 'e';
22             our $NEW_REFERENCE_EXT = 'r';
23             our $PORT_EXT = 'f';
24             our $PID_EXT = 'g';
25             our $SMALL_TUPLE_EXT = 'h';
26             our $LARGE_TUPLE_EXT = 'i';
27             our $NIL_EXT = 'j';
28             our $STRING_EXT = 'k';
29             our $LIST_EXT = 'l';
30             our $BINARY_EXT = 'm';
31             our $BIT_BINARY_EXT = 'M';
32             our $SMALL_BIG_EXT = 'n';
33             our $LARGE_BIG_EXT = 'o';
34             our $NEW_FUN_EXT = 'p';
35             our $EXPORT_EXT = 'q';
36             our $FUN_EXT = 'u';
37              
38             our $NEW_CACHE = 'N';
39             our $CACHED_ATOM = 'C';
40              
41             our $COMPRESSED = 'P';
42             our $VERSION_MAGIC = pack('C',131); # 130 in erlang 4.2.
43              
44             1;
45              
46             # -----------------------------------------------------------------------------
47             # $pkg->new(\&callback);
48             #
49             sub new
50             {
51 0     0 1   my $pkg = shift;
52 0           my $callback = shift;
53            
54 0           my $this = bless {}, $pkg;
55 0           $this->{callback} = $callback;
56 0           if( 0 )
57             {
58             my $logfile = 'port.out';
59             open(my$out, '>>', $logfile) or die "$logfile: $!";
60             select((select($out),$|=1)[0]);
61             print $out "start: $$, ".localtime(time)."\r\n";
62             $this->{log} = $out;
63             }
64 0           $this;
65             }
66              
67             # -----------------------------------------------------------------------------
68             # dtor.
69             #
70             sub DESTROY
71             {
72 0     0     my $this = shift;
73 0 0         if( my $out = $this->{log} )
74             {
75 0           print $out "end: $$, ".localtime(time)."\r\n";
76             }
77             }
78              
79             # -----------------------------------------------------------------------------
80             # $port->loop().
81             #
82             sub loop()
83             {
84 0     0 1   my $this = shift;
85            
86 0           binmode(STDOUT);
87 0           $|=1;
88 0           for(;;)
89             {
90 0           my $cmd = $this->_read_cmd();
91 0           my $obj = $this->decode($cmd);
92 0           my $ret = $this->{callback}->($obj, $this);
93 0           my $bin = $this->encode($ret);
94 0           print pack("n",length($bin)).$bin;
95             }
96             }
97              
98             # -----------------------------------------------------------------------------
99             # $port->_read_cmd() @ private.
100             # read erlang external command.
101             #
102             sub _read_cmd
103             {
104 0     0     my $this = shift;
105 0           my $out = $this->{log};
106            
107 0 0         $out and print $out "read cmd ...\r\n";
108 0           my $len = $this->_read_exact(2);
109 0           $len = unpack("n", $len);
110 0 0         $out and print $out "read cmd, len = $len ...\r\n";
111 0           my $data = $this->_read_exact($len);
112 0           my $x = $data;
113 0           $x =~ s/([^ -~])/sprintf('[%02d]',unpack("C",$1))/eg;
  0            
114 0 0         $out and print $out "read cmd, data($len) = $x ...\r\n";
115 0           $data;
116             }
117              
118             # -----------------------------------------------------------------------------
119             # $port->_read_exact($len) @ private.
120             # read $len bytes.
121             #
122             sub _read_exact
123             {
124 0     0     my $this = shift;
125 0           my $out = $this->{log};
126            
127 0           my $len = shift;
128 0           my $buf = '';
129 0           for(1..$len)
130             {
131 0           my $ret = sysread(STDIN, $buf, 1, length $buf);
132 0 0         if( !defined($ret) )
133             {
134 0 0         $out and print $out "sysread: $!\r\n";
135 0           exit -1;
136             }
137 0 0         if( !$ret )
138             {
139 0 0         $out and print $out "EOF.\r\n";
140 0           exit 0;
141             }
142 0 0         $out and print $out ">> $_ ".sprintf('[%02d]',unpack("C",substr($buf,-1)))."\r\n";
143             }
144 0           $buf;
145             }
146              
147             # -----------------------------------------------------------------------------
148             # $port->decode($bin).
149             # decode external sequence into Erlang object.
150             #
151             sub decode
152             {
153 0     0 1   my $this = shift;
154 0           my $out = $this->{log};
155            
156 0           my $data = shift;
157 0 0         $out and print $out "decode...\n";
158 0 0         if( $data !~ s/^$VERSION_MAGIC// )
159             {
160 0 0         $out and print $out "no magic.\r\n";
161 0           return;
162             }
163 0           my @stack = ([]);
164 0           my @pop = (0);
165 0           while($data ne '')
166             {
167 0 0         if( $data =~ s/^$NIL_EXT// )
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
168             {
169 0           push(@{$stack[-1]}, []);
  0            
170             }elsif( $data =~ s/^$SMALL_INTEGER_EXT(.)//s )
171             {
172 0           push(@{$stack[-1]}, unpack("C",$1));
  0            
173             }elsif( $data =~ s/^$INTEGER_EXT(.{4})//s )
174             {
175 0           push(@{$stack[-1]}, unpack("N",$1));
  0            
176             }elsif( $data =~ s/^$FLOAT_EXT//s )
177             {
178 0           my $s = substr($data, 0, 31, '');
179 0           $s =~ tr/\0//d;
180 0           push(@{$stack[-1]}, $s);
  0            
181             }elsif( $data =~ s/^$STRING_EXT(.{2})//s )
182             {
183 0           my $len = unpack("n",$1);
184 0           my $str = substr($data, 0, $len, '');
185 0           push(@{$stack[-1]}, $str);
  0            
186             }elsif( $data =~ s/^$ATOM_EXT(.{2})//s )
187             {
188 0           my $len = unpack("n",$1);
189 0           my $atom = $this->_newAtom(substr($data, 0, $len, ''));
190 0           push(@{$stack[-1]}, $atom);
  0            
191             }elsif( $data =~ s/^$BINARY_EXT(.{4})//s )
192             {
193 0           my $len = unpack("N",$1);
194 0           my $binary = $this->_newBinary(substr($data, 0, $len, ''));
195 0           push(@{$stack[-1]}, $binary);
  0            
196             }elsif( $data =~ s/^$LIST_EXT(.{4})//s )
197             {
198 0           my $len = unpack("N",$1);
199 0           my $next = [];
200 0           push(@{$stack[-1]}, $next);
  0            
201 0           push(@stack, $next);
202 0           push(@pop, $len);
203 0           next;
204             }elsif( $data =~ s/^$SMALL_TUPLE_EXT(.)//s )
205             {
206 0           my $len = unpack("C",$1);
207 0           my $next = $this->_newTuple([]);
208 0           push(@{$stack[-1]}, $next);
  0            
209 0           push(@stack, $next);
210 0           push(@pop, $len);
211 0           next;
212             }elsif( $data =~ s/^$LARGE_TUPLE_EXT(....)//s )
213             {
214 0           my $len = unpack("N",$1);
215 0           my $next = $this->_newTuple([]);
216 0           push(@{$stack[-1]}, $next);
  0            
217 0           push(@stack, $next);
218 0           push(@pop, $len);
219 0           next;
220             }elsif( $data =~ s/^$PID_EXT//s )
221             {
222 0           my $atom_mark = substr($data, 0, 1, '');
223 0   0       my $atom_len = unpack("n", substr($data, 0, 2, '')) || 0;
224 0   0       my $atom = substr($data, 0, $atom_len, '') || 0;
225 0   0       my $pid = unpack("N", substr($data, 0, 4, '')) || 0;
226 0   0       my $serial = unpack("N", substr($data, 0, 4, '')) || 0;
227 0   0       my $creation = unpack("C", substr($data, 0, 1, '')) || 0;
228 0           my $obj = $this->_newPid([$atom, $pid, $serial, $creation]);
229 0           push(@{$stack[-1]}, $obj);
  0            
230             }else
231             {
232             #$REFERENCE_EXT = 'e';
233             #$NEW_REFERENCE_EXT = 'r';
234             #$BIT_BINARY_EXT = 'M';
235             #$SMALL_BIG_EXT = 'n';
236             #$LARGE_BIG_EXT = 'o';
237             #$NEW_FUN_EXT = 'p';
238             #$EXPORT_EXT = 'q';
239             #$FUN_EXT = 'u';
240             #$NEW_CACHE = 'N';
241             #$CACHED_ATOM = 'C';
242             #$COMPRESSED = 'P';
243 0           my $id = substr($data,0,1);
244 0           my $chr = unpack("C",$id);
245 0 0         $out and print $out "not ready $id ($chr).\r\n";
246 0           last;
247             }
248 0           while( --$pop[-1]==0 )
249             {
250 0           pop @pop;
251 0           pop @stack;
252 0 0         if( !UNIVERSAL::isa($data, 'Erlang::Tuple') )
253             {
254             # List.
255 0           $data =~ s/^$NIL_EXT//;
256 0           my $list = $stack[-1]->[-1];
257 0           my $hash = {};
258 0           foreach my $item (@$list)
259             {
260 0 0 0       if( !UNIVERSAL::isa($item, 'Erlang::Tuple') || @$item!=2 )
261             {
262 0           $hash = undef;
263 0           last;
264             }
265 0           my $key = $this->to_s($item->[0]);
266 0 0         if( !defined($key) )
267             {
268 0           $hash = undef;
269 0           last;
270             }
271 0           $hash->{$key} = $item->[1];
272             }
273 0 0 0       if( $hash && @$list )
274             {
275 0           $stack[-1]->[-1] = $hash;
276             }
277             }
278             }
279             }
280 0           $stack[0]->[0];
281             }
282              
283             # -----------------------------------------------------------------------------
284             # $port->to_s($obj);
285             sub to_s
286             {
287 0     0 1   my $this = shift;
288 0           my $obj = shift;
289 0 0 0       if( defined($obj) && !ref($obj) )
    0 0        
    0 0        
    0 0        
      0        
290             {
291 0           $obj;
292             }elsif( $obj && ref($obj) eq 'ARRAY' && @$obj==0 )
293             {
294 0           "";
295             }elsif( ref($obj) && UNIVERSAL::isa($obj, 'Erlang::Atom') )
296             {
297 0           $$obj;
298             }elsif( ref($obj) && UNIVERSAL::isa($obj, 'Erlang::Binary') )
299             {
300 0           $$obj;
301             }else
302             {
303 0           undef;
304             }
305             }
306              
307             # -----------------------------------------------------------------------------
308             # $port->_newAtom($text) @ private.
309             # create Erlang::Atom object.
310             #
311             sub _newAtom
312             {
313 0     0     my $this = shift;
314 0           my $atom = shift;
315 0           bless \$atom, 'Erlang::Atom';
316             }
317              
318             # -----------------------------------------------------------------------------
319             # $port->_newBinary($bytes) @ private.
320             # create Erlang::Binary object.
321             #
322             sub _newBinary
323             {
324 0     0     my $this = shift;
325 0           my $binary = shift;
326 0           bless \$binary, 'Erlang::Binary';
327             }
328              
329             # -----------------------------------------------------------------------------
330             # $port->_newTuple($tuple) @ private.
331             # create Erlang::Tuple object.
332             #
333             sub _newTuple
334             {
335 0     0     my $this = shift;
336 0   0       my $tuple = shift || [];
337 0           bless $tuple, 'Erlang::Tuple';
338             }
339              
340             # -----------------------------------------------------------------------------
341             # $port->_newPid(\@info) @ private.
342             # create Erlang::Pid object.
343             #
344             sub _newPid
345             {
346 0     0     my $this = shift;
347 0           my $pid = shift;
348 0           bless $pid, 'Erlang::Pid';
349             }
350              
351             # -----------------------------------------------------------------------------
352             # $port->encode($obj).
353             # encode Erlang obj into external sequence.
354             #
355             sub encode
356             {
357 0     0 1   my $this = shift;
358 0           my $obj = shift;
359            
360 0           my $bin = $VERSION_MAGIC;
361 0           $bin .= $this->_encode($obj);
362 0           $bin;
363             }
364              
365             # -----------------------------------------------------------------------------
366             # $port->_encode_list(@data) @ private.
367             # encode multiple objects.
368             #
369             sub _encode_list
370             {
371 0     0     my $this = shift;
372 0           join('', map{ $this->_encode($_) } @_);
  0            
373             }
374              
375             # -----------------------------------------------------------------------------
376             # $port->_encode($obj) @ private.
377             # encode Erlang obj into external format.
378             #
379             sub _encode
380             {
381 0     0     my $this = shift;
382 0           my $obj = shift;
383            
384 0 0         if( UNIVERSAL::isa($obj, 'Erlang::Atom') )
    0          
    0          
    0          
    0          
    0          
385             {
386 0           $ATOM_EXT . pack("n",length($$obj)) . $$obj;
387             }elsif( UNIVERSAL::isa($obj, 'Erlang::Binary') )
388             {
389 0           $BINARY_EXT . pack("N",length($$obj)) . $$obj;
390             }elsif( UNIVERSAL::isa($obj, 'Erlang::Pid') )
391             {
392 0           my ($atom, $pid, $serial, $creation) = @$obj;
393 0           $PID_EXT . $ATOM_EXT . pack("n", length($atom)). $atom . pack("N", $pid) . pack("N", $serial) . pack("C", $creation);
394             }elsif( UNIVERSAL::isa($obj, 'ARRAY') )
395             {
396 0 0         if( UNIVERSAL::isa($obj, 'Erlang::Tuple') )
    0          
397             {
398 0           my $n = @$obj;
399 0 0         if( $n<256 )
400             {
401 0           $SMALL_TUPLE_EXT . pack("C",0+@$obj) . $this->_encode_list(@$obj);
402             }else
403             {
404 0           $LARGE_TUPLE_EXT . pack("N",0+@$obj) . $this->_encode_list(@$obj);
405             }
406             }elsif( @$obj==0 )
407             {
408 0           $NIL_EXT;
409             }else
410             {
411 0           $LIST_EXT . pack('N', 0+@$obj) . $this->_encode_list(@$obj, []);
412             }
413             }elsif( UNIVERSAL::isa($obj, 'HASH') )
414             {
415             # List of Tuples.
416 0           my @conv;
417 0           foreach my $key (sort keys %$obj)
418             {
419 0           my $atom = $this->_newAtom($key);
420 0           push(@conv, $this->_newTuple([$atom, $obj->{$key}]));
421             }
422 0           $this->_encode(\@conv);
423             }elsif( !ref($obj) )
424             {
425 0 0 0       if( !defined($obj) )
    0          
    0          
426             {
427 0           $this->_encode($this->_newAtom('undefined'));
428             }elsif( $obj =~ /^-?\d+$/ && $obj eq unpack("N", pack("N",$obj)) )
429             {
430 0 0 0       if( $obj>=0 && $obj<=255 )
431             {
432 0           $SMALL_INTEGER_EXT . pack("C", $obj);
433             }else
434             {
435 0           $INTEGER_EXT . pack("N", $obj);
436             }
437             }elsif( $obj =~ /^-?\d+(\.\d+)?(e[-+]?\d+)$/ )
438             {
439 0           $FLOAT_EXT . substr(sprintf("%.20e", $obj).("\0"x31), 0, 31);
440             }else
441             {
442 0           $STRING_EXT . pack('n', length($obj)) . $obj;
443             }
444             }else
445             {
446 0 0         if( my $out = $this->{log} )
447             {
448 0           print $out "not ready [$obj].\r\n";
449             }
450 0           $this->_encode($this->_newAtom('encode_not_ready'));
451             }
452             }
453              
454             # -----------------------------------------------------------------------------
455             # End of Module.
456             # -----------------------------------------------------------------------------
457             __END__