#!/usr/bin/perl -w use strict; use warnings; use Data::Dumper; use Data::ParseBinary; use Test::More; $| = 1; my $data; my $string; my $s; my $s1; ok( UBInt16("foo")->parse("\x01\x02") == 258, "Primitive: Parse: UBInt16"); ok( ULInt16("foo")->parse("\x01\x02") == 513, "Primitive: Parse: ULInt16"); ok( UBInt16("foo")->build(31337) eq 'zi', "Primitive: Build: UBInt16"); ok( SBInt16("foo")->build(-31337) eq "\x85\x97", "Primitive: Build: SBInt16"); ok( SLInt16("foo")->build(-31337) eq "\x97\x85", "Primitive: Build: SLInt16"); ok( BFloat32("foo")->build(5) eq "\x40\xa0\0\0", "Primitive: Build: BFloat32"); ok( LFloat32("foo")->build(5) eq "\0\0\xa0\x40", "Primitive: Build: LFloat32"); ok( BFloat32("foo")->parse("\x40\xa0\0\0") == 5, "Primitive: Parse: BFloat32"); ok( LFloat64("foo")->build(5) eq "\0\0\0\0\0\0\x14\x40", "Primitive: Build: LFloat64"); ok( LFloat64("foo")->parse("\0\0\0\0\0\0\x14\x40") == 5, "Primitive: Build: LFloat64"); $s = Struct("foo", UBInt8("a"), SLInt16("b") ); $data = {a => 7, b => 256}; $string = "\x07\x00\x01"; is_deeply($s->parse($string), $data, "Struct: Parse: correct"); ok( $s->build($data) eq $string, "Struct: Build: Rebuild1"); $data->{b} = 5000; ok( $s->build($data) eq "\x07\x88\x13", "Struct: Build: Rebuild2"); $s = Struct("foo", UBInt8("a"), UBInt16("b"), Struct("bar", UBInt8("a"), UBInt16("b"), ) ); $data = {a=>65, b=>16962, bar=>{ a=>97, b=> 25186}}; $string = "ABBabb"; is_deeply($s->parse($string), $data, "Nested Struct: Parse: correct"); ok( $s->build($data) eq $string, "Nested Struct: Build: Rebuild1"); $s = Sequence("foo", UBInt8("a"), UBInt16("b") ); $data = [97, 25186]; $string = "abb"; is_deeply($s->parse($string), $data, "Sequence: Parse: correct"); ok( $s->build($data) eq $string, "Sequence: Build: Rebuild1"); ok( $s->build([1,2]) eq "\x01\x00\x02", "Sequence: Build: correct"); $s = Sequence("foo", UBInt8("a"), UBInt16("b"), Sequence("bar", UBInt8("a"), UBInt16("b"), ) ); $data = [65, 16962, [97, 25186]]; $string = "ABBabb"; is_deeply($s->parse($string), $data, "Nested Sequence: Parse: correct"); ok( $s->build($data) eq $string, "Nested Sequence: Build: correct"); $s = Array(4, UBInt8("foo")); $data = $s->parse("\x01\x02\x03\x04"); is_deeply( $s->parse("\x01\x02\x03\x04"), [1..4], "StrictRepeater: Parse: correct elements1"); eval { $data = $s->parse("\x01\x02\x03") }; ok( $@ , "StrictRepeater: Parse: Die on too few elements"); is_deeply( $s->parse("\x01\x02\x03\x04\x05"), [1..4], "StrictRepeater: Parse: correct elements2"); ok( $s->build([5,6,7,8]) eq "\x05\x06\x07\x08", "StrictRepeater: Build: normal build"); eval { $s->build([5,6,7,8,9]) }; ok( $@, "StrictRepeater: Build: dies on too many elements"); $s = Array(5, Array(2, UBInt8("foo"))); $data = [[97,97], [98,98], [99,99], [100,100], [101,101]]; $string = "aabbccddee"; is_deeply($s->parse($string), $data, "Nested StrictRepeater: Parse: correct"); ok( $s->build($data) eq $string, "Nested StrictRepeater: Build: correct"); $s = Struct("foo", Padding(2), Flag("myflag"), Padding(5), ); $data = {myflag => 1}; $string = "\x00\x00\x01\x00\x00\x00\x00\x00"; is_deeply($s->parse($string), $data, "Struct with Padding, Flag: Parse: correct"); ok( $s->build($data) eq $string, "Struct with Padding, Flag: Build: correct"); $s = BitStruct("foo", Padding(2), Flag("myflag"), Padding(5), ); $data = {myflag => 1}; $string = "\x20"; is_deeply($s->parse($string), $data, "BitStruct with Padding, Flag: Parse: correct"); ok( $s->build($data) eq $string, "BitStruct with Padding, Flag: Build: correct"); $s = BitStruct("foo", BitField("a", 3), Flag("b"), Padding(3), Nibble("c"), BitField("d", 5), ); $data = {a=>7, b=>0, c=>8, d=>31}; $string = "\xe1\x1f"; is_deeply($s->parse($string), $data, "BitStruct: Parse: correct"); ok( $s->build($data) eq $string, "BitStruct: Build: correct"); $s = BitStruct("foo", BitField("a", 3), Flag("b"), Padding(3), Nibble("c"), Struct("bar", Nibble("d"), Bit("e"), ) ); $data = { a=>7, b=>0, c=>8, bar=>{ d=>15, e=>1 } }; $string = "\xe1\x1f"; is_deeply($s->parse($string), $data, "Nested BitStruct: Parse: correct"); ok($s->build($data) eq $string, "Nested BitStruct: Build: correct"); $s = BitStruct("foo", BitField("a", 3), Flag("b"), Byte("c"), ); $data = { a=>7, b=>0, c=>59 }; $string = "\xe3\xb0"; is_deeply($s->parse($string), $data, "BitStruct with Byte: Parse: correct"); ok( $s->build($data) eq $string, "BitStruct with Byte: Build: correct"); $s = Enum(Byte("protocol"), TCP => 6, UDP => 17, ); ok( $s->parse("\x06") eq 'TCP', "Enum: correct1"); ok( $s->parse("\x11") eq 'UDP', "Enum: correct1"); eval { $s->parse("\x12") }; ok( $@, "Enum: dies on undeclared value with default"); ok( $s->build("TCP") eq "\x06", "Enum: build 1"); ok( $s->build("UDP") eq "\x11", "Enum: build 2"); $s = Enum(Byte("protocol"), TCP => 6, UDP => 17, _default_ => blah => 99, ); ok( $s->parse("\x11") eq 'UDP', "Enum with default: correct1"); ok( $s->parse("\x12") eq 'blah', "Enum with default: correct2"); ok( $s->build("TCP") eq "\x06", "Enum with default: build 1"); ok( $s->build("blah") eq "\x63", "Enum with default: build default"); $s = Enum(Byte("protocol"), TCP => 6, UDP => 17, _default_ => $DefaultPass, ); ok( $s->parse("\x11") eq 'UDP', "Enum with pass: correct1"); ok( $s->parse("\x12") == 18, "Enum with pass: correct2"); ok( $s->parse("\xff") == 255, "Enum with pass: correct3"); ok( $s->build("TCP") eq "\x06", "Enum with pass: build 1"); ok( $s->build(18) eq "\x12", "Enum with pass: build 2"); ok( $s->build(255) eq "\xff", "Enum with pass: build 3"); ok( OneOf(UBInt8("foo"), [4,5,6,7])->parse("\x05") == 5, "OneOf: Parse: passing"); eval { OneOf(UBInt8("foo"), [4,5,6,7])->parse("\x08") }; ok( $@, "OneOf: Parse: blocking"); ok( OneOf(UBInt8("foo"), [4,5,6,7])->build(5) eq "\x05", "OneOf: Build: passing"); eval { OneOf(UBInt8("foo"), [4,5,6,7])->build(8) }; ok( $@, "OneOf: Build: blocking"); ok( NoneOf(UBInt8("foo"), [4,5,6,7])->parse("\x08") == 8, "NoneOf: Parse: passing"); eval { NoneOf(UBInt8("foo"), [4,5,6,7])->parse("\x06") }; ok( $@, "NoneOf: Parse: blocking"); ok( NoneOf(UBInt8("foo"), [4,5,6,7])->build(8) eq "\x08", "NoneOf: Build: passing"); eval { NoneOf(UBInt8("foo"), [4,5,6,7])->build(6) }; ok( $@, "NoneOf: Build: blocking"); $s = Struct("foo", Byte("length"), Field("data", sub { $_->ctx->{length} }), ); $data = {data=> 'ABC', length => 3}; $string = "\x03ABC"; is_deeply( $s->parse($string), $data, "MetaField: Parse: correct1"); ok( $s->build($data) eq $string, "MetaField: Build: correct1"); $data = {data=> 'ABCD', length => 4}; $string = "\x04ABCD"; is_deeply( $s->parse($string), $data, "MetaField: Parse: correct2"); ok( $s->build($data) eq $string, "MetaField: Build: correct2"); ok( Field("foo", 3)->parse("ABCD") eq "ABC", "Field: Parse: route to StaticField"); ok( Field("foo", sub {return 3})->parse("ABCD") eq "ABC", "Field: Parse: route to MetaField"); $s = Struct("foo", Byte("length"), Array(sub { $_->ctx->{length}}, UBInt16("data")), ); $data = {length => 3, data => [1,2,3]}; $string = "\x03\x00\x01\x00\x02\x00\x03"; is_deeply( $s->parse($string), $data, "MetaRepeater: Parse: correct"); ok( $s->build($data) eq $string, "MetaRepeater: Build: correct"); $s = RepeatUntil(sub {$_->obj eq "\x00"}, Field("data", 1)); $data = [ split('', "abcdef\x00") ]; $string = "abcdef\x00this is another string"; is_deeply( $s->parse($string), $data, "RepeatUntil: Parse: correct"); $string = "abcdef\x00"; ok( $s->build($data) eq $string, "RepeatUntil: Build: correct"); $s = Struct("foo", Enum(Byte("type"), INT1 => 1, INT2 => 2, INT4 => 3, STRING => 4, ), Switch("data", sub { $_->ctx->{type} }, { "INT1" => UBInt8("spam"), "INT2" => UBInt16("spam"), "INT4" => UBInt32("spam"), "STRING" => String("spam", 6), } ) ); $data = {type => 'INT1', data => 18}; $string = "\x01\x12"; is_deeply( $s->parse($string), $data, "Switch: Parse: correct1"); ok( $s->build($data) eq $string, "Switch: Build: correct1"); $data = {type => 'INT2', data => 4660}; $string = "\x02\x12\x34"; is_deeply( $s->parse($string), $data, "Switch: Parse: correct2"); ok( $s->build($data) eq $string, "Switch: Build: correct2"); $data = {type => 'INT4', data => 305419896}; $string = "\x03\x12\x34\x56\x78"; is_deeply( $s->parse($string), $data, "Switch: Parse: correct3"); ok( $s->build($data) eq $string, "Switch: Build: correct3"); $data = {type => 'STRING', data => 'abcdef'}; $string = "\x04abcdef"; is_deeply( $s->parse($string), $data, "Switch: Parse: correct4"); ok( $s->build($data) eq $string, "Switch: Build: correct4"); $s = Struct("foo", Byte("type"), Switch("data", sub { $_->ctx->{type} }, { 1 => UBInt8("spam"), 2 => UBInt16("spam"), }, default => UBInt8("spam") ) ); $data = {type => 1, data => 255}; $string = "\x01\xff"; is_deeply( $s->parse($string), $data, "Switch with default: Parse: correct1"); ok( $s->build($data) eq $string, "Switch with default: Build: correct1"); $data = {type => 2, data => 65535}; $string = "\x02\xff\xff"; is_deeply( $s->parse($string), $data, "Switch with default: Parse: correct2"); ok( $s->build($data) eq $string, "Switch with default: Build: correct2"); $data = {type => 3, data => 255}; $string = "\x03\xff\xff"; # <-- uses the default construct is_deeply( $s->parse($string), $data, "Switch with default: Parse: correct3"); ok( $s->build($data) eq "\x03\xff", "Switch with default: Build: correct3"); $s = Struct("foo", Byte("type"), Switch("data", sub { $_->ctx->{type} }, { 1 => UBInt8("spam"), 2 => UBInt16("spam"), }, default => $DefaultPass, ) ); $data = {type => 1, data => 255}; $string = "\x01\xff"; is_deeply( $s->parse($string), $data, "Switch with pass: Parse: correct1"); ok( $s->build($data) eq $string, "Switch with pass: Build: correct1"); $data = {type => 2, data => 65535}; $string = "\x02\xff\xff"; is_deeply( $s->parse($string), $data, "Switch with pass: Parse: correct2"); ok( $s->build($data) eq $string, "Switch with pass: Build: correct2"); $data = {type => 3, data => undef}; $string = "\x03\xff\xff"; # <-- uses the default construct is_deeply( $s->parse($string), $data, "Switch with pass: Parse: correct3"); ok( $s->build($data) eq "\x03", "Switch with pass: Build: correct3"); $s = Struct("foo", Pointer(sub { 4 }, Byte("data1")), # <-- data1 is at (absolute) position 4 Pointer(sub { 7 }, Byte("data2")), # <-- data2 is at (absolute) position 7 ); $data = {data1 => 1, data2=> 2}; $string = "\x00\x00\x00\x00\x01\x00\x00\x02"; is_deeply( $s->parse($string), $data, "Pointer: Parse: correct"); ok( $s->build($data) eq $string, "Pointer: Build: Empty"); $s = Struct("foo", Byte("padding_length"), Padding(sub { $_->ctx->{padding_length} } ), Byte("relative_offset"), Anchor("absolute_position"), Pointer(sub { $_->ctx->{absolute_position} + $_->ctx->{relative_offset} }, Byte("data")), ); $data = {relative_offset=>3, absolute_position=>7, data=>255, padding_length=>5}; $string = "\x05\x00\x00\x00\x00\x00\x03\x00\x00\x00\xff"; is_deeply( $s->parse($string), $data, "Pointer n Anchor: Parse: Correct"); ok(( $s->build($data) eq $string ), "Pointer n Anchor: Build: Correct"); $s = Struct("foo", Byte("padding_length"), Padding(sub { $_->ctx->{padding_length} } ), Byte("relative_offset"), Pointer(sub { $_->stream->tell + $_->ctx->{relative_offset} }, Byte("data")), ); $data = {relative_offset=>3, data=>255, padding_length=>5}; $string = "\x05\x00\x00\x00\x00\x00\x03\x00\x00\x00\xff"; is_deeply( $s->parse($string), $data, "Pointer n Anchor: Parse: Correct"); ok(( $s->build($data) eq $string ), "Pointer n Anchor: Build: Correct"); ok(( String("foo", 5)->parse("hello") eq "hello"), "String: Parse: Simple"); $s = String("foo", 10, padchar => "X", paddir => "right"); ok(( $s->parse("helloXXXXX") eq 'hello' ), "Padded String: Parse: Simple"); ok(( $s->build("hello") eq 'helloXXXXX' ), "Padded String: Build: Simple"); $s = PascalString("foo"); ok(( $s->parse("\x05hello") eq 'hello'), "PascalString: Parse: Simple"); ok(( $s->build("hello world") eq "\x0bhello world"), "PascalString: Build: Simple"); $s = PascalString("foo", \&UBInt16); ok(( $s->parse("\x00\x05hello") eq 'hello'), "PascalString: Parse: With cutsom length type"); ok(( $s->build("hello") eq "\x00\x05hello"), "PascalString: Build: With cutsom length type"); $s = CString("foo"); ok(( $s->parse("hello\x00") eq 'hello' ), "CString: Parse: Simple"); ok(( $s->build("hello") eq "hello\x00" ), "CString: Build: Simple"); $s = CString("foo", terminators => "XYZ"); ok(( $s->parse("helloX") eq 'hello' ), "CString: Parse: custom terminator1"); ok(( $s->parse("helloY") eq 'hello' ), "CString: Parse: custom terminator2"); ok(( $s->parse("helloZ") eq 'hello' ), "CString: Parse: custom terminator3"); ok(( $s->build("hello") eq "helloX" ), "CString: Build: custom terminator"); $s = Struct("foo", UBInt8("width"), UBInt8("height"), Value("total_pixels", sub { $_->ctx->{width} * $_->ctx->{height}}), ); is_deeply( $s->parse("\x05\x05"), { width => 5, height => 5, total_pixels => 25 }, "Value: Parse: Simple"); $data = { width => 5, height => 5 }; ok(( $s->build($data) eq "\x05\x05"), "Value: Parse: Ignored"); is_deeply( $data, { width => 5, height => 5, total_pixels => 25 }, "Value: Parse: Added to hash"); $s = Struct("foo", Flag("has_options"), If(sub { $_->ctx->{has_options} }, Bytes("options", 5) ) ); is_deeply( $s->parse("\x01hello"), {options => 'hello', has_options => 1 }, "If: Parse: True"); is_deeply( $s->parse("\x00hello"), {options => undef, has_options => 0 }, "If: Parse: False"); ok(( $s->build({options => undef, has_options => 0 }) eq "\0"), "If: Build: False"); ok(( $s->build({options => 'hello', has_options => 1 }) eq "\x01hello"), "If: Build: True"); $s = Struct("foo", Flag("long_options"), IfThenElse("options", sub { $_->ctx->{long_options} }, Bytes("Long Options", 5), Bytes("Short Options", 3), ), ); is_deeply( $s->parse("\x01hello"), {options => 'hello', long_options => 1 }, "IfThenElse: Parse: True"); is_deeply( $s->parse("\x00hello"), {options => 'hel', long_options => 0 }, "IfThenElse: Parse: False"); ok(( $s->build({options => 'hel', long_options => 0 }) eq "\0hel"), "IfThenElse: Build: False"); ok(( $s->build({options => 'hello', long_options => 1 }) eq "\x01hello"), "IfThenElse: Build: True"); $s = Struct("foo", Flag("has_next"), If(sub { $_->ctx->{has_next} }, LazyBound("next", sub { $s })), ); $data = { has_next => 1, next => { has_next => 1, next => { has_next => 1, next => { has_next => 0, next => undef } } } }; $string = "\x01\x01\x01\x00"; is_deeply( $s->parse($string), $data, "LazyBound: Parse: Correct"); ok(( $s->build($data) eq $string), "LazyBound: Build: Correct"); $s = Struct("foo", Byte("a"), Peek(Byte("b")), Byte("c"), ); is_deeply( $s->parse("\x01\x02"), {a=>1, b=>2, c=>2}, "Peek: Parse: Simple"); ok(( $s->build({a=>1, b=>222, c=>2}) eq "\x01\x02"), "Peek: Build: Ignored"); $s = Struct("foo", Byte("a"), Peek(Byte("b"), 3), UBInt16("c"), Byte("d"), Byte("e"), ); $string = "\x01\xaa\xbb\x03\x04"; $data = {a=>1, c=>43707, d=>3, e=>4}; is_deeply( $s->parse($string), {%$data, b=>4}, "Far Peek: Parse: Simple"); ok(( $s->build($data) eq $string), "Far Peek: Build: Ignored"); $s = Const(Bytes("magic", 6), "FOOBAR"); ok(($s->parse("FOOBAR") eq "FOOBAR"), "Const: Parse: OK"); eval { $s->parse("FOOBAX") }; ok( $@, "Const: Parse: Dies"); ok(( $s->build("FOOBAR") eq "FOOBAR"), "Const: Build: OK"); eval { $s->build("FOOBAX") }; ok( $@, "Const: Build: Dies"); $s = Terminator(); ok(( not defined $s->parse("")), "Terminator: Parse: ok"); eval { $s->parse("x") }; ok( $@, "Terminator: Parse: dies"); ok(( $s->build({}) eq ""), "Terminator: Build: Empty"); $s = Struct("foo", Byte("a"), Alias("b", "a"), ); is_deeply( $s->parse("\x03"), {a=>3, b=>3}, "Alias: Parse: Simple"); $data = {a=>3}; ok(( $s->build($data) eq "\x03"), "Alias: Build: OK"); is_deeply($data, {a=>3, b=>3}, "Alias: Build: Add value"); $data = {a=>3, b=>5}; ok(( $s->build($data) eq "\x03"), "Alias: Build: Ignore b"); $s = Union("foo", UBInt32("a"), UBInt16("b") ); is_deeply( $s->parse("\xaa\xbb\xcc\xdd"), { a => 2864434397, b => 43707 }, "Union: Parse: Simple"); ok(( $s->build( { a=> 2864434397 } ) eq "\xaa\xbb\xcc\xdd" ), "Union: Build: a"); ok(( $s->build( { b => 43707 } ) eq "\xaa\xbb\0\0" ), "Union: Build: b"); $s = Struct("foo", Aligned(Byte("bbb"), 8), Byte("aaa")); $data = { bbb => 99, aaa=>5 }; $string = "c\0\0\0\0\0\0\0\5"; is_deeply( $s->parse($string), $data, "Aligned: Parse: Correct"); ok(( $s->build($data) eq $string), "Aligned: Build: Correct"); $s = Bitwise(Struct("foo", Padding(2), Flag("myflag"), Padding(5), )); $data = {myflag => 1}; $string = "\x20"; is_deeply($s->parse($string), $data, "Bitwise eq BitStruct: Parse: correct"); ok( $s->build($data) eq $string, "Bitwise eq BitStruct: Build: correct"); $s = Struct("foo1", Byte("a"), Select( Const(Byte("b1"), 4), Const(Byte("b2"), 2), ), Byte("c"), ); $s1 = Struct("foo1", Byte("a"), Select( Const(Byte("b1"), 4), Const(Byte("b2"), 2), $DefaultPass, ), Byte("c"), ); $string = "\3\4\xb0"; $data = { a=>3, b1=>4, c=>176}; is_deeply($s->parse($string), $data, "Select: Parse: OK1"); ok( $s->build($data) eq $string, "Select: Build: OK1"); is_deeply($s1->parse($string), $data, "Select with Pass: Parse: OK1"); ok( $s1->build($data) eq $string, "Select with Pass: Build: OK1"); $string = "\3\2\xb0"; $data = { a=>3, b2=>2, c=>176}; is_deeply($s->parse($string), $data, "Select: Parse: OK2"); ok( $s->build($data) eq $string, "Select: Build: OK2"); is_deeply($s1->parse($string), $data, "Select with Pass: Parse: OK2"); ok( $s1->build($data) eq $string, "Select with Pass: Build: OK2"); $string = "\3\3\xb0"; $data = { a=>3, b2=>3, c=>176}; eval { $s->parse($string) }; ok( $@, "Select: Parse: Failed"); eval { $s->build($data) }; ok( $@, "Select: Build: Failed"); $data = { a=>3, c=>3}; is_deeply($s1->parse($string), $data, "Select with Pass: Parse: Pass"); ok( $s1->build($data) eq "\3\3", "Select with Pass: Build: Pass"); $s = FlagsEnum(ULInt16("characteristics"), RELOCS_STRIPPED => 0x0001, EXECUTABLE_IMAGE => 0x0002, LINE_NUMS_STRIPPED => 0x0004, LOCAL_SYMS_STRIPPED => 0x0008, AGGRESSIVE_WS_TRIM => 0x0010, LARGE_ADDRESS_AWARE => 0x0020, MACHINE_16BIT => 0x0040, BYTES_REVERSED_LO => 0x0080, MACHINE_32BIT => 0x0100, DEBUG_STRIPPED => 0x0200, REMOVABLE_RUN_FROM_SWAP => 0x0400, SYSTEM => 0x1000, DLL => 0x2000, UNIPROCESSOR_ONLY => 0x4000, BIG_ENDIAN_MACHINE => 0x8000, ); $data = {}; $string = "\0\0"; is_deeply($s->parse($string), $data, "FlagsEnum: Parse: Empty"); ok( $s->build($data) eq $string, "FlagsEnum: Build: Empty"); $data = {EXECUTABLE_IMAGE => 1, REMOVABLE_RUN_FROM_SWAP=>1}; $string = "\2\4"; is_deeply($s->parse($string), $data, "FlagsEnum: Parse: Pass"); ok( $s->build($data) eq $string, "FlagsEnum: Build: Pass"); $string = "PNG"; $s = Magic($string); ok( $s->build({ }) eq $string, "Magic: Build: Pass"); eval { $s->parse($string) }; ok( (not $@), "Magic: Parse: OK"); eval { $s->parse("PXNG") }; ok( $@, "Magic: Parse: Dies"); $s = ReversedBitStruct("foo", BitField("a", 3), Flag("b"), Byte("c"), ); $data = { a=>7, b=>0, c=>236 }; $string = pack "B*", "0111011100000011"; is_deeply($s->parse($string), $data, "ReversedBitStruct: Parse: correct"); ok( $s->build($data) eq $string, "ReversedBitStruct: Build: correct"); $s = ReversedBitStruct("foo", BitField("a", 3), Flag("b"), ReversedBitField("c", 8), ); $data = { a=>7, b=>0, c=>236 }; $string = pack "B*", "1100011100001110"; is_deeply($s->parse($string), $data, "ReversedBitStruct with ReversedBitField: Parse: correct"); ok( $s->build($data) eq $string, "ReversedBitStruct with ReversedBitField: Build: correct"); #print Dumper($data); done_testing();