Perl: Separate the MSB and rest of the bits from a hex - perl

I need to separate the MSB and the rest of the bits from a hex string. So for example: I have a hex string a2, which is equivalent to 1010 0010. I want to separate out the MSB (1 in this case), and rest of the number convert to decimal. I think I can do something like this:
$hex = 'a2';
$dec = hex($hex);
$bin = sprintf("%b", $dec);
$msb = substr $bin, 0, 1;
$rest = substr $bin 1, 7;
$restDec = oct("0b" . $rest);
However, I do not like using strings for bit operations. Is there a better way of doing this?

Trivial using bitwise operators:
$msb = ($dec & 128) >> 7
$rest = ($dec & 127)
Explanation:
Decimal 128 is 0x80 or 0b1000_0000, so using the bitwise "and" operator with 128 masks (sets to zero) all but the top bit, which we then shift down to the LSB where the result ends up being 0 or 1. In actuality you could dispense with the masking operation and just shift right but explicitly masking has two advantages:
It makes the intent crystal clear, and
works even if you inadvertently apply this to a number larger than 255.
Decimal 127 is 0x7F or 0b0111_1111 and bitwise "and-ing" this with $dec sets the MSB to zero while leaving alone the rest of the bits.
Additional note: Perl has hexadecimal numeric literals (0x...) and binary literals (0b...), so the above could also be written
$msb = ($dec & 0x80) >> 7
$rest = ($dec & 0x7F)
Or even
$msb = ($dec & 0b10000000) >> 7
$rest = ($dec & 0b01111111)

Related

How to do bitwise compare of two hex strings?

I have two strings representing hex numbers which I need to make a bitwise comparison of. Each hex number equates to 256 bits. I need to determine how many of the bits are different. How can I do this in perl?
$hash1 = "7ff005f88270898ec31359b9ca80213165a318f149267e4c2f292a00e216e4ef";
$hash2 = "3fb40df88a78890e815251b1fb8021356da330f149266f453f292a11e216e4ee";
My question is similar to this question but I need to do it in perl.
my $bytes1 = pack('H*', $hash1);
my $bytes2 = pack('H*', $hash2);
my $xor = unpack('B*', $bytes1 ^ $bytes2);
my $count = $xor =~ tr/1//;
pack('H*', ...) converts the hex strings into byte strings. The byte strings are then XORed and converted to a bit string with unpack('B*', ...). The tr operator is used to count the number of 1s (different bits) in the bit string.
Or, using the checksum trick described here:
my $bytes1 = pack('H*', $hash1);
my $bytes2 = pack('H*', $hash2);
my $count = unpack('%32B*', $bytes1 ^ $bytes2);
$hash1 =~ s/([a-f0-9][a-f0-9])/unpack('B*',pack('H*',$1))/egi;
$hash2 =~ s/([a-f0-9][a-f0-9])/unpack('B*',pack('H*',$1))/egi;
$count = ($hash1 ^ $hash2) =~ tr/\0//c;

How to print least significant 4 bits of signed integer in a file using Perl?

For example:
I have $a= -1. If I print it using printf with %.4b or %b, it gives me 32-bit all 1's.
But, I only want to print the least significant 4 bits like 1111 in the file in binary.
Any ideas how to do it?
Thanks
-1 in binary is represented via 2s complement, so it is all 1s. (See here for more: What is “2's Complement”?)
If you want to 'limit' it, then the way you can do this is with a bitwise and.
Switching on 4 bits is
1+2+4+8 = 15.
Therefore:
use strict;
use warnings;
my $val = -1;
printf ( "%b", $val & 15 );
%.4b refers to fractional digits, %04b formats to at least 4 digits, padding leading 0s as needed.
To cater for negative integers, take the modulus by 16 ( 2^<number of least significant bits> ).
my #b = (12, 59, -1, 1 ); # sample of integers
#b = map { $_ % 16; } #b; # take modulus
printf ("4-bits: %04b" . (", %04b" x $#b) . ";\n", #b );
# output with computed number of placeholders

Bitwise Operator & Logic

I need clarification on a problem that I have. I am currently trying to learn Perl using the Beginning Perl book provided by the "Learn Perl" portion of the Perl website. I am down to the ending of chapter 2 and I am currently having a hard time trying to understand the logic behind a solution that the book provided.
#!usr/bin/perl
#Ex2_3.plx
use warnings;
print "Please enter the value(less than 256) you wish to be converted into binary\n";
my $bin = <STDIN>;
chomp ($bin);
print "The binary value of $bin is : ", "\n";
#Use the bitwise and operator to determine the binary value:
print((128 & $bin) / 128);
print((64 & $bin) / 64);
print((32 & $bin) / 32);
print((16 & $bin) / 16);
print((8 & $bin) / 8);
print((4 & $bin) / 4);
print((2 & $bin) / 2);
print((1 & $bin) / 1);
print("\n");
I can't seem to understand the logic behind (128 & n) / 128 and so forth.
While I do understand why is it that the code needs to go from 128 down to 1 I can't seem to grasp the solution after &. I feel bad because I normally hate looking at solutions, but this is all very intriguing to me, most other programming books put very small(if any) attention to bitwise operators.
I will appreciate the help.
For example, decimal 128 is 10000000 in binary. &ing with this gives you the bit of the most significant bit. / 128 part is the same as shifting the bits to the right 7 times, since dividing the number by 2 is the same as shifting one bit to the right.
10000000 (= 128)
& 10000110 (= 134)
10000000
shifting 7 bits to the right, you get 1
Now, doing the same thing for 64
01000000 (= 64)
& 10000110 (= 134)
00000000
shifting 6 bits to the right (because 64 = 2^6), you get 0
Do this for the rest of the bits, you would get 1, 0, 0, 0, 0, 1, 1, 0, which would look 10000110 from the print statements.
The code from the book is basically the same as this:
#!/usr/bin/perl
use strict;
use warnings;
chomp(my $input = <STDIN>);
for (my $i = 7; $i >= 0; $i--) {
print( (($input & (1 << $i)) >> $i) );
}
print "\n";
The numbers 1,2,4,8 are all powers of 2. So the bitwise & of any number with them will only have 2 possible values, The power of 2 or 0.
Then taking the result and dividing it by itself will give a result of either 0 or 1.
By the way, the easier method to convert a number to binary is to use sprintf:
sprintf "%b", 145;
Outputs:
10010001

Use Perl to Modify a Bitstream

How can I re-pack bits into a bitstream after modifying them in Perl?
I'm currently using the following to unpack:
my $bits = 5;
my $code = '';
foreach my $i (reverse 0..$bits-1) {
$code <<= 1;
$code |= vec($data,$i,1);
}
For example, the output might be 16.
EDIT:
This question relates to the data block of a GIF image.
What I'm trying to do is to pad the LZW codes to match the length required by PDF's LZWDecode method.
LZWDecode expects 8-bit images in which the <Clear> code is 256 and the <End> code is 257 (PDF Reference, page 44.)
For 5-bit images, codes 0-31 map to colors in the image's global color table, <Clear> is 32 and <End> is 33.
So I need to re-pack the bitstream so that codes 0-31 remain the same, but 32+ are offset by 256-32.
I'm using ActiveState Perl for Windows.
# Unpack from 8 bit fields.
my #vals = unpack('C*', $bytes);
... transform #vals into 5 bit values here ...
# Pack to 5 bit fields.
my $bits = join '', map substr(pack('B*', $val), -5), #vals;
$bits .= '0' x (-length($bits) % 8); # Pad with zeros to byte boundary
$bytes = pack('B*', $bits);

How can I convert hex strings into numbers in Perl?

I recently wrote a script which parsed a text representation of a single binary byte month field.
(Don't ask :-{ )
After fiddling with sprintf for a while I gave up and did this;
our %months = qw / x01 1
x02 2
x03 3
x04 4
x05 5
x06 6
x07 7
x08 8
x09 9
x0a 10
x0b 11
x0c 12 /;
...
my $month = $months{$text};
Which I get away with, because I'm only using 12 numbers, but is there a better way of doing this?
If you have
$hex_string = "0x10";
you can use:
$hex_val = hex($hex_string);
And you'll get: $hex_val == 16
hex doesn't require the "0x" at the beginning of the string. If it's missing it will still translate a hex string to a number.
You can also use oct to translate binary, octal or hex strings to numbers based on the prefix:
0b - binary
0 - octal
0x - hex
See hex and/or oct.
#!/usr/bin/perl
use strict;
use warnings;
my #months = map hex, qw/x01 x02 x03 x04 x05 x06 x07 x08 x09 x0a x0b x0c/;
print "$_\n" for #months;
If I understand correctly you have 1 byte per month - not string "0x10", but rather byte with 10 in it.
In this way, you should use unpack:
my $in = "\x0a";
print length($in), "\n";
my ($out) = unpack("c", $in);
print length($out), "\n", $out, "\n"
output:
1
2
10
If the input are 3 characters, like "x05", then changing is also quite simple:
my $in = "x0a";
my $out = hex($in);
Here's another way that may be more practical for directly converting the hexadecimals contained in a string.
This make use of the /e (e for eval) regex modifier on s///.
Starting from this string:
$hello_world = "\\x48\\x65\\x6c\\x6c\\x6f\\x20\\x57\\x6f\\x72\\x6c\\x64";
Hexadecimals to characters :
print $hello_world =~ s/\\x([0-9a-fA-F]{2})/chr hex $1/gre;
Hexadecimals to decimal numbers :
print $hello_world =~ s/\\x([0-9a-fA-F]{2})/hex $1/gre;
Drop the /r modifier to substitute the string in-place.
One day I used a python script that did stuff with a binary file and I was stuck with a bytes literal (b'\x09\xff...') containing only hexadecimal digits.
I managed to get back my bytes with a one-liner that was a variant of the above.