The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

use strict;
our @ISA = qw{Data::ParseBinary::Adapter};
# TODO: implement as macro in terms of SymmetricMapping (macro)
# that is implemented as MappingAdapter
sub _init {
my ($self, @params) = @_;
my $decode = {};
my $encode = {};
$self->{have_default} = 0;
$self->{default_action} = undef;
while (@params) {
my $key = shift @params;
my $value = shift @params;
if ($key eq '_default_') {
$self->{have_default} = 1;
$self->{default_action} = $value;
if (ref $value) {
if ($value != $Data::ParseBinary::BaseConstruct::DefaultPass) {
die "Enum Error: got invalid value as default";
}
} elsif (exists $encode->{$value}) {
die "Enum Error: $value should not be defined as regular case";
} else {
$self->{default_value} = shift @params;
}
next;
}
$encode->{$key} = $value;
$decode->{$value} = $key;
}
$self->{encode} = $encode;
$self->{decode} = $decode;
}
sub _decode {
my ($self, $value) = @_;
if (exists $self->{decode}->{$value}) {
return $self->{decode}->{$value};
}
if ($self->{have_default}) {
if (ref($self->{default_action}) and $self->{default_action} == $Data::ParseBinary::BaseConstruct::DefaultPass) {
return $value;
}
return $self->{default_action};
}
die "Enum: unrecognized value $value, and no default defined";
}
sub _encode {
my ($self, $tvalue) = @_;
if (exists $self->{encode}->{$tvalue}) {
return $self->{encode}->{$tvalue};
}
if ($self->{have_default}) {
if (ref($self->{default_action}) and $self->{default_action} == $Data::ParseBinary::BaseConstruct::DefaultPass) {
return $tvalue;
}
return $self->{default_value};
}
die "Enum: unrecognized value $tvalue";
}
our @ISA = qw{Data::ParseBinary::Adapter};
sub _init {
my ($self, @mapping) = @_;
my @pairs;
die "FlagsEnum: Mapping should be even" if @mapping % 2 == 1;
while (@mapping) {
my $name = shift @mapping;
my $value = shift @mapping;
push @pairs, [$name, $value];
}
$self->{pairs} = \@pairs;
}
sub _decode {
my ($self, $value) = @_;
my $hash = {};
foreach my $rec (@{ $self->{pairs} }) {
$hash->{$rec->[0]} = 1 if $value & $rec->[1];
}
return $hash;
}
sub _encode {
my ($self, $tvalue) = @_;
my $value = 0;
foreach my $rec (@{ $self->{pairs} }) {
if (exists $tvalue->{$rec->[0]} and $tvalue->{$rec->[0]}) {
$value |= $rec->[1];
}
}
return $value;
}
our @ISA = qw{Data::ParseBinary::Adapter};
sub _init {
my ($self, $sub_name) = @_;
$self->{sub_name} = $sub_name;
}
sub _decode {
my ($self, $value) = @_;
return $value->{$self->{sub_name}};
}
sub _encode {
my ($self, $tvalue) = @_;
return {$self->{sub_name} => $tvalue};
}
our @ISA = qw{Data::ParseBinary::Adapter};
sub _init {
my ($self, $index) = @_;
$self->{index} = $index || 0;
}
sub _decode {
my ($self, $value) = @_;
return $value->[$self->{index}];
}
sub _encode {
my ($self, $tvalue) = @_;
return [ ('') x $self->{index}, $tvalue ];
}
our @ISA = qw{Data::ParseBinary::Adapter};
sub _decode {
my ($self, $value) = @_;
return join '', @$value;
}
sub _encode {
my ($self, $tvalue) = @_;
return [split '', $tvalue];
}
our @ISA = qw{Data::ParseBinary::Adapter};
sub _init {
my ($self, $value) = @_;
$self->{value} = $value;
}
sub _decode {
my ($self, $value) = @_;
if (not $value eq $self->{value}) {
die "Const Error: expected $self->{value} got $value";
}
return $value;
}
sub _encode {
my ($self, $tvalue) = @_;
if (not defined $self->_get_name()) {
# if we don't have a name, then just use the value
return $self->{value};
}
if (defined $tvalue and $tvalue eq $self->{value}) {
return $self->{value};
}
die "Const Error: expected $self->{value} got ". (defined $tvalue ? $tvalue : "undef");
}
our @ISA = qw{Data::ParseBinary::Adapter};
sub _decode {
my ($self, $value) = @_;
return $value->[1];
}
sub _encode {
my ($self, $tvalue) = @_;
return [length($tvalue), $tvalue];
}
our @ISA = qw{Data::ParseBinary::Adapter};
sub _init {
my ($self, %params) = @_;
if (not defined $params{length}) {
die "PaddedStringAdapter: you must specify length";
}
$self->{length} = $params{length};
$self->{encoding} = $params{encoding};
$self->{padchar} = defined $params{padchar} ? $params{padchar} : "\x00";
$self->{paddir} = $params{paddir} || "right";
$self->{trimdir} = $params{trimdir} || "right";
if (not grep($_ eq $self->{paddir}, qw{right left center})) {
die "PaddedStringAdapter: paddir should be one of {right left center}";
}
if (not grep($_ eq $self->{trimdir}, qw{right left})) {
die "PaddedStringAdapter: trimdir should be one of {right left}";
}
}
sub _decode {
my ($self, $value) = @_;
my $tvalue = $value;
my $char = $self->{padchar};
if ($self->{paddir} eq 'right' or $self->{paddir} eq 'center') {
$tvalue =~ s/$char*\z//;
} elsif ($self->{paddir} eq 'left' or $self->{paddir} eq 'center') {
$tvalue =~ s/\A$char*//;
}
return $tvalue;
}
sub _encode {
my ($self, $tvalue) = @_;
my $value = $tvalue;
if (length($value) < $self->{length}) {
my $add = $self->{length} - length($value);
my $char = $self->{padchar};
if ($self->{paddir} eq 'right') {
$value .= $char x $add;
} elsif ($self->{paddir} eq 'left') {
$value = ($char x $add) . $value;
} elsif ($self->{paddir} eq 'center') {
my $add_left = $add / 2;
my $add_right = $add_left + ($add % 2 == 0 ? 0 : 1);
$value = ($char x $add_left) . $value . ($char x $add_right);
}
}
if (length($value) > $self->{length}) {
my $remove = length($value) - $self->{length};
if ($self->{trimdir} eq 'right') {
substr($value, $self->{length}, $remove, '');
} elsif ($self->{trimdir} eq 'left') {
substr($value, 0, $remove, '');
}
}
return $value;
}
#package Data::ParseBinary::StringAdapter;
#our @ISA = qw{Data::ParseBinary::Adapter};
#
#sub _init {
# my ($self, $encoding) = @_;
# $self->{encoding} = $encoding;
#}
#
#sub _decode {
# my ($self, $value) = @_;
# my $tvalue;
# if ($self->{encoding}) {
# die "TODO: Should implement different encodings";
# } else {
# $tvalue = $value;
# }
# return $tvalue;
#}
#
#sub _encode {
# my ($self, $tvalue) = @_;
# my $value;
# if ($self->{encoding}) {
# die "TODO: Should implement different encodings";
# } else {
# $value = $tvalue;
# }
# return $value;
#}
our @ISA = qw{Data::ParseBinary::Adapter};
sub _init {
my ($self, $terminators) = @_;
$self->{regex} = qr/[$terminators]*\z/;
$self->{terminator} = substr($terminators, 0, 1);
}
sub _decode {
my ($self, $value) = @_;
$value =~ s/$self->{regex}//;
return $value;
}
sub _encode {
my ($self, $tvalue) = @_;
return $tvalue . $self->{terminator};
}
our @ISA = qw{Data::ParseBinary::Validator};
sub _init {
my ($self, @params) = @_;
$self->{coderef} = shift @params;
}
sub _validate {
my ($self, $value) = @_;
return $self->{coderef}->($value);
}
our @ISA = qw{Data::ParseBinary::Adapter};
# this adapter move from a length of bytes, to one unit and the rest
# as an array
sub _init {
my ($self, $unit_length, $first_name, $the_rest) = @_;
$first_name ||= 'FirstUnit';
$the_rest ||= 'TheRest';
$self->{unit_length} = $unit_length;
$self->{first_name} = $first_name;
$self->{the_rest} = $the_rest;
}
sub _decode {
my ($self, $value) = @_;
$value = join('', $value->{$self->{first_name}}, @{ $value->{$self->{the_rest}} } );
return $value;
}
sub _encode {
my ($self, $tvalue) = @_;
my $u_len = $self->{unit_length};
die "Length of input should be dividable by unit_length" unless length($tvalue) % $u_len == 0;
my @units = map substr($tvalue, $_*$u_len, $u_len), 0..(length($tvalue) / $u_len - 1);
my $first = shift @units;
my $value = { $self->{first_name} => $first, $self->{the_rest} => \@units };
return $value;
}
our @ISA = qw{Data::ParseBinary::Adapter};
sub _init {
my ($self, $encoding) = @_;
$self->{encoding} = $encoding;
require Encode;
}
sub _decode {
my ($self, $octets) = @_;
my $string = Encode::decode($self->{encoding}, $octets);
return $string;
}
sub _encode {
my ($self, $string) = @_;
my $octets = Encode::encode($self->{encoding}, $string);
return $octets;
}
our @ISA = qw{Data::ParseBinary::Adapter};
sub _init {
my ($self, $is_signed, $is_bigendian) = @_;
$self->{is_signed} = $is_signed;
$self->{is_bigendian} = $is_bigendian;
require Math::BigInt;
}
sub _decode {
my ($self, $value) = @_;
if (not $self->{is_bigendian}) {
$value = join '', reverse split '', $value;
}
my $is_negative;
if ($self->{is_signed}) {
my $first_char = ord($value);
if ($first_char > 127) {
$value = ~$value;
$is_negative = 1;
}
}
my $hexed = unpack "H*", $value;
my $number = Math::BigInt->new("0x$hexed");
if ($is_negative) {
$number->binc()->bneg();
}
return $number;
}
sub _encode {
my ($self, $number) = @_;
$number = Math::BigInt->new($number);
my $is_negative;
if ($self->{is_signed}) {
if ($number->sign() eq '-') {
$is_negative = 1;
$number->binc()->babs();
}
} else {
if ($number->sign() eq '-') {
die "Was given a negative number for unsigned integer";
}
}
my $hexed = $number->as_hex();
substr($hexed, 0, 2, '');
my $packed = pack "H*", ("0"x(16-length($hexed))).$hexed;
if ($is_negative) {
$packed = ~$packed;
}
if (not $self->{is_bigendian}) {
$packed = join '', reverse split '', $packed;
}
return $packed;
}
1;