Rand Stats

Net::Netmask::Fast

zef:Zer0-Tolerance

Net::Netmask::Fast

Fast IPv4 address and netmask manipulation using native integer arithmetic.

API-compatible with Net::Netmask with additional performance from native integer storage, cached broadcast computation, and zero-allocation contains/match hot paths.

Installation

zef install Net::Netmask::Fast

Quick Start

use Net::Netmask::Fast;

my $net = Net::Netmask::Fast.new('192.168.1.0/24');

say $net.address;          # 192.168.1.0
say $net.broadcast;        # 192.168.1.255
say $net.netmask;          # 255.255.255.0
say $net.hostmask;         # 0.0.0.255
say $net.bits;             # 24
say $net.size;             # 256
say $net.cidr;             # 192.168.1.0/24
say $net.range.join(' - ');# 192.168.1.0 - 192.168.1.255

say $net.contains('192.168.1.42');   # True
say $net.match('192.168.1.42');      # 42  (index within network)
say $net.match('10.0.0.1');          # False

for $net.enumerate -> $ip { ... }              # lazy — all 256 strings
for $net.enumerate(:bit(26)) -> $subnet { ... }# four /26 sub-networks as strings
for $net.enumerate(:bit(26), :nets) -> $n { } # four /26 Net::Netmask::Fast objects

Input Formats

Single network

# CIDR notation
Net::Netmask::Fast.new('192.168.1.0/24')

# Dotted-decimal mask
Net::Netmask::Fast.new('192.168.1.0/255.255.255.0')

# Space-separated
Net::Netmask::Fast.new('192.168.1.0 255.255.255.0')

# Two positional arguments
Net::Netmask::Fast.new('192.168.1.0', '255.255.255.0')

# Named arguments
Net::Netmask::Fast.new(:address<192.168.1.0>, :netmask<255.255.255.0>)

Host bits are silently masked: new('192.168.1.42/24') gives the same result as new('192.168.1.0/24').

IP range → Seq of networks

Range forms always return a Seq of Net::Netmask::Fast objects — the minimum set of CIDR blocks covering the range exactly.

# Spaced dash
my @nets = Net::Netmask::Fast.new('192.168.1.0 - 192.168.1.255');
# [ 192.168.1.0/24 ]  (one block — range is CIDR-aligned)

# Compact dash
my @nets = Net::Netmask::Fast.new('10.0.0.0-10.255.255.255');
# [ 10.0.0.0/8 ]

# Named arguments
my @nets = Net::Netmask::Fast.new(:first<192.168.1.0>, :last<192.168.2.255>);
# [ 192.168.1.0/24, 192.168.2.0/24 ]

# Non-CIDR-aligned range — decomposed into minimum covering set
my @nets = Net::Netmask::Fast.new('192.168.1.1 - 192.168.1.255');
# [ 192.168.1.1/32, 192.168.1.2/31, 192.168.1.4/30, 192.168.1.8/29,
#   192.168.1.16/28, 192.168.1.32/27, 192.168.1.64/26, 192.168.1.128/25 ]

API Reference

Accessors

MethodReturnsNotes
addressStrNetwork address. Aliases: base, first
netmaskStrSubnet mask. Alias: mask
hostmaskStrWildcard/inverse mask
broadcastStrBroadcast address. Alias: last
bitsIntPrefix length (0–32)
sizeIntTotal addresses (including network + broadcast)
cidrStrCanonical CIDR string ("192.168.1.0/24")
descStrAddress/mask form ("192.168.1.0/255.255.255.0")
rangeListTwo-element list (first-ip, last-ip)

Membership

.match(Str $ip)

Returns the zero-based index of $ip within the network, or False if not a member.

my $net = Net::Netmask::Fast.new('10.0.0.0/8');
$net.match('10.0.0.1')    # 1
$net.match('10.0.1.0')    # 256
$net.match('192.168.0.1') # False

.contains(Str $ip --> Bool)

Returns True if $ip is within the network.

$net.contains('10.1.2.3')    # True
$net.contains('192.168.0.1') # False

Enumeration

.enumerate(Int :$bit = 32, Bool :$nets = False --> Seq)

Returns a lazy Seq of all addresses or sub-networks. Safe on large networks.

# All IP strings in a /30
.say for Net::Netmask::Fast.new('10.0.0.0/30').enumerate;
# 10.0.0.0  10.0.0.1  10.0.0.2  10.0.0.3

# Divide a /22 into /24 blocks (strings)
.say for Net::Netmask::Fast.new('10.0.0.0/22').enumerate(:bit(24));
# 10.0.0.0  10.0.1.0  10.0.2.0  10.0.3.0

# Same but return Net::Netmask::Fast objects
for Net::Netmask::Fast.new('10.0.0.0/22').enumerate(:bit(24), :nets) -> $subnet {
    say $subnet.cidr;
}

.nth(Int $n, Int :$bit = 32, Bool :$nets = False)

Direct O(1) access to the nth address or sub-network.

my $net = Net::Netmask::Fast.new('10.0.0.0/8');
$net.nth(0)                  # "10.0.0.0"
$net.nth(255)                # "10.0.0.255"
$net.nth(1, :bit(24))        # "10.0.1.0"
$net.nth(1, :bit(24), :nets) # Net::Netmask::Fast<10.0.1.0/24>

.range(--> List)

Returns (first-ip, last-ip) — useful for firewall rules or DHCP config.

my $net = Net::Netmask::Fast.new('192.168.1.0/24');
my ($start, $end) = $net.range;   # "192.168.1.0", "192.168.1.255"
$net.range.join(' - ')            # "192.168.1.0 - 192.168.1.255"

Navigation

my $net  = Net::Netmask::Fast.new('192.168.1.0/24');
$net.next   # Net::Netmask::Fast<192.168.2.0/24>  (alias: succ)
$net.prev   # Net::Netmask::Fast<192.168.0.0/24>  (alias: pred)

Sorting

# sortkey / sk — numeric key encoding network + prefix
my @sorted = @nets.sort(*.sortkey);

# Exported multi sub sort — typed dispatch for Net::Netmask::Fast arrays
my @nets = <10.0.0.0/8 192.168.2.0/24 192.168.1.0/24>.map: {
    Net::Netmask::Fast.new($_)
};
my @ordered = sort(@nets);  # 10.0.0.0/8, 192.168.1.0/24, 192.168.2.0/24

Type Coercions

CoercionResult
Str / ~$netCIDR string ("192.168.1.0/24")
Int / +$netNetwork address as decimal integer
Real / NumNetwork address as Num
gist"Net::Netmask::Fast<192.168.1.0/24>"

Exported Utility Subs

use Net::Netmask::Fast;

ip2dec('192.168.1.1')   # 3232235777
dec2ip(3232235777)      # "192.168.1.1"

Error Handling

Net::Netmask::Fast.new('bad');        # Invalid network spec: 'bad'
Net::Netmask::Fast.new('1.2.3.4/33') # Prefix length must be 0–32, got '33'

Range constructor errors:

Net::Netmask::Fast.new('10.0.0.5 - 10.0.0.1')  # Range end must be >= range start

Catch with try/CATCH as needed.

Performance

License

Artistic-2.0