File Coverage

blib/lib/Data/Cmp.pm
Criterion Covered Total %
statement 59 59 100.0
branch 31 32 96.8
condition 18 24 75.0
subroutine 6 6 100.0
pod 1 1 100.0
total 115 122 94.2


line stmt bran cond sub pod time code
1             package Data::Cmp;
2              
3             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
4             our $DATE = '2021-04-12'; # DATE
5             our $DIST = 'Data-Cmp'; # DIST
6             our $VERSION = '0.009'; # VERSION
7              
8 1     1   73139 use 5.010001;
  1         13  
9 1     1   6 use strict;
  1         3  
  1         25  
10 1     1   5 use warnings;
  1         2  
  1         49  
11              
12 1     1   8 use Scalar::Util qw(blessed reftype refaddr);
  1         2  
  1         618  
13              
14             require Exporter;
15             our @ISA = qw(Exporter);
16             our @EXPORT_OK = qw(cmp_data);
17              
18             # for when dealing with circular refs
19             my %_seen_refaddrs;
20              
21             sub _cmp_data {
22 39     39   49 my $d1 = shift;
23 39         91 my $d2 = shift;
24              
25 39         59 my $def1 = defined $d1;
26 39         45 my $def2 = defined $d2;
27 39 100       72 if ($def1) {
28 37 100       79 return 1 if !$def2;
29             } else {
30 2 100       12 return $def2 ? -1 : 0;
31             }
32              
33             # so both are defined ...
34              
35 36         95 my $reftype1 = reftype($d1);
36 36         51 my $reftype2 = reftype($d2);
37 36 100 100     186 if (!$reftype1 && !$reftype2) {
    100 75        
38 15         58 return $d1 cmp $d2;
39 2         11 } elsif ( $reftype1 xor $reftype2) { return 2 }
40              
41             # so both are refs ...
42              
43 19 100       45 return 2 if $reftype1 ne $reftype2;
44              
45             # so both are refs of the same type ...
46              
47 18         37 my $pkg1 = blessed($d1);
48 18         27 my $pkg2 = blessed($d2);
49 18 100       34 if (defined $pkg1) {
50 2 100 66     14 return 2 unless defined $pkg2 && $pkg1 eq $pkg2;
51             } else {
52 16 50       26 return 2 if defined $pkg2;
53             }
54              
55             # so both are non-objects or objects of the same class ...
56              
57 17         30 my $refaddr1 = refaddr($d1);
58 17         30 my $refaddr2 = refaddr($d2);
59              
60 17 100 66     101 if ($reftype1 eq 'ARRAY' && !$_seen_refaddrs{$refaddr1} && !$_seen_refaddrs{$refaddr2}) {
    100 66        
      66        
      66        
61 7         25 $_seen_refaddrs{$refaddr1}++;
62 7         15 $_seen_refaddrs{$refaddr2}++;
63             ELEM:
64 7 100       8 for my $i (0..($#{$d1} < $#{$d2} ? $#{$d1} : $#{$d2})) {
  7         15  
  7         12  
  2         5  
  5         16  
65 4         12 my $cmpres = _cmp_data($d1->[$i], $d2->[$i]);
66 4 100       21 return $cmpres if $cmpres;
67             }
68 4         7 return $#{$d1} <=> $#{$d2};
  4         7  
  4         28  
69             } elsif ($reftype1 eq 'HASH' && !$_seen_refaddrs{$refaddr1} && !$_seen_refaddrs{$refaddr2}) {
70 8         17 $_seen_refaddrs{$refaddr1}++;
71 8         14 $_seen_refaddrs{$refaddr2}++;
72 8         17 my $nkeys1 = keys %$d1;
73 8         13 my $nkeys2 = keys %$d2;
74             KEY:
75 8         30 for my $k (sort keys %$d1) {
76 9 100 100     20 unless (exists $d2->{$k}) { return $nkeys1 <=> $nkeys2 || 2 }
  4         36  
77 5         13 my $cmpres = _cmp_data($d1->{$k}, $d2->{$k});
78 5 100       16 return $cmpres if $cmpres;
79             }
80 3         16 return $nkeys1 <=> $nkeys2;
81             } else {
82 2 100       14 return $refaddr1 == $refaddr2 ? 0 : 2;
83             }
84             }
85              
86             sub cmp_data {
87 30     30 1 22536 my $d1 = shift;
88 30         44 my $d2 = shift;
89              
90 30         59 %_seen_refaddrs = ();
91 30         64 _cmp_data($d1, $d2);
92             }
93              
94             1;
95             # ABSTRACT: Compare two data structures, return -1/0/1 like cmp
96              
97             __END__