use warnings; use 5.012; # for "package BLOCK" syntax BEGIN { $INC{'MyUtils.pm'}=__FILE__; # emulate "use" package MyUtils { use Exporter 'import'; our @EXPORT_OK = qw/ binread /; sub binread { # helper function for read+unpack my ($fh, $bytes, $templ) = @_; read($fh, my $data, $bytes) == $bytes or die "failed to read $bytes bytes"; return unpack($templ, $data); } } } package Packet { use Moo; use MyUtils qw/binread/; has type => ( is => 'ro' ); has length => ( is => 'ro' ); my %packet_types = ( 0x01 => 'MainHeader', 0x02 => 'ExtHeader', 0x13 => 'Section' ); sub parse { my ($class, $fh) = @_; my ($type, $length) = binread($fh, 2, "CC"); die "Unknown packet type $type" unless $packet_types{$type}; return $packet_types{$type} ->new( type=>$type, length=>$length )->parse($fh); } } package MainHeader { use Moo; use MyUtils qw/binread/; extends 'Packet'; has foo => ( is => 'rwp' ); sub parse { my ($self, $fh) = @_; my ($foo) = binread($fh, $self->length, 'Z*'); $self->_set_foo($foo); return $self; } } package ExtHeader { use Moo; use MyUtils qw/binread/; extends 'Packet'; has bar => ( is => 'rwp' ); sub parse { my ($self, $fh) = @_; my ($bar) = binread($fh, $self->length, 'N'); $self->_set_bar($bar); return $self; } } package Section { use Moo; use MyUtils qw/binread/; extends 'Packet'; has quz => ( is => 'rwp' ); sub parse { my ($self, $fh) = @_; my ($quz) = binread($fh, $self->length, 'n'); $self->_set_quz($quz); return $self; } } sub parsefile { my $file = shift; open my $fh, '<:raw', $file or die $!; my @pkts; while (!eof($fh)) { push @pkts, Packet->parse($fh); } close $fh; return @pkts; } my $binfile = "\x01\x06Hello\0" ."\x02\x04\0\x12\x34\x56" ."\x13\x02\xBE\xEF"; my @packets = parsefile(\$binfile); use Data::Dump; dd @packets; __END__ ( bless({ foo => "Hello", length => 6, type => 1 }, "MainHeader"), bless({ bar => 1193046, length => 4, type => 2 }, "ExtHeader"), bless({ length => 2, quz => 48879, type => 19 }, "Section"), )