Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

String eval is cool

by DrHyde (Prior)
on Jun 22, 2006 at 11:07 UTC ( #556884=CUFP: print w/replies, xml ) Need Help??

Recently on one of my mailing lists, someone wanted some code to translate CIDR-style descriptions into the more traditional network address and netmask pairs. Another list member posted some perl code which was basically 30 lines of C but with extra $ signs sprinkled throughout. My version was (stripped of data validity checking etc) this:
my ($addr, $bits, $network, $mask) = split("/", $cidr); $network = $network * 256 + $_ foreach(split(/\./, $addr); $mask = eval '0b'.('1' x $bits).('0' x (32 - $bits)); print int2ip($network & $mask)."\t".int2ip($mask)."\n";

(the int2ip function was the same as that the original poster had used, so I don't bother reproducing it here)

The string eval - abhorred by many - meant I could avoid several lines of fairly opaque bit-banging and I think it's *very* clear what that line does, and it also makes it obvious how netmasks are related to the /bits in CIDR notation.

Replies are listed 'Best First'.
Re: String eval is cool
by salva (Abbot) on Jun 22, 2006 at 12:39 UTC
    or without eval...
    $mask = 0xffffffff - 2**(32 - $bits) + 1;
Re: String eval is cool
by ikegami (Pope) on Jun 22, 2006 at 14:06 UTC

    No need for eval to convert from binary:

    sub bin { return unpack('N', pack('B32', substr('0'x32 . $_[0], -32))); } $mask = bin('1'x$bits . '0'x(32-$bits));

    No need for eval to create an IP mask:

    $mask = 0xFFFFFFFF << (32-$bits);
      IMO the eval version is much clearer than anything involving pack/unpack could be.
      $ perldoc -f eval|wc -c 6610 $ perldoc -f pack|wc -c 23377
      That's right, pack's documentation is over three and a half times longer, and is littered with platform dependencies, arbitrary one character magic values, and special cases. It's so complicated that it needs a 51,000 character tutorial to go with it.

      And your use of << is buggy. On a 32 bit machine the results are undefined. On a 64-bit machine you lose the obvious relationship between the /bits and the netmask (you'll have too many 1 bits) although the results will still be correct as the extra 1s will be ANDed with 0 and so thrown away later.

        IMO the eval version is much clearer than anything involving pack/unpack could be.

        True, but that's why it was isolated into a small function with a clear purpose. Modularisation introduces ease of testing, which allows for hiding of complicated code. Have you looked at the guts of the modules you use? You'll find similar complexity.

        Speaking of, this should be done with a module such as NetAddr::IP.

        On a 32 bit machine the results are undefined.

        Oops. I'm used to doing these operations in assembler where portability is a non-issue, so I changed my solution while posting. My original solution (follows) doesn't suffer from that problem, and also works on 64-bit perls unmodified:

        $mask = 0xFFFFFFFF >> (32-$bits) << (32-$bits);
Re: String eval is cool
by jwkrahn (Monsignor) on Jun 23, 2006 at 07:12 UTC
    $mask = eval '0b' . ( 1 x $bits ) . ( 0 x ( 32 - $bits ) );
    Or you could have used the function that perl provides for this purpose:
    $mask = oct '0b' . ( 1 x $bits ) . ( 0 x ( 32 - $bits ) );
      Wow, what a misnomer! I didn't know oct did that. I've been using my own pack-unpack-based routine to convert from binary.
Re: String eval is cool
by shmem (Chancellor) on Jun 22, 2006 at 21:39 UTC
    Just in case you want to save some more bytes ;-)


    #!/usr/bin/perl $m=pack B32,pop=~'/'x$';printf"$` network %vd broadcast %vd netmask %v +d\n",($z=eval$`)&$m,$z|~$m,$m


    _($_=" "x(1<<5)."?\n".q/)Oo.  G\        /
                                  /\_/(q    /
    ----------------------------  \__(m.====.(_("always off the crowd"))."
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: String eval is cool
by ikegami (Pope) on Jun 22, 2006 at 21:53 UTC

    $network = $network * 256 + $_ foreach(split(/\./, $addr);
    can be replaced with
    $network = unpack('N', pack('C4', split(/\./, $addr)));
    Better yet, use
    use Socket qw( inet_aton );
    $network = unpack('N', inet_aton($addr));

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: CUFP [id://556884]
Approved by teamster_jr
Front-paged by grinder
and monks are getting baked in the sun...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (8)
As of 2017-10-23 04:47 GMT
Find Nodes?
    Voting Booth?
    My fridge is mostly full of:

    Results (276 votes). Check out past polls.