Lucas Rey has asked for the wisdom of the Perl Monks concerning the following question:

Dear community, I'm just coding a little diameter avp converion and I need to pad the AVP string. According to RFC 3588, AVP string that do not align on a 32-bit boundary MUST have the necessary padding 00.

examples (space between values is only to shown better and should be removed on final string):
e4 99 68 f8 41 ==> e4 99 68 f8 41 00 00 00 40 ==> 40 00 00 00 2a 2e ==> 2a 2e 00 00

My simple code just takes a string and convert it in hex, but I need to add 00 padding on the right basing on above rules/example:
$Origin_Host_CER="example.me"; $Origin_Host_CER =~ s/(.)/sprintf '%02x', ord $1/seg; print "STX2HEX: $Origin_Host_CER\n\n"; print length($Origin_Host_CER)/2;

This will result in: STX2HEX: 6578616d706c652e6d65
10
So, final string should have 2 padding: 6578616d706c652e6d650000

Could someone help me to understand how to do that possibility without use any external module?
Thank you
Lucas

Replies are listed 'Best First'.
Re: Align string on a 32-bit boundary with padding
by johngg (Canon) on Aug 18, 2022 at 09:51 UTC

    Use pack with the Z template.

    Update: An example.

    johngg@aleatico:~$ perl -Mstrict -Mwarnings -E ' my $str = q{example.me}; my $length = length $str; my $padTo = 4; my $rem = $length % $padTo; my $padLen = $length + ( $rem ? $padTo - $rem : 0 ); print pack qq{Z$padLen}, $str;' | hexdump -C 00000000 65 78 61 6d 70 6c 65 2e 6d 65 00 00 |example.m +e..| 0000000c

    Update 2: Don't use this, if the string is already aligned to a 32-bit boundary the Z template will overwrite the last character with a NULL is it seems to always produce a C-style NULL-terminated string.

    Update 3: Removed the strike-through! The solution is to only pad with pack when necessary but it does begin to look messy.

    johngg@aleatico:~$ perl -Mstrict -Mwarnings -E ' my $str = q{myexample.me}; my $length = length $str; my $padTo = 4; my $rem = $length % $padTo; print $rem ? pack( qq{Z@{ [ $length + $padTo - $rem ] }}, $str ) : $str;' | hexdump -C 00000000 6d 79 65 78 61 6d 70 6c 65 2e 6d 65 |myexample +.me| 0000000c

    Cheers,

    JohnGG

      I took your suggestion as a challenge. Here is the best I could do. It probably is not what you had in mind.
      use strict; use warnings; use Test::More tests=>1; my $Origin_Host_CER="example.me"; my $required = "6578616d706c652e6d650000"; my $length = 4*int((length($Origin_Host_CER)+3)/4); my $result = unpack('H*', pack("Z$length", $Origin_Host_CER)); is($result, $required, 'AVP String');
      Bill
      After studying your updates, private messages, and documentation for 'pack', I was able to fix my previous code by packing one extra (arbitrary) character, and not unpacking it. However, I noticed that the 'pack' was overkill. I could concatenate three extra nulls to the end of the original ASCII string and unpack as much as necessary. The length ($l) has to be twice what it was before because now I am unpacking two hex characters for every one ASCII character that I had been packing.
      use strict; use warnings; use Test::More tests=>4; my @examples = ( [qw( example. 6578616d706c652e )], [qw( example.1 6578616d706c652e31000000)], [qw( example.12 6578616d706c652e31320000)], [qw( example.123 6578616d706c652e31323300)], ); foreach my $example(@examples) { my ($Origin_Host_CER, $required) = @$example; my $l = 8*int((length($Origin_Host_CER)+3)/4); my $result = unpack("H$l", $Origin_Host_CER . "\x00" x 3); is($result, $required, $Origin_Host_CER); }

      RESULTS

      1..4 ok 1 - example. ok 2 - example.1 ok 3 - example.12 ok 4 - example.123
      Bill
      There's really no need for pack when you can just add  ("\0" x $n) to the original string.
        ... add ("\0" x $n) to the original string.

        Shouldn't that be ("\0" x ($n-1)) if $n is the boundary width in bytes?

        Win8 Strawberry 5.8.9.5 (32) Sat 08/20/2022 15:46:20 C:\@Work\Perl\monks >perl use strict; use warnings; use bytes; use Data::Dump qw(pp); use constant BYTE_BOUND => 4; use constant PAD => "\0" x (BYTE_BOUND-1); my $s = 'TheRainInSpainFalls'; while (length $s) { my $p = $s . PAD; $p = bytes::substr $p, 0, bytes::length($p) - bytes::length($p)%BY +TE_BOUND; printf "%4s - %-28s \n", bytes::length($p), pp($p); chop $s; } ^Z 20 - "TheRainInSpainFalls\0" 20 - "TheRainInSpainFall\0\0" 20 - "TheRainInSpainFal\0\0\0" 16 - "TheRainInSpainFa" 16 - "TheRainInSpainF\0" 16 - "TheRainInSpain\0\0" 16 - "TheRainInSpai\0\0\0" 12 - "TheRainInSpa" 12 - "TheRainInSp\0" 12 - "TheRainInS\0\0" 12 - "TheRainIn\0\0\0" 8 - "TheRainI" 8 - "TheRain\0" 8 - "TheRai\0\0" 8 - "TheRa\0\0\0" 4 - "TheR" 4 - "The\0" 4 - "Th\0\0" 4 - "T\0\0\0"


        Give a man a fish:  <%-{-{-{-<

Re: Align string on a 32-bit boundary with padding
by tybalt89 (Monsignor) on Aug 18, 2022 at 14:26 UTC
    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11146218 use warnings; my $want = 8; for ( 1 .. 24 ) { my $string = 'f' x $_; my $padded = $string . 0 x ( - length($string) % $want ); printf "%25s => %s\n", $string, $padded; }

    Outputs:

    f => f0000000 ff => ff000000 fff => fff00000 ffff => ffff0000 fffff => fffff000 ffffff => ffffff00 fffffff => fffffff0 ffffffff => ffffffff fffffffff => fffffffff0000000 ffffffffff => ffffffffff000000 fffffffffff => fffffffffff00000 ffffffffffff => ffffffffffff0000 fffffffffffff => fffffffffffff000 ffffffffffffff => ffffffffffffff00 fffffffffffffff => fffffffffffffff0 ffffffffffffffff => ffffffffffffffff fffffffffffffffff => fffffffffffffffff0000000 ffffffffffffffffff => ffffffffffffffffff000000 fffffffffffffffffff => fffffffffffffffffff00000 ffffffffffffffffffff => ffffffffffffffffffff0000 fffffffffffffffffffff => fffffffffffffffffffff000 ffffffffffffffffffffff => ffffffffffffffffffffff00 fffffffffffffffffffffff => fffffffffffffffffffffff0 ffffffffffffffffffffffff => ffffffffffffffffffffffff
      Upvoted at least as much for the beautiful f-ing Christmas tree as for the code :-)
Re: Align string on a 32-bit boundary with padding
by hippo (Archbishop) on Aug 18, 2022 at 10:16 UTC

    There are so many ways to do this. Here's one:

    use strict; use warnings; use Test::More tests => 1; my $have = '6578616d706c652e6d65'; my $want = '6578616d706c652e6d650000'; my $mult = 8; my $padchar = '0'; my $padlen = length ($have) % $mult; $have .= $padchar x ($mult - $padlen) if $padlen; is $have, $want;

    See also the FAQ.

    without use any external module?

    You mean like String::Pad for instance? Why not use the best and most extensive archive of modules on the net?


    🦛