File Coverage

blib/lib/GDPR/IAB/TCFv2/RangeSection.pm
Criterion Covered Total %
statement 100 100 100.0
branch 29 44 65.9
condition 6 9 66.6
subroutine 13 13 100.0
pod 4 5 80.0
total 152 171 88.8


line stmt bran cond sub pod time code
1             package GDPR::IAB::TCFv2::RangeSection;
2 5     5   4529 use strict;
  5         29  
  5         205  
3 5     5   45 use warnings;
  5         10  
  5         329  
4 5     5   32 use integer;
  5         9  
  5         62  
5 5     5   154 use bytes;
  5         10  
  5         49  
6              
7 5     5   340 use GDPR::IAB::TCFv2::BitUtils qw;
  5         13  
  5         459  
8 5     5   52 use Carp qw;
  5         13  
  5         7466  
9              
10             sub Parse {
11 41     41 0 256 my ( $klass, %args ) = @_;
12              
13 41 50       128 croak "missing 'data'" unless defined $args{data};
14 41 50       103 croak "missing 'data_size'" unless defined $args{data_size};
15 41 50       97 croak "missing 'offset'" unless defined $args{offset};
16             croak "missing 'max_id'"
17 41 50       91 unless defined $args{max_id};
18              
19 41 50       109 croak "missing 'options'" unless defined $args{options};
20 41 50       106 croak "missing 'options.json'" unless defined $args{options}->{json};
21              
22 41         88 my $data = $args{data};
23 41         71 my $data_size = $args{data_size};
24 41         70 my $offset = $args{offset};
25 41         66 my $max_id = $args{max_id};
26 41         108 my $options = $args{options};
27              
28 41 50       100 Carp::confess
29             "a BitField for vendor consent strings using RangeSections require at least 31 bytes. Got $data_size"
30             if $data_size < 31;
31              
32 41         70 my %prefetch;
33             my %cache;
34              
35 41 100       120 if ( exists $options->{prefetch} ) {
36 2         4 my $vendor_ids = $options->{prefetch};
37              
38 2         2 foreach my $vendor_id ( @{$vendor_ids} ) {
  2         3  
39 18         30 $prefetch{$vendor_id} = 1;
40 18         19 $cache{$vendor_id} = 0;
41             }
42             }
43              
44 41         228 my $self = {
45             ranges => [],
46             cache => \%cache,
47             max_id => $max_id,
48             options => $options,
49             };
50              
51 41         101 bless $self, $klass;
52              
53 41         147 my $next_offset = $self->_parse( $data, $data_size, $offset, \%prefetch );
54              
55 41         204 return ( $self, $next_offset );
56             }
57              
58             sub _parse {
59 41     41   126 my ( $self, $data, $data_size, $offset, $prefetch ) = @_;
60              
61 41         119 my ( $num_entries, $next_offset ) = get_uint12( $data, $offset );
62              
63 41         123 foreach my $i ( 1 .. $num_entries ) {
64 319         743 $next_offset = $self->_parse_range(
65             $data,
66             $data_size,
67             $next_offset,
68             $prefetch,
69             );
70             }
71              
72 41         81 return $next_offset;
73             }
74              
75             sub _parse_range {
76 319     319   703 my ( $self, $data, $data_size, $offset, $prefetch ) = @_;
77              
78 319 50       705 croak
79             "bit $offset was suppose to start a new range entry, but the consent string was only $data_size bytes long"
80             if $data_size <= $offset / 8;
81              
82 319         563 my $max_id = $self->{max_id};
83              
84             # If the first bit is set, it's a Range of IDs
85 319         778 my ( $is_range, $next_offset ) = is_set $data, $offset;
86 319 100       789 if ($is_range) {
87 74         146 my ( $start, $end );
88              
89 74         172 ( $start, $next_offset ) = get_uint16( $data, $next_offset );
90 74         174 ( $end, $next_offset ) = get_uint16( $data, $next_offset );
91              
92 74 50       171 croak
93             "bit $offset range entry exclusion starts at $start, but the min vendor ID is 1"
94             if 1 > $start;
95              
96 74 50       163 croak
97             "bit $offset range entry exclusion ends at $end, but the max vendor ID is $max_id"
98             if $end > $max_id;
99              
100 74 50       151 croak "start $start can't be bigger than end $end" if $start > $end;
101              
102 74         118 push @{ $self->{ranges} }, [ $start, $end ];
  74         271  
103              
104 74         127 foreach my $vendor_id ( keys %{$prefetch} ) {
  74         197  
105 16 50 66     34 $self->{cache}->{$vendor_id} = delete( $prefetch->{$vendor_id} )
106             if $start <= $vendor_id && $vendor_id <= $end;
107             }
108              
109 74         204 return $next_offset;
110             }
111              
112 245         376 my $vendor_id;
113              
114 245         538 ( $vendor_id, $next_offset ) = get_uint16( $data, $next_offset );
115              
116 245 50 33     1039 croak
117             "bit $offset range entry exclusion vendor $vendor_id, but only vendors [1, $max_id] are valid"
118             if 1 > $vendor_id || $vendor_id > $max_id;
119              
120 245         369 push @{ $self->{ranges} }, [ $vendor_id, $vendor_id ];
  245         887  
121              
122             $self->{cache}->{$vendor_id} = delete( $prefetch->{$vendor_id} )
123 245 100       626 if exists $prefetch->{$vendor_id};
124              
125 245         548 return $next_offset;
126             }
127              
128             sub max_id {
129 2     2 1 2 my $self = shift;
130              
131 2         7 return $self->{max_id};
132             }
133              
134             sub contains {
135 2537     2537 1 6245 my ( $self, $id ) = @_;
136              
137 2537 50       8920 croak "invalid vendor id $id: must be positive integer bigger than 0"
138             if $id < 1;
139              
140 2537 100       13216 return $self->{cache}->{$id} if exists $self->{cache}->{$id};
141              
142 2519 50       7436 return if $id > $self->{max_id};
143              
144 2519         4602 foreach my $range ( @{ $self->{ranges} } ) {
  2519         10626  
145 14895 100 100     48295 return 1 if $range->[0] <= $id && $id <= $range->[1];
146             }
147              
148 2474         15795 return 0;
149             }
150              
151             sub all {
152 14     14 1 22 my $self = shift;
153              
154 14         23 my @vendors;
155 14         21 foreach my $range ( @{ $self->{ranges} } ) {
  14         34  
156 54         129 push @vendors, $range->[0] .. $range->[1];
157             }
158              
159 14         56 return \@vendors;
160             }
161              
162             sub TO_JSON {
163 12     12 1 20 my $self = shift;
164              
165 12 100       40 return $self->all if !!$self->{options}->{json}->{compact};
166              
167 4         5 my ( $false, $true ) = @{ $self->{options}->{json}->{boolean_values} };
  4         9  
168              
169 4         5 my %map;
170 4 100       8 if ( !!$self->{options}->{json}->{verbose} ) {
171 2         44 %map = map { $_ => $false } 1 .. $self->{max_id};
  1254         1888  
172             }
173              
174 4         69 foreach my $range ( @{ $self->{ranges} } ) {
  4         10  
175 24         550 %map = ( %map, map { $_ => $true } $range->[0] .. $range->[1] );
  32         1320  
176             }
177              
178 4         29 return \%map;
179             }
180              
181             1;
182             __END__