Skip to content

Commit

Permalink
Merge branch 'devel' of github.com:peczenyj/GDPR-IAB-TCFv2 into devel
Browse files Browse the repository at this point in the history
  • Loading branch information
peczenyj committed Dec 13, 2023
2 parents 89bda65 + e0ef785 commit 5c62481
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 112 deletions.
2 changes: 2 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
- refactor on Publisher Restriction parsing.
- small fixes about data and offset.
- performance improvement: when we parse a range-based consent string now the Parse method is 23% faster, TO_JSON is 9% faster and check vendor consent or legitimate interest is between 122% and 137% faster than the previous version
- remove GDPR::IAB::TCFv2::RangeConsent package

Expand Down
82 changes: 33 additions & 49 deletions lib/GDPR/IAB/TCFv2.pm
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,6 @@ sub Parse {

croak 'invalid vendor list version' if $self->vendor_list_version == 0;

# TODO parse special feature opt in

#_parse_bitfield()

# TODO parse purpose section
# TODO parse purpose consent

# TODO parse purpose legitimate interest

# parse vendor section
# parse vendor consent

Expand Down Expand Up @@ -330,7 +321,7 @@ sub check_publisher_restriction {
my ( $self, $purpose_id, $restrict_type, $vendor ) = @_;

return $self->{publisher_restrictions}
->check_publisher_restriction( $purpose_id, $restrict_type, $vendor );
->contains( $purpose_id, $restrict_type, $vendor );
}

sub _format_date {
Expand Down Expand Up @@ -495,37 +486,20 @@ sub _parse_vendor_legitimate_interests {
sub _parse_publisher_restrictions {
my ( $self, $pub_restrict_offset ) = @_;

my ( $num_restrictions, $next_offset ) =
get_uint12( $self->{data}, $pub_restrict_offset );

my %restrictions;

for ( 1 .. $num_restrictions ) {
my ( $purpose_id, $restriction_type, $vendor_restrictions );

( $purpose_id, $next_offset ) =
get_uint6( $self->{data}, $next_offset );

( $restriction_type, $next_offset ) =
get_uint2( $self->{data}, $next_offset );

( $vendor_restrictions, $next_offset ) = $self->_parse_range_section(
ASSUMED_MAX_VENDOR_ID,
$next_offset
);
my $data =
substr( $self->{data}, $pub_restrict_offset, ASSUMED_MAX_VENDOR_ID );

$restrictions{$purpose_id} ||= {};

$restrictions{$purpose_id}->{$restriction_type} = $vendor_restrictions;
}

my $publisher_restrictions = GDPR::IAB::TCFv2::PublisherRestrictions->new(
restrictions => \%restrictions,
);
my ( $publisher_restrictions, $relative_next_offset ) =
GDPR::IAB::TCFv2::PublisherRestrictions->Parse(
data => $data,
data_size => length( $self->{data} ),
max_id => ASSUMED_MAX_VENDOR_ID,
options => $self->{options},
);

$self->{publisher_restrictions} = $publisher_restrictions;

return $next_offset;
return $pub_restrict_offset + $relative_next_offset;
}

sub _get_core_tc_string {
Expand Down Expand Up @@ -571,30 +545,40 @@ sub _is_vendor_consent_range_encoding {
}

sub _parse_range_section {
my ( $self, $max_id, $offset ) = @_;
my ( $self, $max_id, $range_section_start_offset ) = @_;

my $data = substr( $self->{data}, $range_section_start_offset, $max_id );

my ( $range_section, $next_offset ) =
GDPR::IAB::TCFv2::RangeSection->Parse(
data => $self->{data},
offset => $offset,
max_id => $max_id,
options => $self->{options},
data => $data,
data_size => length( $self->{data} ),
offset => 0,
max_id => $max_id,
options => $self->{options},
);

return ( $range_section, $next_offset );
return
wantarray
? ( $range_section, $range_section_start_offset + $next_offset )
: $range_section;
}

sub _parse_bitfield {
my ( $self, $max_id, $offset ) = @_;
my ( $self, $max_id, $bitfield_start_offset ) = @_;

my $data = substr( $self->{data}, $bitfield_start_offset, $max_id );

my ( $bitfield, $next_offset ) = GDPR::IAB::TCFv2::BitField->Parse(
data => $self->{data},
offset => $offset,
max_id => $max_id,
options => $self->{options},
data => $data,
data_size => length( $self->{data} ),
max_id => $max_id,
options => $self->{options},
);

return ( $bitfield, $next_offset );
return wantarray
? ( $bitfield, $bitfield_start_offset + $next_offset )
: $bitfield;
}

sub looksLikeIsConsentVersion2 {
Expand Down
26 changes: 7 additions & 19 deletions lib/GDPR/IAB/TCFv2/BitField.pm
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,27 @@ use Carp qw<croak>;
sub Parse {
my ( $klass, %args ) = @_;

croak "missing 'data'" unless defined $args{data};
croak "missing 'offset'" unless defined $args{offset};
croak "missing 'data'" unless defined $args{data};
croak "missing 'max_id'"
unless defined $args{max_id};

croak "missing 'options'" unless defined $args{options};
croak "missing 'options.json'" unless defined $args{options}->{json};

my $data = $args{data};
my $offset = $args{offset};
my $max_id = $args{max_id};
my $options = $args{options};

my $data_size = length($data);
my $data = $args{data};
my $data_size = $args{data_size};
my $offset = 0;
my $max_id = $args{max_id};
my $options = $args{options};

# add 7 to force rounding to next integer value
my $bytes_required = ( $max_id + $offset + 7 ) / 8;
my $bytes_required = ( $max_id + 7 ) / 8;

croak
"a BitField for $max_id requires a consent string of $bytes_required bytes. This consent string had $data_size"
if $data_size < $bytes_required;

my $self = {

# TODO consider store data as arrayref of bits
data => substr( $data, $offset, $max_id ),
max_id => $max_id,
options => $options,
Expand All @@ -56,14 +52,6 @@ sub contains {
return is_set( $self->{data}, $id - 1 );
}

sub all {
my $self = shift;

my @data = split //, $self->{data};

return [ grep { $data[ $_ - 1 ] } 1 .. $self->{max_id} ];
}

sub TO_JSON {
my $self = shift;

Expand Down
16 changes: 10 additions & 6 deletions lib/GDPR/IAB/TCFv2/BitUtils.pm
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@ our @EXPORT_OK = qw<is_set
sub is_set {
my ( $data, $offset ) = @_;

croak "index out of bounds on offset $offset"
if $offset + 1 > length($data);
my $data_size = length($data);

croak
"index out of bounds on offset $offset: can't read 1, only has: $data_size"
if $offset + 1 > $data_size;

my $r = substr( $data, $offset, 1 ) == 1;

Expand Down Expand Up @@ -139,8 +142,6 @@ sub get_uint36 {
sub _get_bits_with_padding {
my ( $data, $bits, $offset, $nbits ) = @_;

# TODO check if offset is in range of $data ?

my ( $data_with_padding, $next_offset ) =
_add_padding( $data, $bits, $offset, $nbits );

Expand All @@ -152,8 +153,11 @@ sub _get_bits_with_padding {
sub _add_padding {
my ( $data, $bits, $offset, $nbits ) = @_;

croak "index out of bounds on offset $offset"
if $offset + $nbits > length($data);
my $data_size = length($data);

croak
"index out of bounds on offset $offset: can't read $nbits, only has: $data_size"
if $offset + $nbits > $data_size;

my $padding = "0" x ( $bits - $nbits );

Expand Down
68 changes: 56 additions & 12 deletions lib/GDPR/IAB/TCFv2/PublisherRestrictions.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,67 @@ use warnings;

use Carp qw<croak>;

sub new {
use GDPR::IAB::TCFv2::BitUtils qw<is_set
get_uint2
get_uint6
get_uint12
get_uint16
get_uint36
get_char6_pair
>;

sub Parse {
my ( $klass, %args ) = @_;

my $restrictions = $args{restrictions}
or croak "missing field 'restrictions'";
croak "missing 'data'" unless defined $args{data};
croak "missing 'data_size'" unless defined $args{data_size};
croak "missing 'max_id'"
unless defined $args{max_id};

croak "missing 'options'" unless defined $args{options};
croak "missing 'options.json'" unless defined $args{options}->{json};

my $data = $args{data};
my $data_size = $args{data_size};
my $offset = 0;
my $max_id = $args{max_id};
my $options = $args{options};

my ( $num_restrictions, $next_offset ) = get_uint12( $data, $offset );

my %restrictions;

for ( 1 .. $num_restrictions ) {
my ( $purpose_id, $restriction_type, $vendor_restrictions );

( $purpose_id, $next_offset ) = get_uint6( $data, $next_offset );

( $restriction_type, $next_offset ) = get_uint2( $data, $next_offset );

( $vendor_restrictions, $next_offset ) =
GDPR::IAB::TCFv2::RangeSection->Parse(
data => $data,
data_size => $data_size,
offset => $next_offset,
max_id => $max_id,
options => $options,
);

$restrictions{$purpose_id} ||= {};

$restrictions{$purpose_id}->{$restriction_type} = $vendor_restrictions;
}

my $self = {
restrictions => $restrictions,
restrictions => \%restrictions,
};

bless $self, $klass;

return $self;
return wantarray ? ( $self, $next_offset ) : $self;
}

sub check_publisher_restriction {
sub contains {
my ( $self, $purpose_id, $restrict_type, $vendor ) = @_;

return 0
Expand Down Expand Up @@ -62,12 +107,11 @@ GDPR::IAB::TCFv2::PublisherRestrictions - Transparency & Consent String version
=head1 SYNOPSIS
my $range = GDPR::IAB::TCFv2::PublisherRestrictions->new(
restrictions => {
purpose id => {
restriction type => instance of GDPR::IAB::TCFv2::RangeSection
},
},
my ($publisher_restrictions, $next_offset) = GDPR::IAB::TCFv2::PublisherRestrictions->Parse(
data => $self->{data},
offset => $pub_restrict_offset,
max_id =>ASSUMED_MAX_VENDOR_ID,
options => $self->{options},
);
die "there is publisher restriction on purpose id 1, type 0 on vendor 284"
Expand Down
Loading

0 comments on commit 5c62481

Please sign in to comment.