File Coverage

blib/lib/Convert/Binary/C.pm
Criterion Covered Total %
statement 34 34 100.0
branch 9 10 90.0
condition 3 3 100.0
subroutine 6 6 100.0
pod n/a
total 52 53 98.1


line stmt bran cond sub pod time code
1             ################################################################################
2             #
3             # MODULE: Convert::Binary::C
4             #
5             ################################################################################
6             #
7             # DESCRIPTION: Convert::Binary::C Perl extension module
8             #
9             ################################################################################
10             #
11             # $Project: /Convert-Binary-C $
12             # $Author: mhx $
13             # $Date: 2006/11/02 12:57:01 +0100 $
14             # $Revision: 86 $
15             # $Source: /lib/Convert/Binary/C.pm $
16             #
17             ################################################################################
18             #
19             # Copyright (c) 2002-2006 Marcus Holland-Moritz. All rights reserved.
20             # This program is free software; you can redistribute it and/or modify
21             # it under the same terms as Perl itself.
22             #
23             ################################################################################
24              
25             package Convert::Binary::C;
26              
27 61     61   954 use strict;
  61         1673  
  61         1836  
28 61     61   1064 use DynaLoader;
  61         2793  
  61         1716  
29 61     61   1353 use Carp;
  61         546  
  61         1109  
30 61     61   919 use vars qw( @ISA $VERSION $XS_VERSION $AUTOLOAD );
  61         614  
  61         3554  
31              
32             @ISA = qw(DynaLoader);
33              
34             $VERSION    = do { my @r = '$Snapshot: /Convert-Binary-C/0.67 $' =~ /(\d+\.\d+(?:_\d+)?)/; @r ? $r[0] : '9.99' };
35             $XS_VERSION = $VERSION;
36             $VERSION    = eval $VERSION;
37              
38             bootstrap Convert::Binary::C $XS_VERSION;
39              
40             # Unfortunately, XS AUTOLOAD isn't supported
41             # by stable perl distributions before 5.8.0.
42              
43             sub AUTOLOAD
44             {
45 6200     6200   11589590   my $self = shift;
46 6200         74657   my $opt = $AUTOLOAD;
47 6200 50       303279   ref $self or croak "$self is not an object";
48 6200         159911   $opt =~ s/.*://;
49 6200 100       178656   $opt =~ /^[A-Z]/ or croak "Invalid method $opt called";
50 6199 100       135261   @_ <= 1 or croak "$opt cannot take more than one argument";
51 6198 100 100     80659   unless (@_ or defined wantarray) {
52 21         279     carp "Useless use of $opt in void context";
53 21         580     return;
54               }
55 6177         66729   my @warn;
56               {
57 6177     4   62913     local $SIG{__WARN__} = sub { push @warn, $_[0] };
  6177         213388  
  4         51  
58 6177         129738     $opt = eval { $self->configure( $opt, @_ ) };
  6177         157486  
59               }
60 6177         242186   for my $w (@warn) {
61 4         83     $w =~ s/\s+at.*?C\.pm.*//s;
62 4         54     carp $w;
63               }
64 6177 100       75689   if ($@) {
65 198         3458     $@ =~ s/\s+at.*?C\.pm.*//s;
66 198         2448     croak $@;
67               }
68 5979         87570   $opt;
69             }
70              
71             1;
72              
73             __END__
74            
75             =head1 NAME
76            
77             Convert::Binary::C - Binary Data Conversion using C Types
78            
79             =head1 SYNOPSIS
80            
81             =head2 Simple
82            
83             use Convert::Binary::C;
84            
85             #---------------------------------------------
86             # Create a new object and parse embedded code
87             #---------------------------------------------
88             my $c = Convert::Binary::C->new->parse(<<ENDC);
89            
90             enum Month { JAN, FEB, MAR, APR, MAY, JUN,
91             JUL, AUG, SEP, OCT, NOV, DEC };
92            
93             struct Date {
94             int year;
95             enum Month month;
96             int day;
97             };
98            
99             ENDC
100            
101             #-----------------------------------------------
102             # Pack Perl data structure into a binary string
103             #-----------------------------------------------
104             my $date = { year => 2002, month => 'DEC', day => 24 };
105            
106             my $packed = $c->pack('Date', $date);
107            
108             =head2 Advanced
109            
110             use Convert::Binary::C;
111             use Data::Dumper;
112            
113             #---------------------
114             # Create a new object
115             #---------------------
116             my $c = new Convert::Binary::C ByteOrder => 'BigEndian';
117            
118             #---------------------------------------------------
119             # Add include paths and global preprocessor defines
120             #---------------------------------------------------
121             $c->Include('/usr/lib/gcc-lib/i686-pc-linux-gnu/3.3.6/include',
122             '/usr/include')
123             ->Define(qw( __USE_POSIX __USE_ISOC99=1 ));
124            
125             #----------------------------------
126             # Parse the 'time.h' header file
127             #----------------------------------
128             $c->parse_file('time.h');
129            
130             #---------------------------------------
131             # See which files the object depends on
132             #---------------------------------------
133             print Dumper([$c->dependencies]);
134            
135             #-----------------------------------------------------------
136             # See if struct timespec is defined and dump its definition
137             #-----------------------------------------------------------
138             if ($c->def('struct timespec')) {
139             print Dumper($c->struct('timespec'));
140             }
141            
142             #-------------------------------
143             # Create some binary dummy data
144             #-------------------------------
145             my $data = "binary_test_string";
146            
147             #--------------------------------------------------------
148             # Unpack $data according to 'struct timespec' definition
149             #--------------------------------------------------------
150             if (length($data) >= $c->sizeof('timespec')) {
151             my $perl = $c->unpack('timespec', $data);
152             print Dumper($perl);
153             }
154            
155             #--------------------------------------------------------
156             # See which member lies at offset 5 of 'struct timespec'
157             #--------------------------------------------------------
158             my $member = $c->member('timespec', 5);
159             print "member('timespec', 5) = '$member'\n";
160            
161             =head1 DESCRIPTION
162            
163             Convert::Binary::C is a preprocessor and parser for C type
164             definitions. It is highly configurable and supports
165             arbitrarily complex data structures. Its object-oriented
166             interface has L<C<pack>|/"pack"> and L<C<unpack>|/"unpack"> methods
167             that act as replacements for
168             Perl's L<C<pack>|perlfunc/"pack"> and L<C<unpack>|perlfunc/"unpack"> and
169             allow to use C types instead of a string representation
170             of the data structure for conversion of binary data from and
171             to Perl's complex data structures.
172            
173             Actually, what Convert::Binary::C does is not very different
174             from what a C compiler does, just that it doesn't compile the
175             source code into an object file or executable, but only parses
176             the code and allows Perl to use the enumerations, structs, unions
177             and typedefs that have been defined within your C source for binary
178             data conversion, similar to
179             Perl's L<C<pack>|perlfunc/"pack"> and L<C<unpack>|perlfunc/"unpack">.
180            
181             Beyond that, the module offers a lot of convenience methods
182             to retrieve information about the C types that have been parsed.
183            
184             =head2 Background and History
185            
186             In late 2000 I wrote a real-time debugging interface for an
187             embedded medical device that allowed me to send out data from
188             that device over its integrated Ethernet adapter.
189             The interface was C<printf()>-like, so you could easily send
190             out strings or numbers. But you could also send out what I
191             called I<arbitrary data>, which was intended for arbitrary
192             blocks of the device's memory.
193            
194             Another part of this real-time debugger was a Perl application
195             running on my workstation that gathered all the messages that
196             were sent out from the embedded device. It printed all the
197             strings and numbers, and hex-dumped the arbitrary data.
198             However, manually parsing a couple of 300 byte hex-dumps of a
199             complex C structure is not only frustrating, but also error-prone
200             and time consuming.
201            
202             Using L<C<unpack>|perlfunc/"unpack"> to retrieve the contents
203             of a C structure works fine for small structures and if you
204             don't have to deal with struct member alignment. But otherwise,
205             maintaining such code can be as awful as deciphering hex-dumps.
206            
207             As I didn't find anything to solve my problem on the CPAN,
208             I wrote a little module that translated simple C structs
209             into L<C<unpack>|perlfunc/"unpack"> strings. It worked, but
210             it was slow. And since it couldn't deal with struct member
211             alignment, I soon found myself adding padding bytes everywhere.
212             So again, I had to maintain two sources, and changing one of
213             them forced me to touch the other one.
214            
215             All in all, this little module seemed to make my task a bit
216             easier, but it was far from being what I was thinking of:
217            
218             =over 2
219            
220             =item *
221            
222             A module that could directly use the source I've been coding
223             for the embedded device without any modifications.
224            
225             =item *
226            
227             A module that could be configured to match the properties
228             of the different compilers and target platforms I was using.
229            
230             =item *
231            
232             A module that was fast enough to decode a great amount of
233             binary data even on my slow workstation.
234            
235             =back
236            
237             I didn't know how to accomplish these tasks until I read something
238             about XS. At least, it seemed as if it could solve my performance
239             problems. However, writing a C parser in C isn't easier than it is
240             in Perl. But writing a C preprocessor from scratch is even worse.
241            
242             Fortunately enough, after a few weeks of searching I found both,
243             a lean, open-source C preprocessor library, and a reusable YACC
244             grammar for ANSI-C. That was the beginning of the development of
245             Convert::Binary::C in late 2001.
246            
247             Now, I'm successfully using the module in my embedded environment
248             since long before it appeared on CPAN. From my point of view, it
249             is exactly what I had in mind. It's fast, flexible, easy to use
250             and portable. It doesn't require external programs or other Perl
251             modules.
252            
253             =head2 About this document
254            
255             This document describes how to use Convert::Binary::C. A lot of
256             different features are presented, and the example code sometimes
257             uses Perl's more advanced language elements. If your experience
258             with Perl is rather limited, you should know how to use Perl's
259             very good documentation system.
260            
261             To look up one of the manpages, use the L<C<perldoc>|perldoc> command.
262             For example,
263            
264             perldoc perl
265            
266             will show you Perl's main manpage. To look up a specific Perl
267             function, use C<perldoc -f>:
268            
269             perldoc -f map
270            
271             gives you more information about the L<C<map>|perlfunc/"map"> function.
272             You can also search the FAQ using C<perldoc -q>:
273            
274             perldoc -q array
275            
276             will give you everything you ever wanted to know about Perl
277             arrays. But now, let's go on with some real stuff!
278            
279             =head2 Why use Convert::Binary::C?
280            
281             Say you want to pack (or unpack) data according to the following
282             C structure:
283            
284             struct foo {
285             char ary[3];
286             unsigned short baz;
287             int bar;
288             };
289            
290             You could of course use
291             Perl's L<C<pack>|perlfunc/"pack"> and L<C<unpack>|perlfunc/"unpack"> functions:
292            
293             @ary = (1, 2, 3);
294             $baz = 40000;
295             $bar = -4711;
296             $binary = pack 'c3 S i', @ary, $baz, $bar;
297            
298             But this implies that the struct members are byte aligned. If
299             they were long aligned (which is the default for most compilers),
300             you'd have to write
301            
302             $binary = pack 'c3 x S x2 i', @ary, $baz, $bar;
303            
304             which doesn't really increase readability.
305            
306             Now imagine that you need to pack the data for a completely
307             different architecture with different byte order. You would
308             look into the L<C<pack>|perlfunc/"pack"> manpage again and
309             perhaps come up with this:
310            
311             $binary = pack 'c3 x n x2 N', @ary, $baz, $bar;
312            
313             However, if you try to unpack C<$foo> again, your signed values
314             have turned into unsigned ones.
315            
316             All this can still be managed with Perl. But imagine your
317             structures get more complex? Imagine you need to support
318             different platforms? Imagine you need to make changes to
319             the structures? You'll not only have to change the C source
320             but also dozens of L<C<pack>|perlfunc/"pack"> strings in
321             your Perl code. This is no fun. And Perl should be fun.
322            
323             Now, wouldn't it be great if you could just read in the C
324             source you've already written and use all the types defined
325             there for packing and unpacking? That's what Convert::Binary::C
326             does.
327            
328             =head2 Creating a Convert::Binary::C object
329            
330             To use Convert::Binary::C just say
331            
332             use Convert::Binary::C;
333            
334             to load the module. Its interface is completely object
335             oriented, so it doesn't export any functions.
336            
337             Next, you need to create a new Convert::Binary::C object. This
338             can be done by either
339            
340             $c = Convert::Binary::C->new;
341            
342             or
343            
344             $c = new Convert::Binary::C;
345            
346             You can optionally pass configuration options to
347             the L<constructor|/"new"> as described in the next section.
348            
349             =head2 Configuring the object
350            
351             To configure a Convert::Binary::C object, you can either call
352             the L<C<configure>|/"configure"> method or directly pass the configuration
353             options to the L<constructor|/"new">. If you want to change byte order
354             and alignment, you can use
355            
356             $c->configure(ByteOrder => 'LittleEndian',
357             Alignment => 2);
358            
359             or you can change the construction code to
360            
361             $c = new Convert::Binary::C ByteOrder => 'LittleEndian',
362             Alignment => 2;
363            
364             Either way, the object will now know that it should use
365             little endian (Intel) byte order and 2-byte struct member
366             alignment for packing and unpacking.
367            
368             Alternatively, you can use the option names as names of
369             methods to configure the object, like:
370            
371             $c->ByteOrder('LittleEndian');
372            
373             You can also retrieve information about the current
374             configuration of a Convert::Binary::C object. For details,
375             see the section about the L<C<configure>|/"configure"> method.
376            
377             =head2 Parsing C code
378            
379             Convert::Binary::C allows two ways of parsing C source. Either
380             by parsing external C header or C source files:
381            
382             $c->parse_file('header.h');
383            
384             Or by parsing C code embedded in your script:
385            
386             $c->parse(<<'CCODE');
387             struct foo {
388             char ary[3];
389             unsigned short baz;
390             int bar;
391             };
392             CCODE
393            
394             Now the object C<$c> will know everything about C<struct foo>.
395             The example above uses a so-called here-document. It allows to
396             easily embed multi-line strings in your code. You can find more
397             about here-documents in L<perldata> or L<perlop>.
398            
399             Since the L<C<parse>|/"parse"> and L<C<parse_file>|/"parse_file"> methods
400             throw an exception when a parse error occurs, you usually want to catch
401             these in an C<eval> block:
402            
403             eval { $c->parse_file('header.h') };
404             if ($@) {
405             # handle error appropriately
406             }
407            
408             Perl's special C<$@> variable will contain an empty string (which
409             evaluates to a false value in boolean context) on success or
410             an error string on failure.
411            
412             As another feature, L<C<parse>|/"parse"> and L<C<parse_file>|/"parse_file"> return
413             a reference to their object on success, just like L<C<configure>|/"configure"> does
414             when you're configuring the object. This will allow you to write constructs
415             like this:
416            
417             my $c = eval {
418             Convert::Binary::C->new(Include => ['/usr/include'])
419             ->parse_file('header.h')
420             };
421             if ($@) {
422             # handle error appropriately
423             }
424            
425             =head2 Packing and unpacking
426            
427             Convert::Binary::C has two methods, L<C<pack>|/"pack"> and L<C<unpack>|/"unpack">,
428             that act similar to the functions of same denominator in Perl.
429             To perform the packing described in the example above,
430             you could write:
431            
432             $data = {
433             ary => [1, 2, 3],
434             baz => 40000,
435             bar => -4711,
436             };
437             $binary = $c->pack('foo', $data);
438            
439             Unpacking will work exactly the same way, just that
440             the L<C<unpack>|/"unpack"> method will take a byte string as its input
441             and will return a reference to a (possibly very complex)
442             Perl data structure.
443            
444             $binary = get_data_from_memory();
445             $data = $c->unpack('foo', $binary);
446            
447             You can now easily access all of the values:
448            
449             print "foo.ary[1] = $data->{ary}[1]\n";
450