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.008'; # VERSION
7              
8 1     1   76559 use 5.010001;
  1         13  
9 1     1   6 use strict;
  1         2  
  1         27  
10 1     1   5 use warnings;
  1         2  
  1         55  
11              
12 1     1   6 use Scalar::Util qw(blessed reftype refaddr);
  1         3  
  1         583  
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   52 my $d1 = shift;
23 39         49 my $d2 = shift;
24              
25 39         56 my $def1 = defined $d1;
26 39         46 my $def2 = defined $d2;
27 39 100       67 if ($def1) {
28 37 100       85 return 1 if !$def2;
29             } else {
30 2 100       9 return $def2 ? -1 : 0;
31             }
32              
33             # so both are defined ...
34              
35 36         76 my $reftype1 = reftype($d1);
36 36         63 my $reftype2 = reftype($d2);
37 36 100 100     172 if (!$reftype1 && !$reftype2) {
    100 75        
38 15         55 return $d1 cmp $d2;
39 2         8 } 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         31 my $pkg1 = blessed($d1);
48 18         29 my $pkg2 = blessed($d2);
49 18 100       31 if (defined $pkg1) {
50 2 100 66     13 return 2 unless defined $pkg2 && $pkg1 eq $pkg2;
51             } else {
52 16 50       31 return 2 if defined $pkg2;
53             }
54              
55             # so both are non-objects or objects of the same class ...
56              
57 17         46 my $refaddr1 = refaddr($d1);
58 17         23 my $refaddr2 = refaddr($d2);
59              
60 17 100 66     103 if ($reftype1 eq 'ARRAY' && !$_seen_refaddrs{$refaddr1} && !$_seen_refaddrs{$refaddr2}) {
    100 66        
      66        
      66        
61 7         14 $_seen_refaddrs{$refaddr1}++;
62 7         13 $_seen_refaddrs{$refaddr2}++;
63             ELEM:
64 7 100       10 for my $i (0..($#{$d1} < $#{$d2} ? $#{$d1} : $#{$d2})) {
  7         11  
  7         14  
  2         7  
  5         16  
65 4         13 my $cmpres = _cmp_data($d1->[$i], $d2->[$i]);
66 4 100       19 return $cmpres if $cmpres;
67             }
68 4         7 return $#{$d1} <=> $#{$d2};
  4         8  
  4         22  
69             } elsif ($reftype1 eq 'HASH' && !$_seen_refaddrs{$refaddr1} && !$_seen_refaddrs{$refaddr2}) {
70 8         17 $_seen_refaddrs{$refaddr1}++;
71 8         11 $_seen_refaddrs{$refaddr2}++;
72 8         19 my $nkeys1 = keys %$d1;
73 8         11 my $nkeys2 = keys %$d2;
74             KEY:
75 8         26 for my $k (sort keys %$d1) {
76 9 100 100     21 unless (exists $d2->{$k}) { return $nkeys1 <=> $nkeys2 || 2 }
  4         60  
77 5         12 my $cmpres = _cmp_data($d1->{$k}, $d2->{$k});
78 5 100       19 return $cmpres if $cmpres;
79             }
80 3         13 return $nkeys1 <=> $nkeys2;
81             } else {
82 2 100       11 return $refaddr1 == $refaddr2 ? 0 : 2;
83             }
84             }
85              
86             sub cmp_data {
87 30     30 1 20828 my $d1 = shift;
88 30         43 my $d2 = shift;
89              
90 30         53 %_seen_refaddrs = ();
91 30         58 _cmp_data($d1, $d2);
92             }
93              
94             1;
95             # ABSTRACT: Compare two data structures, return -1/0/1 like cmp
96              
97             __END__