File Coverage

blib/lib/FFI/Platypus/Record.pm
Criterion Covered Total %
statement 115 117 98.2
branch 35 44 79.5
condition 11 18 61.1
subroutine 14 15 93.3
pod 2 2 100.0
total 177 196 90.3


line stmt bran cond sub pod time code
1             package FFI::Platypus::Record;
2              
3 9     9   211524 use strict;
  9         22  
  9         238  
4 9     9   39 use warnings;
  9         15  
  9         197  
5 9     9   161 use 5.008004;
  9         27  
6 9     9   45 use Carp qw( croak );
  9         15  
  9         407  
7 9     9   625 use FFI::Platypus;
  9         15  
  9         341  
8 9     9   46 use Exporter qw( import );
  9         96  
  9         292  
9 9     9   44 use constant 1.32 ();
  9         128  
  9         5337  
10              
11             our @EXPORT = qw( record_layout record_layout_1 );
12              
13             # ABSTRACT: FFI support for structured records data
14             our $VERSION = '2.07'; # VERSION
15              
16              
17             sub record_layout_1
18             {
19 5 100 66 5 1 22145 if(@_ % 2 == 0)
    100 33        
    50          
20             {
21 3         15 my $ffi = FFI::Platypus->new( api => 2);
22 3         10 unshift @_, $ffi;
23 3         13 goto &record_layout;
24             }
25             elsif(defined $_[0] && ref($_[0]) eq 'ARRAY')
26             {
27 1         3 my @args = @{ shift @_ };
  1         3  
28 1         3 unshift @args, api => 2;
29 1         2 unshift @_, \@args;
30 1         3 goto &record_layout;
31             }
32 1         8 elsif(defined $_[0] && eval { $_[0]->isa('FFI::Platypus') })
33             {
34 1         3 goto &record_layout;
35             }
36             else
37             {
38 0         0 croak "odd number of arguments, but first argument is not either an array reference or Platypus instance";
39             }
40             }
41              
42              
43             sub record_layout
44             {
45 24     24 1 93512 my $ffi;
46              
47 24 50       83 if(defined $_[0])
48             {
49 24 100       87 if(ref($_[0]) eq 'ARRAY')
    100          
50             {
51 2         5 my @args = @{ shift() };
  2         5  
52 2         9 $ffi = FFI::Platypus->new(@args);
53             }
54 22         198 elsif(eval { $_[0]->isa('FFI::Platypus') })
55             {
56 7         15 $ffi = shift;
57             }
58             }
59              
60 24   66     140 $ffi ||= FFI::Platypus->new;
61              
62 24         43 my $offset = 0;
63 24         66 my $record_align = 0;
64              
65 24 50       86 croak "uneven number of arguments!" if scalar(@_) % 2;
66              
67 24         94 my($caller, $filename, $line) = caller;
68              
69 24 50 33     544 if($caller->can("_ffi_record_size")
70             || $caller->can("ffi_record_size"))
71             {
72 0         0 croak "record already defined for the class $caller";
73             }
74              
75 24         73 my @destroy;
76             my @ffi_types;
77 24         0 my $has_string;
78              
79 24         62 while(@_)
80             {
81 92         146 my $spec = shift;
82 92         115 my $name = shift;
83 92         352 my $type = $ffi->{tp}->parse( $spec, { member => 1 } );
84              
85 92 50 66     444 croak "illegal name $name"
86             unless $name =~ /^[A-Za-z_][A-Za-z_0-9]*$/
87             || $name eq ':';
88 92 100       661 croak "accessor/method $name already exists"
89             if $caller->can($name);
90              
91 91         322 my $size = $type->sizeof;
92 91         230 my $align = $type->alignof;
93 91 100       280 $record_align = $align if $align > $record_align;
94 91         617 my $meta = $type->meta;
95              
96 91         284 $offset++ while $offset % $align;
97              
98             {
99 91         122 my $count;
  91         137  
100             my $ffi_type;
101              
102 91 100       184 if($meta->{type} eq 'record') # this means fixed string atm
103             {
104 9         19 $ffi_type = 'sint8';
105 9         17 $count = $size;
106             }
107             else
108             {
109 82         120 $ffi_type = $meta->{ffi_type};
110 82         118 $count = $meta->{element_count};
111 82 100       159 $count = 1 unless defined $count;
112              
113 82 100       202 $has_string = 1 if $meta->{type} eq 'string';
114             }
115 91         404 push @ffi_types, $ffi_type for 1..$count;
116             }
117              
118 91 100       205 if($name ne ':')
119             {
120              
121 66 100 100     186 if($meta->{type} eq 'string'
122             && $meta->{access} eq 'rw')
123             {
124 8         350 push @destroy, eval '# line '. __LINE__ . ' "' . __FILE__ . qq("\n) .qq{
125             sub {
126             shift->$name(undef);
127             };
128             };
129 8 50       25 die $@ if $@;
130             }
131              
132 66         151 my $full_name = join '::', $caller, $name;
133 66         591 my $error_str = _accessor
134             $full_name,
135             "$filename:$line",
136             $type,
137             $offset;
138 66 50       187 croak("$error_str ($spec $name)") if $error_str;
139             };
140              
141 91         300 $offset += $size;
142             }
143              
144 23         45 my $size = $offset;
145              
146 9     9   60 no strict 'refs';
  9         16  
  9         2943  
147 23         1214 constant->import("${caller}::_ffi_record_size", $size);
148 23         567 constant->import("${caller}::_ffi_record_align", $record_align);
149 23         71 *{join '::', $caller, '_ffi_record_ro'} = \&_ffi_record_ro;
  23         120  
150 23         93 *{join '::', $caller, 'new'} = sub {
151 21     21   13161 my $class = shift;
152 21 100       66 my $args = ref($_[0]) ? [%{$_[0]}] : \@_;
  1         4  
153 21 50       70 croak "uneven number of arguments to record constructor"
154             if @$args % 2;
155 21         73 my $record = "\0" x $class->_ffi_record_size;
156 21         43 my $self = bless \$record, $class;
157              
158 21         57 while(@$args)
159             {
160 23         43 my $key = shift @$args;
161 23         29 my $value = shift @$args;
162 23         117 $self->$key($value);
163             }
164              
165 21         108 $self;
166 23         116 };
167              
168             {
169 23         42 require FFI::Platypus::Record::Meta;
  23         3640  
170 23         148 my $ffi_meta = FFI::Platypus::Record::Meta->new(
171             \@ffi_types,
172             !$has_string,
173             );
174 23     7   96 *{join '::', $caller, '_ffi_meta'} = sub { $ffi_meta };
  23         131  
  7         336  
175             }
176              
177 23     0   78 my $destroy_sub = sub {};
178              
179 23 100       73 if(@destroy)
180             {
181             $destroy_sub = sub {
182 5 50   5   18143 return if _ffi_record_ro($_[0]);
183 5         20 $_->($_[0]) for @destroy;
184 4         17 };
185             }
186 23         40 do {
187 9     9   55 no strict 'refs';
  9         15  
  9         915  
188 23         32 *{"${caller}::DESTROY"} = $destroy_sub;
  23         83  
189             };
190 23         132 ();
191             }
192              
193             1;
194              
195             __END__