The following builds a binary tree, then collapses it into the format I think you want.
use strict;
use warnings;
# -----
{
package SubnetTreeBuilder;
use Socket qw( inet_aton );
sub new {
my ($class) = @_;
my $btree;
return bless(\$btree, $class);
}
sub add {
my ($self, $addr_first, $size, $data) = @_;
$data = "$addr_first/$size" if !defined($data);
my $pkd_first = inet_aton($addr_first);
my $vec_first = pack 'L', unpack 'N', $pkd_first;
my $p = $self;
$p = \($$p->[ vec($vec_first, 31-$_, 1) ]) for 0 .. $size-1;
$$p->[2] = $data;
}
sub generate {
my ($self, $data) = @_;
$data = '0.0.0.0/32' if !defined($data);
local *_helper = sub {
my ($node) = @_;
my @children;
push @children, _helper($node->[0]) if defined $node->[0];
push @children, _helper($node->[1]) if defined $node->[1];
if (defined($node->[2])) {
return [ $node->[2], @children ];
} else {
return @children;
}
};
return [ $data, _helper($$self) ];
}
1;
}
# -----
sub visit {
my ($node, $cb, $depth) = @_;
$cb->($node->[0], $depth||0);
++$depth;
visit($node->[$_], $cb, $depth) for 1..$#$node;
}
my $builder = SubnetTreeBuilder->new();
$builder->add('64.0.0.0', 3); # 0,1,0
$builder->add('128.0.0.0', 2); # 1,0
$builder->add('128.0.0.0', 3); # 1,0,0
$builder->add('160.0.0.0', 3); # 1,0,1
# $tree contains a hierarchy of arrays.
# Each array represents a subnet.
# The first element of a subnet array is the name of the subnet.
# Subsequent elements of a subnet array are subnets of the subnet.
my $tree = $builder->generate();
visit($tree, sub {
my $subnet = $_[0];
my $indent = ' ' x ($_[1]*3);
print("$indent$subnet\n");
});
0.0.0.0/32
64.0.0.0/3
128.0.0.0/2
128.0.0.0/3
160.0.0.0/3
By the way, it will find the first address of the subnet without even trying if you supply an address that's not the first address of the subnet.
Update: Bug fix in the return value.
|