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