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
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
| Method | Returns | Notes |
|---|
address | Str | Network address. Aliases: base, first |
netmask | Str | Subnet mask. Alias: mask |
hostmask | Str | Wildcard/inverse mask |
broadcast | Str | Broadcast address. Alias: last |
bits | Int | Prefix length (0–32) |
size | Int | Total addresses (including network + broadcast) |
cidr | Str | Canonical CIDR string ("192.168.1.0/24") |
desc | Str | Address/mask form ("192.168.1.0/255.255.255.0") |
range | List | Two-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
| Coercion | Result |
|---|
Str / ~$net | CIDR string ("192.168.1.0/24") |
Int / +$net | Network address as decimal integer |
Real / Num | Network 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.
- Network/mask/broadcast stored as native integers — no heap allocation in hot paths
- Broadcast cached at construction;
contains/match are two integer ops enumerate is lazy — O(1) memory regardless of network sizenth is O(1) direct computation- NQP primitives used throughout for string parsing and bit manipulation
License
Artistic-2.0