File Coverage

blib/lib/FFI/C/ArrayDef.pm
Criterion Covered Total %
statement 85 88 96.5
branch 26 36 72.2
condition 13 24 54.1
subroutine 17 17 100.0
pod 2 2 100.0
total 143 167 85.6


line stmt bran cond sub pod time code
1             package FFI::C::ArrayDef;
2              
3 4     4   1403 use strict;
  4         9  
  4         120  
4 4     4   20 use warnings;
  4         29  
  4         92  
5 4     4   63 use 5.008001;
  4         41  
6 4     4   529 use Ref::Util qw( is_blessed_ref is_ref is_plain_arrayref );
  4         1553  
  4         237  
7 4     4   1684 use FFI::C::Array;
  4         11  
  4         101  
8 4     4   494 use Sub::Install ();
  4         1676  
  4         61  
9 4     4   437 use Sub::Util ();
  4         295  
  4         81  
10 4     4   19 use Scalar::Util qw( refaddr );
  4         10  
  4         197  
11 4     4   35 use base qw( FFI::C::Def );
  4         20  
  4         2268  
12              
13             our @CARP_NOT = qw( FFI::C );
14              
15             # ABSTRACT: Array data definition for FFI
16             our $VERSION = '0.14'; # VERSION
17              
18              
19             sub new
20             {
21 11     11 1 1164 my $self = shift->SUPER::new(@_);
22              
23 11         24 my %args = %{ delete $self->{args} };
  11         50  
24              
25 11         29 my $member;
26 11         23 my $count = 0;
27              
28 11 50       18 my @members = @{ delete $args{members} || [] };
  11         45  
29 11 100       48 if(@members == 1)
    50          
30             {
31 4         12 ($member) = @members;
32             }
33             elsif(@members == 2)
34             {
35 7         18 ($member, $count) = @members;
36             }
37             else
38             {
39 0         0 Carp::croak("The members argument should be a struct/union type and an optional element count");
40             }
41              
42 11 100       42 if(my $def = $self->ffi->def('FFI::C::Def', $member))
43             {
44 4         40 $member = $def;
45             }
46              
47 11 100       250 Carp::croak("Canot nest an array def inside of itself") if refaddr($member) == refaddr($self);
48              
49 10 50 33     98 Carp::croak("Illegal member")
      33        
50             unless defined $member && is_blessed_ref($member) && $member->isa("FFI::C::Def");
51              
52 10 50 33     80 Carp::croak("The element count must be a positive integer")
53             if defined $count && $count !~ /^[1-9]*[0-9]$/;
54              
55 10         36 $self->{size} = $member->size * $count;
56 10         33 $self->{align} = $member->align;
57 10         29 $self->{members}->{member} = $member;
58 10         25 $self->{members}->{count} = $count;
59              
60 10         109 Carp::carp("Unknown argument: $_") for sort keys %args;
61              
62 10 100       36 if($self->class)
63             {
64             # not handled by the superclass:
65             # 3. Any nested cdefs must have Perl classes.
66              
67             {
68 5         12 my $member = $self->{members}->{member};
  5         10  
69 5         15 my $accessor = $self->class . '::get';
70             Carp::croak("Missing Perl class for $accessor")
71 5 50 33     20 if $member->{nest} && !$member->{nest}->{class};
72             }
73              
74 5         26 $self->_generate_class(qw( get ));
75              
76             {
77 5         20 my $member_class = $self->{members}->{member}->class;
78 5         47 my $member_size = $self->{members}->{member}->size;
79             my $code = sub {
80 61     28   9948 my($self, $index) = @_;
        28      
        61      
        33      
        27      
81 61 100       445 Carp::croak("Negative array index") if $index < 0;
82 57 100 100     797 Carp::croak("OOB array index") if $self->{count} && $index >= $self->{count};
83 53         98 my $ptr = $self->{ptr} + $member_size * $index;
84 53         140 $member_class->new($ptr,$self);
85 5         66 };
86 5         17 Sub::Util::set_subname(join('::', $self->class), $code);
87 5         37 Sub::Install::install_sub({
88             code => $code,
89             into => $self->class,
90             as => 'get',
91             });
92             }
93              
94             {
95 4     4   51 no strict 'refs';
  4         9  
  4         1478  
  5         207  
  5         218  
96 5         43 push @{ join '::', $self->class, 'ISA' }, 'FFI::C::Array';
  5         18  
97             }
98              
99             }
100              
101 10         58 $self;
102             }
103              
104              
105             sub create
106             {
107 7     7 1 338 my $self = shift;
108              
109 7 50       21 return $self->class->new(@_) if $self->class;
110              
111 7         21 local $self->{size} = $self->{size};
112 7         17 my $count = $self->{members}->{count};
113 7 100       54 if(@_ == 1)
114             {
115 3 50       28 if(! is_ref $_[0])
    50          
116             {
117 0         0 $count = shift;
118             }
119             elsif(is_plain_arrayref $_[0])
120             {
121 3         8 $count = scalar @{$_[0]};
  3         10  
122             }
123 3 50       13 if($count)
124             {
125 3         14 $self->{size} = $self->{members}->{member}->size * $count;
126             }
127             }
128              
129 7 50 66     52 if( (@_ == 2 && ! is_ref $_[0]) || ($self->size) )
      66        
130             {
131 7         30 my $array = $self->SUPER::create(@_);
132 7         99 $array->{count} = $count;
133 7 100 66     55 FFI::C::Util::perl_to_c($array, $_[0]) if @_ == 1 && is_plain_arrayref $_[0];
134 7         32 return $array;
135             }
136              
137 0           Carp::croak("Cannot create array without knowing the number of elements");
138             }
139              
140             1;
141              
142             __END__