Unpack and checksums

F

Fatted

I'm trying to get unpack to generate a checksum but the result is always 0.
E.G.

my $string = 'ThisIsAString';

my $chksum = unpack("%H*",$string);

print $chksum."\n";


Results in 0.

perlfunc says unpack will generate the checksum (default 16 bit) if the
template is preceded by %.
What am I missing?
 
A

Anno Siegel

Fatted said:
I'm trying to get unpack to generate a checksum but the result is always 0.
E.G.

my $string = 'ThisIsAString';

my $chksum = unpack("%H*",$string);

print $chksum."\n";


Results in 0.

perlfunc says unpack will generate the checksum (default 16 bit) if the
template is preceded by %.
What am I missing?

There seems to be a bug in unpack, which appears to return a spurious
0 with the "%H" format. Try

perl -le 'print join( " | ", unpack "%H*", "a")'

to see it. As a workaround, use

my ( $chksum) = unpack("%H*",$string);

Anno
 
F

fifo

There seems to be a bug in unpack, which appears to return a spurious
0 with the "%H" format. Try

perl -le 'print join( " | ", unpack "%H*", "a")'

to see it. As a workaround, use

my ( $chksum) = unpack("%H*",$string);

Isn't this just the same as doing

my $chksum = unpack("H*",$string);

as unpack "H*" is only going to give you a single scalar anyway?
 
A

Anno Siegel

fifo said:
Isn't this just the same as doing

my $chksum = unpack("H*",$string);

as unpack "H*" is only going to give you a single scalar anyway?

We're not talking "H*" but "%H*". There's a *bug*, and it doesn't
behave according to specification.

Please read for comprehension.

Anno
 
F

Fatted

Anno said:
We're not talking "H*" but "%H*". There's a *bug*, and it doesn't
behave according to specification.

Please read for comprehension.

Anno

I don't think I follow either :(

take:

my $string = 'ThisIsAString';
# PERFORM PLAIN UNPACK
my $chksum = unpack("H*",$string)."\n";
print $chksum."\n";

gives:
54686973497341537472696e67

try:

my $string = 'ThisIsAString';
# PERFORM CHECKSUM UNPACK
my $chksum = unpack("%H*",$string);
print $chksum."\n";

gives:
0
as discussed

try suggested workaround:

my $string = 'ThisIsAString';
# PERFORM WORKAROUND CHECKSUM UNPACK
my ($chksum) = unpack("%H*",$string);
print $chksum."\n";

gives:
54686973497341537472696e67
which is the same result as the plain unpack and not the checksum sought.
 
F

fifo

We're not talking "H*" but "%H*". There's a *bug*, and it doesn't
behave according to specification.

Please read for comprehension.

I agree that there's a bug with "%H*". The point I was trying to make
was that "%H*" doesn't do anything useful over "H*" anyway, since it's
just 'summing' a single value. I was thinking maybe the OP really meant
to use "%C*"?
 
A

Anno Siegel

Fatted said:
I don't think I follow either :(

take:

my $string = 'ThisIsAString';
# PERFORM PLAIN UNPACK
my $chksum = unpack("H*",$string)."\n";
print $chksum."\n";

gives:
54686973497341537472696e67

try:

my $string = 'ThisIsAString';
# PERFORM CHECKSUM UNPACK
my $chksum = unpack("%H*",$string);
print $chksum."\n";

gives:
0
as discussed

try suggested workaround:

my $string = 'ThisIsAString';
# PERFORM WORKAROUND CHECKSUM UNPACK
my ($chksum) = unpack("%H*",$string);
print $chksum."\n";

gives:
54686973497341537472696e67
which is the same result as the plain unpack and not the checksum sought.

Ugh, you're right. I didn't notice it doesn't do the checksum at all.
Forget the workaround, sorry.

Anno
 
F

Fatted

fifo said:
I agree that there's a bug with "%H*". The point I was trying to make
was that "%H*" doesn't do anything useful over "H*" anyway, since it's
just 'summing' a single value. I was thinking maybe the OP really meant
to use "%C*"?

What I was looking for was the unpack equivalent of:
####
my $string = 'ThisIsAString';
my $chksum = 0;

for( split(//,$string) )
{
$chksum += ord;
}

print sprintf("%X",$chksum)."\n";
####

I thought "%H*" would handle that (if "H*" gives a concat of the hex
values then "%H*" should give sum, right?), but then comparing "H*" with
"C*":

"H*" returns the concatentation of the hex values of the string
whereas
"C*" returns the decimal value of the first value.

On the other hand "%C*" *does* return the checksum (in decimal).

I've read and reread the perlfunc for pack and unpack and I'm now pretty
confused.
 
F

fifo

What I was looking for was the unpack equivalent of:
####
my $string = 'ThisIsAString';
my $chksum = 0;

for( split(//,$string) )
{
$chksum += ord;
}

print sprintf("%X",$chksum)."\n";
####

I thought "%H*" would handle that (if "H*" gives a concat of the hex
values then "%H*" should give sum, right?), but then comparing "H*" with
"C*":

"H*" returns the concatentation of the hex values of the string
whereas

It returns a single string of hexadecimal digits.
"C*" returns the decimal value of the first value.

No it returns a _list_ of the char values of each byte in the string:

$ perl -le'print join ":", unpack "C*", "ThisIsAString"'
84:104:105:115:73:115:65:83:116:114:105:110:103
 
G

Greg Bacon

: What I was looking for was the unpack equivalent of:
: ####
: my $string = 'ThisIsAString';
: my $chksum = 0;
:
: for( split(//,$string) )
: {
: $chksum += ord;
: }
:
: print sprintf("%X",$chksum)."\n";
: ####

You're right there! Well, sort of. See below.

$ cat try
#!/usr/local/bin/perl -w

use strict;
use warnings;

sub explicit_checksum {
my $str = shift;

my $sum = 0;
$sum += ord for split // => $str;

$sum;
}

sub unpack_checksum {
unpack "%16C*", shift;
}

my $string = 'ThisIsAString';
my $chksum = 0;

for (split //, $string) {
$chksum += ord;
}

my @method = (
[ explicit => \&explicit_checksum ],
[ unpack => \&unpack_checksum ],
);

for (@method) {
my($name,$code) = @$_;

printf "%-10s %X\n", $name . ":", $code->($string);
}

$ ./try
explicit: 50C
unpack: 50C

These results match coincidentally. Given a long enough $string, i.e.,
one whose ASCII values sum greater than 0xFFFF, only the least
significant sixteen bits would match.

For example:

$ cat try
[...]
open my $fh, '/etc/lynx.cfg' or die "$0: open /etc/lynx.cfg: $!";
my $string = join '', <$fh>;
[...]

$ ./try
explicit: ADA3B4
unpack: A3B4

The unpack isn't equivalent, but that's because the checksum your code
computes doesn't follow the convention of producing a result clamped
to a certain number of bits, e.g., $sum & 0xFFFF for a 16-bit checksum.

: I thought "%H*" would handle that (if "H*" gives a concat of the hex
: values then "%H*" should give sum, right?), but then comparing "H*"
: with "C*":

Remember that H returns hex strings, but C returns unsigned char
values. Which of these matches the values returned from Perl's ord
operator?

: On the other hand "%C*" *does* return the checksum (in decimal).

No, it doesn't return a value in a particular base, just a number.
Base is a representation, e.g., input and output, issue. Yes, I
realize it's almost certainly a twos-complement bitpattern in some
register in your machine, but we're speaking at the level of
abstraction of the Perl programming language, not the bare metal.

: I've read and reread the perlfunc for pack and unpack and I'm now
: pretty confused.

I hope this helps you through the confusion.

Greg
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,528
Members
45,000
Latest member
MurrayKeync

Latest Threads

Top