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

Good evening,
I try to figure out which VLANs are allowed on a Cisco Switch port configured as Trunk.
I read via SNMP the Cisco_VTP_MIB which returns me this information in the form :
Index : blabla, blabla beeing a 1024 bits field.
Example (obtained with a MIB browser)
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
16 Bytes/ Line x 8 Lines = 128 Bytes x 8 bits or Vlans = 1024 Vlans
16 Bytes / Line => 1st line covers Vlan Indexes from 0 to 127
the O2 is 134th bit =>Vlan 134,
the 10 is 810th bit => Vlan 827
The port in question is condigured as
switchport trunk allowed vlan 134,827
I was hoping that a loop with the shift left (<<) would allow me to find out which bit are set to one (like in assembler, but there is no "Carry" bit in which the shifted out bit can go).
Any idea out to find out the ones - and where they are - in a 1024 bits long binary-field.
Thanks
Juju

Replies are listed 'Best First'.
Re: Working with BIG binary fields
by BrowserUk (Patriarch) on Sep 20, 2010 at 17:23 UTC

    I don't understand the format of the data you've posted. But, assuming that by "binary", you really do mean binary, and not some ascii-encoded hex representation of the original binary, then the solution to your problem is vec.

    my $binaryString = ...; if( vec( $binaryString, 123, 1 ) == 1 ) { ## bit 123 was set } else { ## it wasn't }

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Working with BIG binary fields
by jethro (Monsignor) on Sep 20, 2010 at 17:59 UTC

    if you need a really fast way, you might create an array with 256 entries, in which every possible entry aka byte points to an array of the set bits in it:

    my @SetBits= ( [], [0], [1], [0,1], [2], [0,2], [1,2], [3], [0,3] ... ... sub findsetbits { #feed with array of bytes my @result; my $offset=0; foreach my $byte (@_) { foreach (@{$SetBits[$byte]}) { push @result, $_+$offset; } $offset+= 8; } return @result; }

    (Untested)

      Here's an automated way to create the lookup table. Bit-counting function assumes data passed as raw binary string of any length, but test statements show a way to convert from hex-digit text string if it isn't.

      >perl -wMstrict -le "my @setbits; for my $byte (0 .. 255) { my $bits = unpack 'B*', chr $byte; my @offsets; push @offsets, $-[1] while $bits =~ m{(1)}xmsg; push @setbits, [ @offsets ]; } sub find_set_bits { my ($raw_binary) = @_; my @offsets; my $offset = 0; for my $byte (unpack 'C*', $raw_binary) { push @offsets, map $_ + $offset, @{ $setbits[$byte] }; $offset += 8; } return @offsets; } my $str = qq{ 01 02 03 \n 04 00 E0 0a 00 00 00 00 00 00 00 00 00 \n}; (my $hexdigits = $str) =~ s{[^[:xdigit:]]}{}xmsg; my $raw = pack 'H*', $hexdigits; my @ones_offsets = find_set_bits($raw); print qq{'$str'}; print qq{no 1-bits found} unless @ones_offsets; print qq{1-bit found at offset $_} for @ones_offsets; " ' 01 02 03 04 00 E0 0a 00 00 00 00 00 00 00 00 00 ' 1-bit found at offset 7 1-bit found at offset 14 1-bit found at offset 22 1-bit found at offset 23 1-bit found at offset 29 1-bit found at offset 40 1-bit found at offset 41 1-bit found at offset 42 1-bit found at offset 52 1-bit found at offset 54
Re: Working with BIG binary fields
by AnomalousMonk (Archbishop) on Sep 20, 2010 at 23:09 UTC

    Here's another approach, based on my guess that you're processing 'text' strings (of any length: 16 hex bytes used for example) consisting of any combination of hex digits and whitespace. (Assumes: '01' == 0b00000001 and '0304' == 0b0000001100000100 and etc.)

    >perl -wMstrict -le "my $str = qq{ 01 02 03 \n 04 00 E0 0a 00 00 00 00 00 00 00 00 00 \n}; (my $bits = $str) =~ s{[^[:xdigit:]]}{}xmsg; $bits = unpack 'B*', pack 'H*', $bits; my @ones_offsets; push @ones_offsets, $-[1] while $bits =~ m{(1)}xmsg; print qq{'$str'}; print qq{'$bits'}; print qq{no one bits found} unless @ones_offsets; print qq{1 bit at offset $_} for @ones_offsets; " ' 01 02 03 04 00 E0 0a 00 00 00 00 00 00 00 00 00 ' '000000010000001000000011000001000000000011100000000010100000000000000 +00000000000000000000000000000000000000000000000000000000000' 1 bit at offset 7 1 bit at offset 14 1 bit at offset 22 1 bit at offset 23 1 bit at offset 29 1 bit at offset 40 1 bit at offset 41 1 bit at offset 42 1 bit at offset 52 1 bit at offset 54

    If BrowserUk's guess is right and you already have the data as raw binary string (say, in $bits), just drop the
        $bits = unpack 'B*', pack 'H*', $bits;
    statement and all before it, and change that statement to
        $bits = unpack 'B*', $bits;
    instead (tested).

      GREAT !!!
      Thanks you very much, I work with raw binary data and your code -modified according your last remark- returns me exactly what I wanted.

      This is "Grand Art"

      I am writing PERL scripts (big ones) since years in order to help me in my daily work as Head of NOC. I spent 4 hours this morning to NOT understand your code (the 1st one), I gave up I tryed the second one with immediate succes (in results, not in understanding) .
      I thought I "can" write PERL, now I am really down and full of doubts.

      Thanks a lot

      Juju