Skip to content

Commit

Permalink
Merge branch 'release/v0.083'
Browse files Browse the repository at this point in the history
  • Loading branch information
peczenyj committed Dec 13, 2023
2 parents 9dc37eb + d146178 commit 99d84ce
Show file tree
Hide file tree
Showing 11 changed files with 184 additions and 260 deletions.
6 changes: 6 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
0.083
- 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

0.082
- increase TO_JSON performance by 76% on bitfields and 3116% on range based vendor section
- add json section 'publisher' and include all publisher restriction, if any, per purpose id, vendor id and restriction type
Expand Down
1 change: 0 additions & 1 deletion MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ lib/GDPR/IAB/TCFv2/Constants/Purpose.pm
lib/GDPR/IAB/TCFv2/Constants/RestrictionType.pm
lib/GDPR/IAB/TCFv2/Constants/SpecialFeature.pm
lib/GDPR/IAB/TCFv2/PublisherRestrictions.pm
lib/GDPR/IAB/TCFv2/RangeConsent.pm
lib/GDPR/IAB/TCFv2/RangeSection.pm
LICENSE
Makefile.PL
Expand Down
14 changes: 9 additions & 5 deletions README.pod
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ GDPR::IAB::TCFv2 - Transparency & Consent String version 2 parser

=head1 VERSION

Version 0.082
Version 0.083

=head1 SYNOPSIS

Expand Down Expand Up @@ -122,19 +122,19 @@ Parse may receive an optional hash parameter C<json> with the following properti
=item *

C<verbose> changes the json encoding. By default we omit some false values such as C<vendor_consents> to create
a compact json representation. With C<verbose> we will present everything. See L<TO_JSON> for more details.
a compact json representation. With C<verbose> we will present everything. See L</TO_JSON> for more details.

=item *

C<compact> changes the json encoding. All fields that are a mapping of something to a boolean will be changed to an array
of all elements keys where the value is true. This affects the following fields: C<special_features_opt_in>,
C<purpose/consents>, C<purpose/legitimate_interests>, C<vendor/consents> and C<vendor/legitimate_interests>. See L<TO_JSON> for more details.
C<purpose/consents>, C<purpose/legitimate_interests>, C<vendor/consents> and C<vendor/legitimate_interests>. See L</TO_JSON> for more details.

=item *

C<use_epoch> changes the json encode. By default we format the C<created> and C<last_updated> are converted to string using
L<ISO_8601|https://en.wikipedia.org/wiki/ISO_8601>. With C<use_epoch> we will return the unix epoch in seconds.
See L<TO_JSON> for more details.
See L</TO_JSON> for more details.

=item *

Expand Down Expand Up @@ -409,7 +409,7 @@ Outputs:
}


If L<JSON> is installed, the C<TO_JSON> method will use C<JSON::true> and C<JSON::false> as boolean value.
If L<JSON> is installed, the L</TO_JSON> method will use C<JSON::true> and C<JSON::false> as boolean value.

By default it returns a compacted format where we omit the C<false> on fields like C<vendor_consents> and we convert the dates
using L<ISO_8601|https://en.wikipedia.org/wiki/ISO_8601>. This behaviour can be changed by extra option in the L<Parse> constructor.
Expand All @@ -428,6 +428,10 @@ The original documentation of the L<TCF v2 from IAB documentation|https://github

Tiago Peczenyj L<mailto:tiago.peczenyj+gdpr-iab-tcfv2@gmail.com>

=head1 THANKS

Special thanks to L<ikegami|https://metacpan.org/author/IKEGAMI> for the patience on several question about Perl on L<Stack Overflow|https://stackoverflow.com>.

=head1 BUGS

Please report any bugs or feature requests to L<https://github.com/peczenyj/GDPR-IAB-TCFv2/issues>.
Expand Down
98 changes: 43 additions & 55 deletions lib/GDPR/IAB/TCFv2.pm
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use GDPR::IAB::TCFv2::BitUtils qw<is_set
use GDPR::IAB::TCFv2::PublisherRestrictions;
use GDPR::IAB::TCFv2::RangeSection;

our $VERSION = "0.082";
our $VERSION = "0.083";

use constant {
CONSENT_STRING_TCF2_SEPARATOR => '.',
Expand Down 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
);

$restrictions{$purpose_id} ||= {};
my $data =
substr( $self->{data}, $pub_restrict_offset, ASSUMED_MAX_VENDOR_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 Expand Up @@ -636,7 +620,7 @@ GDPR::IAB::TCFv2 - Transparency & Consent String version 2 parser
=head1 VERSION
Version 0.082
Version 0.083
=head1 SYNOPSIS
Expand Down Expand Up @@ -732,19 +716,19 @@ Parse may receive an optional hash parameter C<json> with the following properti
=item *
C<verbose> changes the json encoding. By default we omit some false values such as C<vendor_consents> to create
a compact json representation. With C<verbose> we will present everything. See L<TO_JSON> for more details.
a compact json representation. With C<verbose> we will present everything. See L</TO_JSON> for more details.
=item *
C<compact> changes the json encoding. All fields that are a mapping of something to a boolean will be changed to an array
of all elements keys where the value is true. This affects the following fields: C<special_features_opt_in>,
C<purpose/consents>, C<purpose/legitimate_interests>, C<vendor/consents> and C<vendor/legitimate_interests>. See L<TO_JSON> for more details.
C<purpose/consents>, C<purpose/legitimate_interests>, C<vendor/consents> and C<vendor/legitimate_interests>. See L</TO_JSON> for more details.
=item *
C<use_epoch> changes the json encode. By default we format the C<created> and C<last_updated> are converted to string using
L<ISO_8601|https://en.wikipedia.org/wiki/ISO_8601>. With C<use_epoch> we will return the unix epoch in seconds.
See L<TO_JSON> for more details.
See L</TO_JSON> for more details.
=item *
Expand Down Expand Up @@ -1019,7 +1003,7 @@ Outputs:
}
If L<JSON> is installed, the C<TO_JSON> method will use C<JSON::true> and C<JSON::false> as boolean value.
If L<JSON> is installed, the L</TO_JSON> method will use C<JSON::true> and C<JSON::false> as boolean value.
By default it returns a compacted format where we omit the C<false> on fields like C<vendor_consents> and we convert the dates
using L<ISO_8601|https://en.wikipedia.org/wiki/ISO_8601>. This behaviour can be changed by extra option in the L<Parse> constructor.
Expand All @@ -1038,6 +1022,10 @@ The original documentation of the L<TCF v2 from IAB documentation|https://github
Tiago Peczenyj L<mailto:tiago.peczenyj+gdpr-iab-tcfv2@gmail.com>
=head1 THANKS
Special thanks to L<ikegami|https://metacpan.org/author/IKEGAMI> for the patience on several question about Perl on L<Stack Overflow|https://stackoverflow.com>.
=head1 BUGS
Please report any bugs or feature requests to L<https://github.com/peczenyj/GDPR-IAB-TCFv2/issues>.
Expand Down
28 changes: 8 additions & 20 deletions lib/GDPR/IAB/TCFv2/BitField.pm
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,28 @@ 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 ),
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
Loading

0 comments on commit 99d84ce

Please sign in to comment.