Home > Back-end >  byte sequences in Perl
byte sequences in Perl

Time:11-22

I have an array and each item in the array is an array of 2 numbers the values of which are in the range of 16 bits.
For each item in the array I need to place these 2 numbers inside a 32 bit integer and then save it in a table in a MySQL database in a column of type varbinary.

I think I need to use pack with template S! to create one 32 bit integer containing the other 2 numbers in the upper/lower 16 bits.
I think I need to use pack with template I! for creating the final value to be saved in the database.
But I am not sure how I can do that with pack and I don't really understand the example in the doc about how to form a loop.

my( $ip, $cs,
    $flags,$fl,$fh,
    $ax,$al,$ah, $bx,$bl,$bh, $cx,$cl,$ch, $dx,$dl,$dh, 
    $si, $di, $bp, $ds, $es ) =
unpack( 'v2' . ('vXXCC' x 5) . 'v5', $frame );
 

Could someone please help with how can I use pack for implementing this? Or is pack not the right solution for both cases?

For instance for creating one 32 bit number the following snippet:

my $number1 = 120;
my $number2 = 3090;
my $word = pack( 'S!*S!*', $number1, $number2 );
print "$word \n";  

prints: x

I was expecting that it would print 1 number (32 bit integer). What am I doing wrong/misunderstanding here?

Update after @ikegami comment:

  1. 32-bit integer
  2. $number1 is the high order bits
  3. Big endian

Update 2 after @ikegami comments. I tried the following:

my @array = (
 [120, 3090],
 [34, 2018],
 [47, 4005],
 [98, 2345],
 [111, 1]
);

my @packed;
foreach ( @array ) {
    my @elem = $_;
    print $elem[0][0]. " - ". $elem[0][1]. "\n";
    my $id = $elem[0][0];
    my $num = $elem[0][1];
    my $word = pack( 'S>2', $id, $num);
    push @packed, $word;
    say sprintf "%v02X", $word;
}

my $packed_output = pack('I*', @packed); // line 63
say sprintf "%v02X", $packed_output;

This fails with the following print:

00.78.0C.12
120 - 3090
00.78.0C.12
34 - 2018
00.22.07.E2
47 - 4005
00.2F.0F.A5
98 - 2345
00.62.09.29
111 - 1
00.6F.00.01
Argument "\0x\f^R" isn't numeric in pack at line 63.
Argument "\0"^G?" isn't numeric in pack at  line 63.
Argument "\0/^O?" isn't numeric in pack at  line 63.
Argument "\0b^I)" isn't numeric in pack at  line 63.
Argument "\0o\0^A" isn't numeric in pack at line 63.
00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00

CodePudding user response:

Close.

The double * makes no sense. S!*S!* should be S!*, S!S! or S!2.

S! is wrong. That refers to an unsigned int, which is at least 16-bits, but it can vary. You want exactly 16-bit, so that should be S.

You also didn't specify the byte order. Since you want big-ending byte order, you actually want S> (or n).

my $packed = pack( "S>2", $number1, $number2 );  # "\x00\x78\x0C\x12"

say sprintf "%v02X", $packed;  # 00.78.0C.12

As you can see, this produces a string of four bytes rather than a number. But that is what you want. VARBINARY is used to store a sequence of bytes. Unpacking the string of bytes using using L> gets the 32-bit number.

This brings up an alternative. You could also compute the 32-bit number and pack that.

my $packed = pack( "L>", $number1 << 16 | $number2 );  # "\x00\x78\x0C\x12"

say sprintf "%v02X", $packed;  # 00.78.0C.12

Reference: Formats for Packing and Unpacking Numbers

  • Related