File Coverage

blib/lib/CPU/x86_64/InstructionWriter/_int32.pm
Criterion Covered Total %
statement 16 59 27.1
branch 3 18 16.6
condition n/a
subroutine 6 10 60.0
pod n/a
total 25 87 28.7


line stmt bran cond sub pod time code
1             package # Hide from CPAN, since this module is not terribly re-usable (yet)
2             CPU::x86_64::InstructionWriter::_int32;
3 17     17   12397 use strict;
  17         43  
  17         502  
4 17     17   90 use warnings;
  17         48  
  17         454  
5 17     17   85 use Exporter 'import';
  17         36  
  17         9653  
6             our @EXPORT_OK = qw( pack int64 );
7              
8             # ABSTRACT: Handle 64-bit integer operations on 32-bit perl
9              
10             # This wrapper only handles the specific use ELF::Writer makes of the pack
11             # function which are not supported on all perls:
12             # - 5.8 perl does not support ">" "<" modifiers.
13             # - Perl compiled without 64-bit integers doesn't support "Q".
14             # It would be nice if there were a standalone module that enhances 'pack' on
15             # those old perls.
16              
17             # On 5.8, a 64-bit big-endian system needs to byte-swap 'Q<' fields
18             sub _pack_wrapper_64_5_8_be {
19 0     0   0 my $fmt= shift;
20 0         0 my $new_fmt= '';
21 0         0 my @new_args;
22 0         0 for (split / +/, $fmt) { # ELF::Writer uses spaces between all fields
23 0 0       0 if ($_ eq 'Q<') {
    0          
24             # Convert 64-bit integer into two 32-bit little-endian arguments
25 0         0 $new_fmt .= 'VV';
26 0         0 my $qw= shift;
27 0         0 push @new_args, ($qw & '4294967295'), ($qw >> 32);
28             } elsif ($_ eq 'Q>') {
29 0         0 $new_fmt .= 'Q';
30 0         0 push @new_args, shift;
31             } else {
32 0         0 $new_fmt .= $_;
33 0         0 push @new_args, shift;
34             }
35             }
36 0         0 return pack $new_fmt, @new_args;
37             }
38              
39             # On 5.8, a 64-bit little-endian system needs to byte-swap 'Q>' fields
40             sub _pack_wrapper_64_5_8_le {
41 0     0   0 my $fmt= shift;
42 0         0 my $new_fmt= '';
43 0         0 my @new_args;
44 0         0 for (split / +/, $fmt) {
45 0 0       0 if ($_ eq 'Q>') {
    0          
46             # Convert 64-bit integer into two 32-bit big-endian arguments
47 0         0 $new_fmt .= 'NN';
48 0         0 my $qw= shift;
49 0         0 push @new_args, ($qw >> 32), ($qw & '4294967295');
50             } elsif ($_ eq 'Q<') {
51 0         0 $new_fmt .= 'Q';
52 0         0 push @new_args, shift;
53             } else {
54 0         0 $new_fmt .= $_;
55 0         0 push @new_args, shift;
56             }
57             }
58 0         0 return pack $new_fmt, @new_args;
59             }
60              
61             # On perl without 64-bit support, replace all 'Q' with 32-bit operations
62             sub _pack_wrapper_32 {
63 0     0   0 my $fmt= shift;
64 0         0 my $new_fmt= '';
65 0         0 my @new_args;
66 0         0 my $mask32= Math::BigInt->new('4294967295');
67 0         0 for (split / *(?=[A-Za-z])/, $fmt) {
68 0 0       0 if ($_ eq 'Q>') {
    0          
    0          
69             # Convert a 64-bit integer into two 32-bit big-endian arguments
70 0         0 $new_fmt .= 'NN';
71 0         0 my $qw= Math::BigInt->new(shift);
72 0         0 push @new_args, ($qw >> 32)->numify(), ($qw & $mask32)->numify();
73             } elsif ($_ eq 'Q<') {
74             # Convert 64-bit integer into two 32-bit little-endian arguments
75 0         0 $new_fmt .= 'VV';
76 0         0 my $qw= Math::BigInt->new(shift);
77 0         0 push @new_args, ($qw & $mask32)->numify(), ($qw >> 32)->numify();
78             } elsif ($_ eq 'Q') {
79 0         0 Carp::croak("Ambiguous 64-bit value");
80             } else {
81 0         0 $new_fmt .= $_;
82 0         0 push @new_args, shift;
83             }
84             }
85 0         0 return pack $new_fmt, @new_args;
86             }
87              
88             sub _int64_native {
89 17     17   143 no warnings 'portable';
  17         65  
  17         2402  
90 46 100   46   47156 $_[0] =~ /^(-?)0x(.*)/? hex($2)*($1? -1 : 1) : 0+$_[0]
    50          
91             }
92             sub _int64_bigint {
93 0     0     Math::BigInt->new($_[0])
94             }
95              
96 17     17   140 no strict 'refs';
  17         37  
  17         4448  
97             # Do we have full support?
98             if (eval { pack('Q<', 1) }) {
99             *pack= CORE->can('pack')? \*CORE::pack : sub { pack(@_) };
100             }
101             # Do we have 64bit?
102             elsif (eval { pack('Q', 1) }) {
103             # choose correct endian
104             *pack= (pack('Q', 1) eq "\x01\0\0\0\0\0\0\0")
105             ? \&_pack_wrapper_64_5_8_le
106             : \&_pack_wrapper_64_5_8_be;
107             }
108             # else need BigInteger implementation
109             else {
110             require Math::BigInt;
111             *pack= \&_pack_wrapper_32;
112             }
113              
114             # Can scalars hold 64-bit ints natively?
115             if ((0x7FFFFFFE << 31) > 0 && (0x7FFFFFFE << 63) == 0) { # 64-bit scalar support
116             *int64= \&_int64_native;
117             } else {
118             *int64= \&_int64_bigint;
119             }
120              
121             __END__