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   1118 use strict;
  4         10  
  4         96  
4 4     4   16 use warnings;
  4         17  
  4         75  
5 4     4   52 use 5.008001;
  4         28  
6 4     4   411 use Ref::Util qw( is_blessed_ref is_ref is_plain_arrayref );
  4         1397  
  4         198  
7 4     4   1408 use FFI::C::Array;
  4         9  
  4         88  
8 4     4   388 use Sub::Install ();
  4         1449  
  4         51  
9 4     4   374 use Sub::Util ();
  4         262  
  4         82  
10 4     4   18 use Scalar::Util qw( refaddr );
  4         7  
  4         156  
11 4     4   32 use base qw( FFI::C::Def );
  4         12  
  4         1841  
12              
13             our @CARP_NOT = qw( FFI::C );
14              
15             # ABSTRACT: Array data definition for FFI
16             our $VERSION = '0.12'; # VERSION
17              
18              
19             sub new
20             {
21 11     11 1 1017 my $self = shift->SUPER::new(@_);
22              
23 11         18 my %args = %{ delete $self->{args} };
  11         43  
24              
25 11         23 my $member;
26 11         18 my $count = 0;
27              
28 11 50       14 my @members = @{ delete $args{members} || [] };
  11         40  
29 11 100       36 if(@members == 1)
    50          
30             {
31 4         7 ($member) = @members;
32             }
33             elsif(@members == 2)
34             {
35 7         17 ($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       35 if(my $def = $self->ffi->def('FFI::C::Def', $member))
43             {
44 4         33 $member = $def;
45             }
46              
47 11 100       211 Carp::croak("Canot nest an array def inside of itself") if refaddr($member) == refaddr($self);
48              
49 10 50 33     77 Carp::croak("Illegal member")
      33        
50             unless defined $member && is_blessed_ref($member) && $member->isa("FFI::C::Def");
51              
52 10 50 33     65 Carp::croak("The element count must be a positive integer")
53             if defined $count && $count !~ /^[1-9]*[0-9]$/;
54              
55 10         33 $self->{size} = $member->size * $count;
56 10         28 $self->{align} = $member->align;
57 10         23 $self->{members}->{member} = $member;
58 10         18 $self->{members}->{count} = $count;
59              
60 10         31 Carp::carp("Unknown argument: $_") for sort keys %args;
61              
62 10 100       28 if($self->class)
63             {
64             # not handled by the superclass:
65             # 3. Any nested cdefs must have Perl classes.
66              
67             {
68 5         9 my $member = $self->{members}->{member};
  5         9  
69 5         11 my $accessor = $self->class . '::get';
70             Carp::croak("Missing Perl class for $accessor")
71 5 50 33     17 if $member->{nest} && !$member->{nest}->{class};
72             }
73              
74 5         25 $self->_generate_class(qw( get ));
75              
76             {
77 5         16 my $member_class = $self->{members}->{member}->class;
78 5         48 my $member_size = $self->{members}->{member}->size;
79             my $code = sub {
80 61     28   8190 my($self, $index) = @_;
        28      
        61      
        33      
        27      
81 61 100       356 Carp::croak("Negative array index") if $index < 0;
82 57 100 100     595 Carp::croak("OOB array index") if $self->{count} && $index >= $self->{count};
83 53         121 my $ptr = $self->{ptr} + $member_size * $index;
84 53         110 $member_class->new($ptr,$self);
85 5         52 };
86 5         13 Sub::Util::set_subname(join('::', $self->class), $code);
87 5         30 Sub::Install::install_sub({
88             code => $code,
89             into => $self->class,
90             as => 'get',
91             });
92             }
93              
94             {
95 4     4   43 no strict 'refs';
  4         9  
  4         1193  
  5         170  
  5         191  
96 5         39 push @{ join '::', $self->class, 'ISA' }, 'FFI::C::Array';
  5         15  
97             }
98              
99             }
100              
101 10         51 $self;
102             }
103              
104              
105             sub create
106             {
107 7     7 1 277 my $self = shift;
108              
109 7 50       19 return $self->class->new(@_) if $self->class;
110              
111 7         20 local $self->{size} = $self->{size};
112 7         15 my $count = $self->{members}->{count};
113 7 100       18 if(@_ == 1)
114             {
115 3 50       22 if(! is_ref $_[0])
    50          
116             {
117 0         0 $count = shift;
118             }
119             elsif(is_plain_arrayref $_[0])
120             {
121 3         6 $count = scalar @{$_[0]};
  3         9  
122             }
123 3 50       8 if($count)
124             {
125 3         11 $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         26 my $array = $self->SUPER::create(@_);
132 7         81 $array->{count} = $count;
133 7 100 66     41 FFI::C::Util::perl_to_c($array, $_[0]) if @_ == 1 && is_plain_arrayref $_[0];
134 7         23 return $array;
135             }
136              
137 0           Carp::croak("Cannot create array without knowing the number of elements");
138             }
139              
140             1;
141              
142             __END__