File Coverage

blib/lib/Inline/Struct.pm
Criterion Covered Total %
statement 113 131 86.2
branch 30 46 65.2
condition n/a
subroutine 7 8 87.5
pod 0 4 0.0
total 150 189 79.3


line stmt bran cond sub pod time code
1             package Inline::Struct;
2              
3 15     15   2630362 use strict;
  15         205  
  15         596  
4 15     15   116 use warnings;
  15         52  
  15         490  
5 15     15   100 use Carp;
  15         42  
  15         1406  
6             require Inline;
7             require Inline::Struct::grammar;
8 15     15   110 use Data::Dumper;
  15         46  
  15         30717  
9              
10             our $VERSION = '0.30';
11              
12             #=============================================================================
13             # Inline::Struct is NOT an ILSM: no register() function
14             #=============================================================================
15              
16             #=============================================================================
17             # parse -- gets all C/C++ struct definitions and binds them to Perl
18             #=============================================================================
19             sub parse {
20 15     15 0 2393393 require Parse::RecDescent;
21 15         93 my $o = shift;
22 15 50       105 return if $o->{STRUCT}{'.parser'};
23 15 50       109 return unless $o->{STRUCT}{'.any'};
24              
25             # Figure out whether to grab all structs
26 68         270 my $nstructs = scalar grep { $_ =~ /^[_a-z][_0-9a-z]*$/i }
27 15         42 keys %{$o->{STRUCT}};
  15         118  
28 15 100       94 $o->{STRUCT}{'.all'} = 1
29             if $nstructs == 0;
30              
31             # Load currently-defined types (stored in $o->{ILSM}{typeconv})
32 15         178 $o->get_maps;
33 15         2585 $o->get_types;
34              
35             # Parse structs
36 15         219466 $::RD_HINT++;
37 15 50       222 my $grammar = Inline::Struct::grammar::grammar()
38             or croak "Can't find Struct grammar!\n";
39 15         202 my $parser = $o->{STRUCT}{'.parser'} = Parse::RecDescent->new($grammar);
40 15         1738382 $parser->{data}{typeconv} = $o->{ILSM}{typeconv};
41 15         298 $parser->code($o->{ILSM}{code});
42 15         88953 $o->{ILSM}{typeconv} = $parser->{data}{typeconv};
43              
44 15         83 $o->{STRUCT}{'.xs'} = "";
45 15         54 $o->{STRUCT}{'.macros'} = <
46             #define NEW_INLINE_STRUCT(_IS_targ,_IS_type) INLINE_STRUCT_NEW_##_IS_type(_IS_targ)
47             #define INLINE_STRUCT_FIELDS(_IS_type) INLINE_STRUCT_FIELDS_##_IS_type
48             #define INLINE_STRUCT_INIT_LIST(_IS_targ,_IS_type) INLINE_STRUCT_INITL_##_IS_type(_IS_targ)
49             #define INLINE_STRUCT_ARRAY(_IS_src,_IS_targ,_IS_type) INLINE_STRUCT_ARRAY_##_IS_type(_IS_src,_IS_targ)
50             #define INLINE_STRUCT_VALUES(_IS_src,_IS_targ,_IS_type) INLINE_STRUCT_ARRAY_##_IS_type(_IS_src,_IS_targ)
51             #define INLINE_STRUCT_HASH(_IS_src,_IS_targ,_IS_type) INLINE_STRUCT_HASH_##_IS_type(_IS_src,_IS_targ)
52             #define INLINE_STRUCT_KEYS(_IS_src,_IS_targ,_IS_type) INLINE_STRUCT_KEYS_##_IS_type(_IS_src,_IS_targ)
53             END
54              
55 15         38 my @struct_list;
56 15 100       74 if ($o->{STRUCT}{'.all'}) {
57 10 50       55 die "No valid structs found" unless $parser->{data}{structs};
58 10         55 @struct_list = @{$parser->{data}{structs}};
  10         72  
59             }
60             else {
61 33         110 @struct_list = grep { $_ =~ /^[_a-z][_a-z0-9]*$/i }
62 5         12 keys %{$o->{STRUCT}}
  5         31  
63             }
64 15         66 for my $struct (@struct_list) {
65 21 50       102 unless (defined $parser->{data}{struct}{$struct}) {
66 0 0       0 warn "Struct $struct requested but not found" if $^W;
67 0         0 next;
68             }
69 21         85 $o->{STRUCT}{'.bound'}{$struct}++;
70 21         95 my $cname = $parser->{data}{struct}{$struct}{cname};
71 21         65 my ($NEW, $FIELDS, $INITL, $HASH, $ARRAY, $KEYS);
72              
73             # Set up the initial part of the macros
74 21         211 $NEW = <
75             #define INLINE_STRUCT_NEW_${struct}(_IS_targ) { \\
76             HV *hv = get_hv("Inline::Struct::${struct}::_map_", 1); \\
77             HV *entry = newHV(); \\
78             SV *entrv = (SV*)newRV((SV*)entry); \\
79             SV *lookup; \\
80             char *key; \\
81             STRLEN klen; \\
82             ENTER; \\
83             SAVETMPS; \\
84             Newz(1564,_IS_targ,1,$cname); \\
85             lookup = newSViv((IV)_IS_targ); \\
86             key = SvPV(lookup, klen); \\
87             sv_2mortal(lookup); \\
88             hv_store(entry, "REFCNT", 6, newSViv(0), 0); \\
89             hv_store(entry, "FREE", 4, newSViv(1), 0); \\
90             hv_store(hv, key, klen, entrv, 0); \\
91             FREETMPS; \\
92             LEAVE; \\
93             }
94             END
95             $FIELDS = "#define INLINE_STRUCT_FIELDS_$struct " .
96 21         85 (scalar @{$parser->{data}{struct}{$struct}{fields}}) . "\n";
  21         167  
97 21         75 $INITL = "#define INLINE_STRUCT_INITL_$struct(_IS_targ) {\\\n";
98 21         65 $HASH = <
99             #define INLINE_STRUCT_HASH_$struct(_IS_src,_IS_targ) \\
100             hv_clear(_IS_targ); \\
101             END
102 21         58 $ARRAY = <
103             #define INLINE_STRUCT_ARRAY_$struct(_IS_src,_IS_targ) \\
104             av_clear(_IS_targ); \\
105             END
106              
107 21         63 $KEYS = <
108             #define INLINE_STRUCT_KEYS_$struct(_IS_src,_IS_targ) \\
109             av_clear(_IS_targ); \\
110             END
111              
112 21         39 my $maxi = scalar @{$parser->{data}{struct}{$struct}{fields}};
  21         55  
113 21 50       104 next unless $maxi > 0;
114              
115 21         462 $o->{STRUCT}{'.xs'} .= <
116              
117             MODULE = $o->{API}{module} PACKAGE = Inline::Struct::$struct
118              
119             PROTOTYPES: DISABLE
120              
121             $cname *
122             new(klass, ...)
123             char *klass
124             PREINIT:
125             int _items = items - 1;
126             CODE:
127             NEW_INLINE_STRUCT(RETVAL,$struct);
128             if (_items == 0) { }
129             else {
130             INLINE_STRUCT_INIT_LIST(RETVAL,$struct);
131             }
132             OUTPUT:
133             RETVAL
134              
135             void
136             DESTROY(object)
137             $cname *object
138             PREINIT:
139             HV *map = get_hv("Inline::Struct::${struct}::_map_", 1);
140             SV *lookup;
141             STRLEN klen;
142             char *key;
143             CODE:
144             ENTER;
145             SAVETMPS;
146             lookup = newSViv((IV)object);
147             key = SvPV(lookup, klen);
148             sv_2mortal(lookup);
149             if (hv_exists(map, key, klen)) {
150             HV *info = (HV*)SvRV(*hv_fetch(map, key, klen, 0));
151             SV *refcnt = *hv_fetch(info, "REFCNT", 6, 0);
152             int tofree = SvIV(*hv_fetch(info, "FREE", 4, 0));
153             if (tofree && SvIV(refcnt) == 1) {
154             Safefree(object);
155             hv_delete(map, key, klen, 0);
156             }
157             else
158             sv_dec(refcnt);
159             }
160             FREETMPS;
161             LEAVE;
162              
163             HV *
164             _HASH(object)
165             $cname *object
166             CODE:
167             RETVAL = newHV();
168             INLINE_STRUCT_HASH(object, RETVAL, $struct);
169             OUTPUT:
170             RETVAL
171              
172             AV *
173             _VALUES(object)
174             $cname *object
175             CODE:
176             RETVAL = newAV();
177             INLINE_STRUCT_VALUES(object, RETVAL, $struct);
178             OUTPUT:
179             RETVAL
180              
181             AV *
182             _ARRAY(object)
183             $cname *object
184             CODE:
185             RETVAL = newAV();
186             INLINE_STRUCT_ARRAY(object, RETVAL, $struct);
187             OUTPUT:
188             RETVAL
189              
190             AV *
191             _KEYS(object)
192             $cname *object
193             CODE:
194             RETVAL = newAV();
195             INLINE_STRUCT_KEYS(object, RETVAL, $struct);
196             OUTPUT:
197             RETVAL
198              
199             END
200              
201 21         63 my $i=1;
202 21         50 for my $field (@{$parser->{data}{struct}{$struct}{fields}}) {
  21         89  
203 47         98 my $flen = length $field;
204 47         158 my $type = $parser->{data}{struct}{$struct}{field}{$field};
205 47 100       148 my $q = ($i == 1 ? 'if' : 'else if');
206 47         247 my $t =
207             typeconv($o, "_IS_targ->$field",
208             "val",
209             $type,
210             "input_expr",
211             1,
212             '_KEYS',
213             );
214 47         193 my $s =
215             typeconv($o, "_IS_src->$field",
216             "tmp",
217             $type,
218             "output_expr",
219             1,
220             '_KEYS',
221             );
222 47         224 $INITL .=
223             (typeconv($o, "_IS_targ->$field",
224             "ST($i)",
225             $type,
226             "input_expr",
227             1,
228             '_KEYS',
229             ) .
230             "; \\\n");
231 47 100       279 $HASH .= (qq{{\\\n\tSV*tmp=newSViv(0);\\\n$s \\
232             \thv_store(_IS_targ, "$field", $flen, tmp, 0); \\\n}} .
233             ($i == $maxi ? "" : "\\") .
234             "\n"
235             );
236 47 100       192 $ARRAY .= (qq{{\\\n\tSV*tmp=newSViv(0);\\\n$s \\
237             \tav_push(_IS_targ, tmp); \\\n}} .
238             ($i == $maxi ? "" : "\\") .
239             "\n"
240             );
241 47 100       167 $KEYS .= (qq{av_push(_IS_targ, newSVpv("$field", 0));} .
242             ($i == $maxi ? "" : "\\") .
243             "\n"
244             );
245 47         125 my $is_sv = $type =~ /^SV\s*\*$/;
246 47         172 $o->{STRUCT}{'.xs'} .= <
247             void
248             $field(object, ...)
249             $cname *object
250             PREINIT:
251             SV *retval = newSViv(0);
252             int mortalise_retval = 0;
253             PPCODE:
254             ENTER;
255             SAVETMPS;
256             if (items == 1) {
257 47         153 @{[typeconv($o, "object->$field", "retval", $type, "output_expr", undef, $field)]}
258             @{[
259             # mortalise if not an SV *
260 47 100       217 $is_sv ? '' : 'mortalise_retval = 1;'
261             ]}
262             }
263             else {
264             @{[
265 47 100       169 $is_sv ?
266             qq{if (object->$field && SvOK(object->$field)) {
267             SvREFCNT_dec(object->$field);
268             }} : ""
269             ]}
270 47         153 @{[typeconv($o, "object->$field", "ST(1)", $type, "input_expr", undef, $field)]};
271             @{[
272 47 100       200 $is_sv ?
273             qq{if (object->$field && SvOK(object->$field)) {
274             SvREFCNT_inc(object->$field);
275             }} : ""
276             ]}
277 47         158 @{[typeconv($o, "object", "retval", "$cname *", "output_expr", undef, $field)]};
278             mortalise_retval = 1;
279             }
280             FREETMPS;
281             LEAVE;
282             if (mortalise_retval) sv_2mortal(retval);
283             XPUSHs(retval);
284              
285             EOF
286 47         255 $i++;
287             }
288 21         58 $INITL .= "}\n";
289              
290 21         198 $o->{STRUCT}{'.macros'} .= <
291             $NEW
292             $FIELDS
293             $INITL
294             $HASH
295             $ARRAY
296             $KEYS
297             END
298              
299             }
300              
301             # Write a typemap file containing typemaps for each thingy
302 15         76 write_typemap($o);
303             }
304              
305             sub write_typemap {
306 15     15 0 40 my $o = shift;
307 15         50 my $data = $o->{STRUCT}{'.parser'}{data};
308              
309 15         62 my ($TYPEMAP, $INPUT, $OUTPUT);
310 15         36 for my $struct (@{$data->{structs}}) {
  15         58  
311 21         125 my $type = "O_OBJECT_$struct";
312 1131         2064 my @ctypes = grep { $data->{typeconv}{type_kind}{$_} eq $type }
313 21         47 keys %{$data->{typeconv}{type_kind}};
  21         284  
314 21         94 $TYPEMAP .= join "", map { "$_\t\t$type\n" } @ctypes;
  28         119  
315 21         126 $INPUT .= $type."\n".$data->{typeconv}{input_expr}{$type};
316 21         107 $OUTPUT .= $type."\n".$data->{typeconv}{output_expr}{$type};
317             }
318              
319             $o->mkpath($o->{API}{build_dir})
320 15 50       713 unless -d $o->{API}{build_dir};
321 15         54 my $fh;
322 15         73 my $fname = $o->{API}{build_dir}.'/Struct.map';
323 15 50       2370 open $fh, ">$fname"
324             or die $!;
325 15         441 print $fh <
326             TYPEMAP
327             $TYPEMAP
328             INPUT
329             $INPUT
330              
331             OUTPUT
332             $OUTPUT
333             END
334              
335 15         1130 close $fh;
336 15         194 $o->validate( TYPEMAPS => $fname );
337             }
338              
339             sub typeconv {
340 282     282 0 488 my $o = shift;
341 282         391 my $var = shift;
342 282         404 my $arg = shift;
343 282         401 my $type = shift;
344 282         402 my $dir = shift;
345 282         415 my $preproc = shift;
346 282         403 my $pname = shift;
347 282         685 my $tkind = $o->{ILSM}{typeconv}{type_kind}{$type};
348 282 50       603 die "Error: unknown type '$type'" if !$tkind;
349 282         768 my $compile = qq{qq{$o->{ILSM}{typeconv}{$dir}{$tkind}}};
350 282         16701 my $ret = eval $compile;
351 282 50       1123 die "Error while compiling: >>>$compile<<<\n$@" if $@;
352 282         522 chomp $ret;
353 282 100       630 $ret =~ s/\n/\\\n/g if $preproc;
354 282         1239 return $ret;
355             }
356              
357             #=============================================================================
358             # Return a little info about the structs we bound to.
359             #=============================================================================
360             sub info {
361 0     0 0   my $o = shift;
362 0           my $info = "";
363 0 0         parse($o) unless defined $o->{STRUCT}{'.parser'};
364 0           my $data = $o->{STRUCT}{'.parser'}{data};
365 0 0         if (defined $o->{STRUCT}{'.bound'}) {
366 0           $info .= "The following structs have been bound to Perl:\n";
367 0           for my $struct (keys %{$o->{STRUCT}{'.bound'}}) {
  0            
368 0           $info .= "\tstruct $struct {\n";
369 0           for my $field (@{$data->{struct}{$struct}{fields}}) {
  0            
370 0           my $type = $data->{struct}{$struct}{field}{$field};
371 0           $info .= "\t\t$type $field;\n";
372             }
373 0           $info .= "\t};\n";
374             }
375             }
376             else {
377 0           $info .= "No structs were bound to Perl.\n";
378             }
379 0           return $info;
380             }
381              
382             1;
383              
384             __END__