File Coverage

blib/lib/Data/Sofu/Binary.pm
Criterion Covered Total %
statement 66 107 61.6
branch 16 46 34.7
condition n/a
subroutine 11 15 73.3
pod 10 10 100.0
total 103 178 57.8


line stmt bran cond sub pod time code
1             ###############################################################################
2             #Binary.pm
3             #Last Change: 2009-28-01
4             #Copyright (c) 2009 Marc-Seabstian "Maluku" Lucksch
5             #Version 0.3
6             ####################
7             #This file is part of the sofu.pm project, a parser library for an all-purpose
8             #ASCII file format. More information can be found on the project web site
9             #at http://sofu.sourceforge.net/ .
10             #
11             #sofu.pm is published under the terms of the MIT license, which basically means
12             #"Do with it whatever you want". For more information, see the license.txt
13             #file that should be enclosed with libsofu distributions. A copy of the license
14             #is (at the time of this writing) also available at
15             #http://www.opensource.org/licenses/mit-license.php .
16             ###############################################################################
17              
18             =head1 NAME
19              
20             Data::Sofu::Binary - Interface to various binary drivers
21              
22             =head1 DESCRIPTION
23              
24             This module can be used to convert complex data structures and SofuObject trees to binary files and streams.
25              
26             =head1 Synopsis
27              
28             use Data::Sofu qw/readSofu writeSofuBinary/;
29             my $tree = [%ENV];
30             $tree->{Foo}=@INC;
31             writeSofuBinary("env.sofu",$tree); #Write as a binary sofu file
32             my $tree2=readSofu("env.sofu"); #Reading doesn't care if its binary or normal sofu.
33              
34             #Or using just this module:
35             my $tree = [%ENV];
36             $tree->{Foo}=@INC;
37             require Data::Sofu::Binary;
38             my $bsofu=Data::Sofu::Binary->new();
39             my $bstream = $bsofu->pack($tree);
40             my $tree2=$bsofu->read(\$tree); # This can only read binary data.
41              
42             #More detailed:
43             writeSofuBinary("env.sofu",$tree,$comments,$encoding,$byteorder,$mark); #For details on these parameters see the pack() Method.
44              
45             =head1 SYNTAX
46              
47             This Module is pure OO, exports nothing
48              
49             =cut
50              
51              
52             package Data::Sofu::Binary;
53 1     1   6 use strict;
  1         2  
  1         53  
54 1     1   7 use warnings;
  1         3  
  1         72  
55              
56             our $VERSION="0.3";
57             #We are really going to need these modules:
58 1     1   6 use Encode;
  1         2  
  1         138  
59 1     1   7 use Carp qw/confess/;
  1         1  
  1         2530  
60             require Data::Sofu;
61              
62             =head1 Binary Drivers
63              
64             These are the known binary drivers (for now):
65              
66             =over
67              
68             =item "000_002_000_000"
69              
70             C
71              
72             Sofu binary version 0.2.0.0 Driver.
73              
74             =back
75              
76              
77              
78             B
79              
80             Data::Sofu's writeBinary will always take the latest stable one.
81              
82             =cut
83              
84             my %versions = (
85             "000_002_000_000"=>"Data::Sofu::Binary::Bin0200"
86             );
87              
88             =head1 METHODS
89              
90             These Methods are also avaiable for the returned binary driver.
91              
92             Also see the C or whatever driver you are using for more methods.
93              
94             =head2 new([DRIVER])
95              
96             Creates a new Binary Driver using DRIVER or the latest one available.
97              
98             require Data::Sofu::Binary;
99             $bsofu = Data::Sofu::Binary->new(); #Using the latest.
100             $bsofu = Data::Sofu::Binary->new("000_002_000_000"); Taking a specific one.
101             #You can call it directly:
102             require C;
103             $bsofu = C->new(); #The same
104              
105             =cut
106              
107             sub new {
108 7     7 1 17 my $class=shift;
109 7         22 my $version = shift;
110 7 100       35 $version = "000_002_000_000" unless $version;
111 7         32 $version = $versions{$version};
112 7 50       21 $version = "0200.pm" unless $version;
113 7         817 eval "require $version";
114 7 50       41 confess $@ if $@;
115 7         63 return $version->new();
116             }
117              
118             =head2 warn()
119              
120             Internal method, will throw an exception containing a stacktrace and the offset of the file where it happened.
121              
122             =cut
123              
124              
125             sub warn {
126 0     0 1 0 my $self=shift;
127             #croak "Sofu Warning, Binary decoder: @_";
128 0         0 confess "Sofu Warning, Binary mode: @_ at offset $self->{OFFSET}";
129             }
130              
131             =head2 die()
132              
133             Internal method, will throw an exception containing a stacktrace and the offset of the file where it happened.
134              
135             =cut
136              
137              
138             sub die {
139 0     0 1 0 my $self=shift;
140 0         0 confess "Sofu Error, Binary mode: @_ at offset $self->{OFFSET}";
141             }
142              
143             =head2 open(FILE)
144              
145             A helper method to open files
146              
147             File can be:
148              
149             A filename, (the file will be opened in raw mode)
150              
151             a filehandle, (will be set to binmode)
152              
153             or a scalarref (data will be written to/form the referenced scalar
154              
155             =cut
156              
157             sub open { #Opens the data;
158 58     58 1 114 local $_;
159 58         140 my $self=shift;
160 58         97 my $data=shift;
161 58         200 $self->{OFFSET}=0;
162 58 50       308 if (ref $data eq "GLOB") {
    50          
    0          
163 0         0 binmode $data;
164 0         0 $self->{IN} = $data;
165             }
166             elsif (ref $data eq "SCALAR") {
167 1     1   6 CORE::open my $in, '<:utf8', $data;
  1         2  
  1         9  
  58         1498  
168 58         4105 binmode $in;
169 58         317 $self->{IN}=$in;
170             }
171             elsif (ref $data) {
172 0         0 $self->warn("Unsupported Data Input Method:",ref $data);
173             }
174             else {
175 0 0       0 CORE::open (my $in,'<:raw',$data) or $self->die("Can't open input file $data: $!");
176 0         0 binmode $in;
177 0         0 $self->{IN}=$in;
178             }
179             }
180              
181             =head2 openout(FILE)
182              
183             Same as open() for output.
184              
185             =cut
186              
187             sub openout { #Opens the data;
188 0     0 1 0 local $_;
189 0         0 my $self=shift;
190 0         0 my $data=shift;
191 0         0 $self->{OFFSET}=0;
192 0 0       0 if (ref $data eq "GLOB") {
    0          
    0          
193 0         0 binmode $data;
194 0         0 $self->{OUT} = $data;
195             }
196             elsif (ref $data eq "SCALAR") {
197 0         0 CORE::open my $out, '>:utf8', $data;
198 0         0 binmode $out;
199 0         0 $self->{OUT}=$out;
200             }
201             elsif (ref $data) {
202 0         0 $self->warn("Unsupported Data Input Method:",ref $data);
203             }
204             else {
205 0 0       0 CORE::open (my $out,'>:raw',$data) or $self->die("Can't open output file $data: $!");
206 0         0 binmode $out;
207 0         0 $self->{OUT}=$out;
208             }
209             }
210              
211             =head2 get(AMOUNT)
212              
213             Internal method, used to read AMOUNT bytes from the filestream.
214              
215             =cut
216              
217             sub get { #Reads some bytes..
218 9722     9722 1 12092 local $_;
219 9722         17560 my $self=shift;
220 9722         23551 my $in =$self->{IN};
221 9722         14443 my $amount = shift;
222 9722         11998 my $data;
223 9722         33758 my $read = CORE::read($in,$data,$amount);
224 9722 50       27408 $self->die("Error while reading: $!") unless defined $read;
225 9722 100       25024 return undef unless $read;
226 9664         27326 $self->{OFFSET}+=$read;
227 9664 50       22735 $self->die("Can't read any more bytes, file corrupt?") if $read < $amount;
228 9664         57452 return $data;
229             }
230              
231             =head2 unpackHeader()
232              
233             Internal method, determines endianess and version the binary file was written in.
234              
235             Returns ByteOrderMark and Sofu Version.
236              
237             =cut
238              
239             sub unpackHeader {
240 58     58 1 118 my $self=shift;
241 58         247 my $end = $self->get(2);
242 58 100       209 if ($end eq "So") {
243 51         210 my $t = $self->get(2);
244 51 50       154 $self->die("Incomplete Mark: $t") if $t ne "fu";
245 51         138 $end = $self->get(2)
246             }
247 58         330 my $bom = unpack("S",$end);
248 58         256 my $version = $self->get(4);
249 58 50       192 $self->die("Can't read version, incomplete Header!") unless defined $version;
250 58         337 my @v = unpack ("CCCC",$version);
251 58         183 return ($bom,join("_",map {sprintf("%03d",$_)} @v));
  232         1031  
252              
253             }
254              
255             =head2 read(FILE)
256              
257             Reads FILE in binary mode and returns a perl datastructure (Hashes, Arrays, Scalars)
258              
259             See open() for info on the FILE parameter.
260              
261             Loads automatically the right driver for FILE, no matter what driver is in use right now. But it will keep the current driver if it can read it.
262              
263             Will not change the driver you are currently using!
264              
265             =cut
266              
267              
268             sub read { #Perl Structure Parser
269 26     26 1 5580 local $_;
270 26         54 my $self=shift;
271 26         111 $self->{COMMENTS}=[];
272 26         295 $self->open(shift);
273 26         229 my ($bom, $ver) = $self->unpackHeader();
274 26 50       270 return $self->unpack($bom) if ($self->{SUPPORTED}->{$ver});
275 0         0 my $module=$versions{$ver};
276 0 0       0 $self->die("Unknown Version: $ver") unless $module;
277 0         0 eval "require $module";
278 0 0       0 confess $@ if $@;
279 0         0 my $m = "$module"->new();
280 0         0 return $m->unpack($bom);
281            
282              
283             }
284              
285              
286             =head2 load(FILE)
287              
288             Reads FILE in binary mode and returns a Sofu datastructure (Data::Sofu::Object's, Maps, Lists and Values)
289              
290             See open() for info on the FILE parameter.
291              
292             Loads automatically the right driver for FILE, no matter what driver is in use right now. But it will keep the current driver if it can read it.
293              
294             Will not change the driver you are currently using!
295              
296             =cut
297              
298              
299             sub load { #Object parser
300 32     32 1 50 local $_;
301 32         171 require Data::Sofu::Object;
302 32         53 my $self=shift;
303 32         138 $self->open(shift);
304 32         285 my ($bom, $ver) = $self->unpackHeader();
305 32 50       347 return $self->unpackObject($bom) if ($self->{SUPPORTED}->{$ver});
306 0           my $module=$versions{$ver};
307 0 0         $self->die("Unknown Version: $ver") unless $module;
308 0           eval "require $module";
309 0 0         confess $@ if $@;
310 0           my $m = "$module"->new();
311 0           return $m->unpackObject($bom);
312             }
313              
314             =head2 write(FILE,TREE,[COMMENTS,[ENCODING,[BYTEORDER,[SOFUMARK,[...]]]]])
315              
316             Writes TREE to FILE.
317              
318             See open() for FILE.
319              
320             See pack() for COMMENTS,ENCODING,BYTEORDER,SOFUMARK,...
321              
322             TREE can be a perl datastructure or a Data::Sofu::Object or derived.
323              
324             =cut
325              
326             sub write {
327 0     0 1   local $_;
328 0           my $self=shift;
329 0           $self->openout(shift);
330 0           my $fh=$self->{OUT};
331 0           print $fh $self->pack(@_);
332              
333             }
334              
335             =head2 pack(TREE,[COMMENTS,[ENCODING,[BYTEORDER,[SOFUMARK,[...]]]]])
336              
337             This method is implemented only in the driver, but it is important to discuss the arguments here.
338              
339             Note: These arguments are the ones used in drivers up to the default C. Later drivers might add more arguments (therefore ...), and earlier drivers might support fewer.
340              
341             print FH, $bsofu->pack(readSofu("something.sofu"),getSofucomments(),"UTF-32","LE","0.4");
342              
343             =over
344              
345             =item TREE
346              
347             First driver to support: C
348              
349             Perl datastructure to pack. Can be a hash, array or scalar (or array of hashes of hashes of arrays or whatever). Anything NOT a hash will be converted to TREE={Value=>TREE};
350              
351             It can also be a Data::Sofu::Object or derived (Data::Sofu::Map, Data::Sofu::List, Data::Sofu::Value, Data::Sofu::...).
352             Anything not a Data::Sofu::Map will be converted to one (A Map with one attribute called "Value" that holds TREE).
353              
354             =item COMMENTS
355              
356             First driver to support: C
357              
358             Comment hash (as returned by Data::Sofu::getSofucomments() or Data::Sofu->new()->comments() after any file was read).
359              
360             Can be undef or {}.
361              
362             =item ENCODING
363              
364             First driver to support: C
365              
366             Specifies the encoding of the strings in the binary sofu file, which can be:
367              
368             =over
369              
370             =item C<"0"> or C<"UTF-8">
371              
372             First driver to support: C
373              
374             This is default.
375              
376             Normal UTF-8 encoding (supports almost all chars)
377              
378             =item C<"1"> or C<"UTF-7">
379              
380             First driver to support: C
381              
382             This is default for byteorder = 7Bit (See below)
383              
384             7Bit encoding (if your transport stream isn't 8-Bit safe
385              
386             =item C<"2"> or C<"UTF-16">
387              
388             First driver to support: C
389              
390             UTF 16 with byte order mark in EVERY string.
391              
392             Byteoder depends on your machine
393              
394             =item C<"3"> or C<"UTF-16BE">
395              
396             First driver to support: C
397              
398             No BOM, always BigEndian
399              
400             =item C<"4"> or C<"UTF-16LE">
401              
402             First driver to support: C
403              
404             No BOM, always LittleEndian
405              
406             =item C<"5"> or C<"UTF-32">
407              
408             First driver to support: C
409              
410             UTF-32 with byte order mark in EVERY string.
411              
412             Byteoder depends on your machine
413              
414             =item C<"6"> or C<"UTF-32BE">
415              
416             First driver to support: C
417              
418             No BOM, always BigEndian
419              
420             =item C<"7"> or C<"UTF-32LE">
421              
422             First driver to support: C
423              
424             No BOM, always LittleEndian
425              
426             =item C<"8","9">
427              
428             Reserved for future use
429              
430             =item C<"10"> or C<"ascii">
431              
432             First driver to support: C
433              
434             Normal ASCII encoding
435              
436             Might not support all characters and will warn about that.
437              
438             =item C<"11"> or C<"cp1252">
439              
440             First driver to support: C
441              
442             Windows Codepage 1252
443              
444             Might not support all characters and will warn about that.
445              
446             =item C<"12"> or C<"latin1">
447              
448             First driver to support: C
449              
450             ISO Latin 1
451              
452             Might not support all characters and will warn about that.
453              
454             =item C<"13"> or C<"latin9">
455              
456             First driver to support: C
457              
458             ISO Latin 9
459              
460             Might not support all characters and will warn about that.
461              
462             =item C<"14"> or C<"latin10">
463              
464             First driver to support: C
465              
466             ISO Latin 10
467              
468             Might not support all characters and will warn about that.
469              
470             =back
471              
472             =item BYTEORDER
473              
474             First driver to support: C
475              
476             Defines how the integers of the binary file are encoded.
477              
478             =over
479              
480             =item C
481              
482             First driver to support: C
483              
484             Maschine order
485              
486             This is Default.
487              
488             BOM is placed to detect the order used.
489              
490             =item C<"LE">
491              
492             First driver to support: C
493              
494             Little Endian
495              
496             BOM is placed to detect the order used.
497              
498             Use this to give it to machines which are using Little Endian and have to read the file alot
499              
500             =item C<"BE">
501              
502             First driver to support: C
503              
504             Big Endian
505              
506             BOM is placed to detect the order used.
507              
508             Use this to give it to machines which are using Big Endian and have to read the file alot
509              
510             =item C<"7Bit">
511              
512             First driver to support: C
513              
514             Use this byteorder if you can't trust your transport stream to be 8-Bit save.
515              
516             Encoding is forced to be UTF-7. No byte in the file will be > 127.
517              
518             BOM is set to 00 00.
519              
520             =item C<"NOFORCE7Bit">
521              
522             First driver to support: C
523              
524             Use this byteorder if you can't trust your transport stream to be 8-Bit save but you want another enconding than UTF-7
525              
526             Encoding is NOT forced to be UTF-7.
527              
528             BOM is set to 00 00.
529              
530             =back
531              
532             =item SOFUMARK
533              
534             First driver to support: C
535              
536             Defines how often the string "Sofu" is placed in the file (to tell any user with a text-editor what type of file this one is).
537              
538             =over
539              
540             =item C
541              
542             First driver to support: C
543              
544             Only place one "Sofu" at the beginning of the file.
545              
546             This is default.
547              
548             =item C<"0" or "">
549              
550             First driver to support: C
551              
552             Place no string anywhere.
553              
554             =item C<< "1" or >1 >>
555              
556             First driver to support: C
557              
558             Place a string on every place it is possible
559              
560             Warning, the file might get big.
561              
562             =item C<"0.000001" - "0.99999">
563              
564             First driver to support: C
565              
566             Place strings randomly.
567              
568             =back
569              
570             =back
571              
572             B
573              
574             Encoding, Byteorder and encoding driver (and Sofumark of course) are saved in the binary file. So you don't need to specify them for reading files, in fact just give them the Data::Sofu's readSofu() and all will be fine.
575              
576             =head1 BUGS
577              
578             C<< Data::Sofu::Object->writeBinary() >> will only use the Bin0200 driver, no other.
579              
580             $map = new Data::Sofu::Map;
581             .....
582             $map->writeBinary($file); #Bin0200 driver always.
583             use Data::Sofu;
584             writeSofuBinary($file,$map); #Will use the latest driver.
585              
586             =head1 SEE ALSO
587              
588             perl(1),L
589              
590             L, L, L
591              
592              
593             =cut
594             1;