Convert::Binary::C で C 構造体をバイナリ変換 (入れ子になった構造体)

入れ子になった C 構造体をバイナリ変換する。

ヘッダファイル

sub_struct.h

#ifndef SUB_STRUCT_H
#define SUB_STRUCT_H

typedef unsigned char  u_char;
typedef unsigned short u_short;
typedef unsigned int   u_int;
typedef unsigned long  u_long;

#pragma pack(1)

struct sub_t {
    u_char sub_member1;
    u_char sub_member2;
};

struct top_t {
    u_char top_member1;
    u_char top_member2;
    struct sub_t sub;
};

#pragma pack

#endif

変換前のデータ

ハッシュのハッシュで表現する。

# data
my %test_data = (
    top_member1 => '0x12',    # 18
    top_member2 => '0x34',    # 53
    sub         => {
        sub_member1 => '0x56',    # 86
        sub_member2 => '0x78',    # 120
    },
);

pack

my $packed = $c->pack( 'top_t', \%test_data );
print hexdump( data => $packed );

出力

  0x0000 : 12 34 56 78                                     : .4Vx

unpack

my $unpacked = $c->unpack( 'top_t', $packed );
print Dumper($unpacked);

出力

$VAR1 = {
          'top_member1' => 18,
          'sub' => {
                     'sub_member1' => 86,
                     'sub_member2' => 120
                   },
          'top_member2' => 52
        };

テストコード

#!/usr/bin/perl
use strict;
use warnings;
use Carp;
use Convert::Binary::C;
use Data::Dumper;
use Data::Hexdumper;

my $c = Convert::Binary::C->new();
$c->configure( ByteOrder => 'BigEndian' );
$c->parse_file('./sub_struct.h');

# data
my %test_data = (
    top_member1 => '0x12',    # 18
    top_member2 => '0x34',    # 53
    sub         => {
        sub_member1 => '0x56',    # 86
        sub_member2 => '0x78',    # 120
    },
);

# pack
my $packed = $c->pack( 'top_t', \%test_data );
print '--- pack', $/;
print hexdump( data => $packed );

# unpack
my $unpacked = $c->unpack( 'top_t', $packed );
print '--- unpack', $/;
print Dumper($unpacked);

# offset, type, size
sub print_info {
    my ( $struct_name, $c, $packed_ref, $base_offset ) = @_;
    if ( !defined $base_offset ) {
        $base_offset = 0;
    }

    while ( my ( $member, $value ) = each %$packed_ref ) {
        my $absolute_name = $struct_name . '.' . $member;

        print "--- member $member", $/;
        print "absolute : $absolute_name\n";

        print "type     : ", $c->typeof($absolute_name), $/;
        print "size     : ", $c->sizeof($absolute_name), $/;
        my $offset = $c->offsetof( $struct_name, $member ) + $base_offset;
        print "offset   : ", $offset, $/;

        if ( ref($value) eq 'HASH' ) {

            # sub struct
            print_info( $absolute_name, $c, $value, $offset );
        }
        else {
            print "value    : $value", $/;
        }
    }
}

print '--- each member info', $/;
print_info( 'top_t', $c, $unpacked );

# member
print '--- member method', $/;
for my $offset ( 0 .. $c->sizeof('top_t') - 1 ) {
    print "\$c->member('top_t', $offset)";
    my $member = eval { $c->member( 'top_t', $offset ) };
    print $@ ? "\n  exception: $@" : " => '$member'\n";
}

出力

--- pack
  0x0000 : 12 34 56 78                                     : .4Vx
--- unpack
$VAR1 = {
          'top_member1' => 18,
          'sub' => {
                     'sub_member1' => 86,
                     'sub_member2' => 120
                   },
          'top_member2' => 52
        };
--- each member info
--- member top_member1
absolute : top_t.top_member1
type     : u_char
size     : 1
offset   : 0
value    : 18
--- member sub
absolute : top_t.sub
type     : struct sub_t
size     : 2
offset   : 2
--- member sub_member1
absolute : top_t.sub.sub_member1
type     : u_char
size     : 1
offset   : 2
value    : 86
--- member sub_member2
absolute : top_t.sub.sub_member2
type     : u_char
size     : 1
offset   : 3
value    : 120
--- member top_member2
absolute : top_t.top_member2
type     : u_char
size     : 1
offset   : 1
value    : 52
--- member method
$c->member('top_t', 0) => '.top_member1'
$c->member('top_t', 1) => '.top_member2'
$c->member('top_t', 2) => '.sub.sub_member1'
$c->member('top_t', 3) => '.sub.sub_member2'