How can I get the number of pack`ed items in Perl without actually unpacking? - perl

I have a string of packed values which was created sequentially using something like:
while (...) {
...
$packed .= pack( 'L', $val );
}
In another program, after I load $packed, I wish to find out how many values were actually packed. I know how to do that after unpacking:
my #vals = unpack( 'L*', $packed );
print scalar(#vals);
But is it really necessary? If I only care about the number of values, can I do better and skip the unpacking?

Since you know the size of a packed value (L is an unsigned 32-bit int, or 4 bytes), just divide the length by the size:
my $count = length($packed) / 4;
If you don't want to hard code the size, you could also pack a sample value to calculate it. (Note that Perl's compile-time constant folding doesn't work with pack, at least not with 5.10.1, so you'd want to do that calculation only once.)
my $size = length(pack('L', 0));
...
my $count = length($packed) / $size;

Since L is merely groups of 32 bit values, you can simply count the number of bytes and divide by 4.

use constant LLength => length(pack("L", 0));
...
print length($packed)/LLength;
Check if LLength is really constant:
$ perl -MO=Deparse,-d -e'use constant L => length(pack("L", 0));print L, "\n";'
sub L () { 4 }
use constant ("L", length pack("L", 0));
print 4, "\n";
-e syntax OK

Related

generate random binary number in perl

I want to generate 64 iteration of non-repetitive 6 digits that only consist of 0 and 1 (eg. 111111, 101111, 000000) by using perl.
I found code that can generate random hex and try to modify it but I think my code is all wrong. This is my code:
use strict;
use warnings;
my %a;
foreach (1 .. 64) {
my $r;
do {
$r = int(rand(2));
}
until (!exists($a{$r}));
printf "%06d\n", $r;
$a{$r}++;
}
Do you mean that you want 64 six-bit numbers, all distinct from each other? If so, then you should just shuffle the list (0, 1, 2, 3, …, 63), because there are exactly 64 six-bit numbers — you just want them in a random order.
And if you want to print them as base-two string, use the %06b format.
use List::Util;
my #list = List::Util::shuffle 0..63;
printf "%06b\n", $_ for #list;
From the comments:
I am actually want to generate all possible 6-bit binary number. Since writing all the possible combination by hand is cumbersome and prone to human error, I think it will be good idea to just generate it by using rand() with no repetition and store it into array.
This is a horribly inefficent approach to take, thanks to random number collisons.
You get the same result with:
printf ( "%06b\n", $_ ) for 1..63;
If you're after a random order (although, you don't seem to suggest that you do):
use List::Util qw ( shuffle );
printf ( "%06b\n", $_ ) for shuffle (0..63);
If you want 64 x 6-bit integers you can call int(rand(64)); 64 times, there's no need to generate each bit separately.
Your code can be modified to work like this:
#!/usr/bin/perl
# your code goes here
use strict;
use warnings;
my %a;
foreach (1 .. 64) {
my $r;
do
{
$r = int(rand(64));
} until (!exists($a{$r}));
printf "%06b\n", $r;
$a{$r}++;
}
The results are stored in a array of integers. The %06b format specifier string prints out a 6 bit binary number.

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

Converting strings to floats

could soemone help me with the following condition, please?
I'm trying to compare $price and $lsec.
if( (sprintf("%.2f", ($price*100+0.5)/100)*1 != $lsec*1) )
{
print Dumper($price,$lsec)
}
Sometimes the dumper prints same numbers(as strings) and jumps in.
Thought, that multiplying with 1 makes floats from them...
Here dumper output:
$VAR1 = '8.5';
$VAR2 = '8.5';
What am I doing wrong?
Thank you,
Greetings and happy easter.
There is a difference between what is stored in a Perl variable and how it is used. You are correct that multiplying by 1 forces a variable to be used as a number. It also causes the number to be stored in the SV data structure that represents the variable to the interpreter. You can use the Devel::Peek module to see what Perl has stored in each variable:
use Devel::Peek;
my $num = "8.5";
Dump $num;
outputs:
SV = PV(0xa0a46d8) at 0xa0c3f08
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0xa0be8c8 "8.5"\0
CUR = 3
LEN = 4
continuing...
my $newnum = $num * 1;
Dump $num;
Dump $newnum;
outputs:
SV = PVNV(0xa0a46d8) at 0xa0c3f08
REFCNT = 1
FLAGS = (PADMY,NOK,POK,pIOK,pNOK,pPOK)
IV = 8
NV = 8.5
PV = 0xa0be8c8 "8.5"\0
CUR = 3
LEN = 4
SV = NV(0x9523660) at 0x950df20
REFCNT = 1
FLAGS = (PADMY,NOK,pNOK)
NV = 8.5
The attributes we are concerned with are PV (string pointer), NV (floating-point number), and IV (integer). Initially, $num only has the string value, but using it as a number (e.g. in multiplication) causes it to store the numeric values. However, $num still "remembers" that it is a string, which is why Data::Dumper treats it like one.
For most purposes, there is no need to explicitly force the use of a string as a number, since operators and functions can use them in the most appropriate form. The == and != operators, for example, coerce their operands into numeric form to do numeric comparison. Using eq or ne instead forces a string comparison. This is one more reason to always use warnings in your Perl scripts, since trying to compare a non-numeric string with == will garner this warning:
Argument "asdf" isn't numeric in numeric eq (==) at -e line 1.
You are correct to say that multiplying a string by 1 will force it to be evaluated as a number, but the numeric != comparator will do the same thing. This is presumably a technique you have acquired from other languages as Perl will generally do the right thing and there is no need to force a cast of either operand.
Lets take a look at the values you're comparing:
use strict;
use warnings;
use Data::Dumper;
my $price = '8.5';
my $lsec = '8.5';
my $rounded_price = sprintf("%.2f", ($price * 100 + 0.5) / 100);
print "$rounded_price <=> $lsec\n";
if ( $rounded_price != $lsec ) {
print Dumper($price,$lsec);
}
output
8.51 <=> 8.5
$VAR1 = '8.5';
$VAR2 = '8.5';
So Perl is correctly saying that 8.51 is unequal to 8.5.
I suspect that your
($price * 100 + 0.5) / 100
is intended to round $price to two decimal places, but all it does in fact is to increase $price by 0.005. I think you meant to write
int($price * 100 + 0.5) / 100
but you also put the value through sprintf which is another way to do the same thing.
Either
$price = int($price * 100 + 0.5) / 100
or
$price = sprintf ".2f", $price
but both is overkill!
This part:
($price*100+0.5)/100)
If you put in 8.5, you get back 8.505. Which naturally is not equal to 8.5. Since you do not change $price, you do not notice any difference.
Perl handles conversion automatically, so you do not need to worry about that.
my $x = "8.5";
my $y = 8.5;
print "Equal" if $x == $y; # Succeeds
The nature of the comparison, == or in your case != converts the arguments to numeric, whether they are numeric or not.
You're doing nothing wrong. Perl converts it to a string before dumping it. For comparisons, use == and != for numeric comparisons and eq and ne for a string comparisons. Perl converts to strings and numbers as needed.
Example:
$ perl -MData::Dumper -e "my $a=3.1415; print Dumper($a);"
$VAR1 = '3.1415';

How to convert hex to string of hex

I have a problem understanding and using the 'vec' keyword.
I am reading a logpacket in which values are stored in little endian hexadecimal. In my code, I have to unpack the different bytes into scalars using the unpack keyword.
Here's an example of my problem:
my #hexData1 = qw(50 65);
my $data = pack ('C*', #hexData1);
my $x = unpack("H4",$data); # At which point the hexadecimal number became a number
print $x."\n";
#my $foo = sprintf("%x", $foo);
print "$_-> " . vec("\x65\x50", $_, 1) . ", " for (0..15); # This works.
print "\n";
But I want to use the above statement in the way below. I don't want to send a string of hexadecimal in quotes. I want to use the scalar array of hex $x. But it won't work. How do I convert my $x to a hexadecimal string. This is my requirement.
print "$_-> " . vec($x, $_, 1).", " for (0..15); # This doesn't work.
print "\n";
My final objective is to read the third bit from the right of the two byte hexadecimal number.
How do I use the 'vec' command for that?
You are making the mistake of unpacking $data into $x before using it in a call to vec. vec expects a string, so if you supply a number it will be converted to a string before being used. Here's your code
my #hexData1 = qw(50 65);
my $data= pack ('C*', #hexData1);
The C pack format uses each value in the source list as a character code. It is the same as calling chr on each value and concatenating them. Unfortunately your values look like decimal, so you are getting chr(50).chr(65) or "2A". Since your values are little-endian, what you want is chr(0x65).chr(0x50) or "\x65\x50", so you must write
my $data= pack ('(H2)*', reverse #hexData1);
which reverses the list of data (to account for it being little-endian) and packs it as if it was a list of two-digit hex strings (which, fortunately, it is).
Now you have done enough. As I say, vec expects a string so you can write
print join ' ', map vec($data, $_, 1), 0 .. 15;
print "\n";
and it will show you the bits you expect. To extract the the 3rd bit from the right (assuming you mean bit 13, where the last bit is bit 15) you want
print vec $data, 13, 1;
First, get the number the bytes represent.
If you start with "\x50\x65",
my $num = unpack('v', "\x50\x65");
If you start with "5065",
my $num = unpack('v', pack('H*', "5065"));
If you start with "50","65",
my $num = unpack('v', pack('H*', join('', "50","65"));
Then, extract the bit you want.
If you want bit 10,
my $bit = ($num >> 10) & 1;
If you want bit 2,
my $bit = ($num >> 2) & 1;
(I'm listing a few possibilities because it's not clear to me what you want.)

A quick string checksum function in Perl generating values in the 0..2^32-1 range

I'm looking for a Perl string checksum function with the following properties:
Input: Unicode string of undefined length ($string)
Output: Unsigned integer ($hash), for which 0 <= $hash <= 2^32-1 holds (0 to 4294967295, matching the size of a 4-byte MySQL unsigned int)
Pseudo-code:
sub checksum {
my $string = shift;
my $hash;
... checksum logic goes here ...
die unless ($hash >= 0);
die unless ($hash <= 4_294_967_295);
return $hash;
}
Ideally the checksum function should be quick to run and should generate values somewhat uniformly in the target space (0 .. 2^32-1) to avoid collisions. In this application random collisions are totally non-fatal, but obviously I want to avoid them to the extent that it is possible.
Given these requirements, what is the best way to solve this?
Any hash function will be sufficient - simply truncate it to 4-bytes and convert to a number. Good hash functions have a random distribution, and this distribution will be constant no matter where you truncate the string.
I suggest Digest::MD5 because it is the fastest hash implementation that comes with Perl as standard. String::CRC, as Pim mentions, is also implemented in C and should be faster.
Here's how to calculate the hash and convert it to an integer:
use Digest::MD5 qw(md5);
my $str = substr( md5("String-to-hash"), 0, 4 );
print unpack('L', $str); # Convert to 4-byte integer (long)
From perldoc -f unpack:
For example, the following computes the same number as the
System V sum program:
$checksum = do {
local $/; # slurp!
unpack("%32W*",<>) % 65535;
};
Don't know how quick it is, but you might try String::CRC.