Convert::Binary::C で C 構造体をバイナリ変換 (固定長配列)

C の固定長配列をバイナリ変換する。

ヘッダファイル

fixed_array.h

#ifndef _FIXED_ARRAY_H_
#define _FIXED_ARRAY_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_member;
};

struct top_t {
    u_char       char_array[3];
    u_short      short_array[3];
    u_int        int_array[3];
    char         string[5];
    struct sub_t sub_array[3];
};

#pragma pack


#endif /* _FIXED_ARRAY_H_ */

変換前のデータ

配列で表現する。

my %test_data = (
    char_array  => [ 0x01, 0x02, 0x03 ],
    short_array => [ 0x04, 0x05, 0x06 ],
    int_array   => [ 0x07, 0x08, 0x09 ],
    string      => "test",
    sub_array =>
        [ { sub_member => 1 }, { sub_member => 2 }, { sub_member => 3 }, ],
);

文字列

文字列は tag メソッドを使って明示的に示す必要がある。

$c->tag('top_t.string', Format => 'String');

pack

my $packed = $c->pack( 'top_t', \%test_data );
print hexdump( data => $packed );
  0x0000 : 01 02 03 00 04 00 05 00 06 00 00 00 07 00 00 00 : ................
  0x0010 : 08 00 00 00 09 74 65 73 74 00 01 02 03          : .....test....

unpack

my $unpacked = $c->unpack( 'top_t', $packed );
print Dumper($unpacked);
$VAR1 = {
          'int_array' => [
                           7,
                           8,
                           9
                         ],
          'sub_array' => [
                           {
                             'sub_member' => 1
                           },
                           {
                             'sub_member' => 2
                           },
                           {
                             'sub_member' => 3
                           }
                         ],
          'char_array' => [
                            1,
                            2,
                            3
                          ],
          'string' => 'test',
          'short_array' => [
                             4,
                             5,
                             6
                           ]
        };

テストコード

#!/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('./fixed_array.h');

# tagging string format to top_t.string string
$c->tag('top_t.string', Format => 'String');

# data
my %test_data = (
    char_array  => [ 0x01, 0x02, 0x03 ],
    short_array => [ 0x04, 0x05, 0x06 ],
    int_array   => [ 0x07, 0x08, 0x09 ],
    string      => "test",
    sub_array =>
        [ { sub_member => 1 }, { sub_member => 2 }, { sub_member => 3 }, ],
);

# 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
print '--- offset, type, size', $/;
print "type     : ", $c->typeof('top_t.char_array[1]'), $/;
print "size     : ", $c->sizeof('top_t.char_array'), $/;
print "offset   : ", $c->offsetof( 'top_t', 'char_array[1]' ), $/;

# member
print '--- member method', $/;
for my $offset ( 0 .. 10 ) {
    print "\$c->member('top_t', $offset)";
    my $member = eval { $c->member( 'top_t', $offset ) };
    print $@ ? "\n  exception: $@" : " => '$member'\n";
}
--- pack
  0x0000 : 01 02 03 00 04 00 05 00 06 00 00 00 07 00 00 00 : ................
  0x0010 : 08 00 00 00 09 74 65 73 74 00 01 02 03          : .....test....
--- unpack
$VAR1 = {
          'int_array' => [
                           7,
                           8,
                           9
                         ],
          'sub_array' => [
                           {
                             'sub_member' => 1
                           },
                           {
                             'sub_member' => 2
                           },
                           {
                             'sub_member' => 3
                           }
                         ],
          'char_array' => [
                            1,
                            2,
                            3
                          ],
          'string' => 'test',
          'short_array' => [
                             4,
                             5,
                             6
                           ]
        };
--- offset, type, size
type     : u_char
size     : 3
offset   : 1
--- member method
$c->member('top_t', 0) => '.char_array[0]'
$c->member('top_t', 1) => '.char_array[1]'
$c->member('top_t', 2) => '.char_array[2]'
$c->member('top_t', 3) => '.short_array[0]'
$c->member('top_t', 4) => '.short_array[0]+1'
$c->member('top_t', 5) => '.short_array[1]'
$c->member('top_t', 6) => '.short_array[1]+1'
$c->member('top_t', 7) => '.short_array[2]'
$c->member('top_t', 8) => '.short_array[2]+1'
$c->member('top_t', 9) => '.int_array[0]'
$c->member('top_t', 10) => '.int_array[0]+1'