Compare commits

...

2 Commits

3 changed files with 60 additions and 17 deletions

View File

@ -14,14 +14,15 @@
// along with netaddr. If not, see <https://www.gnu.org/licenses/>. // along with netaddr. If not, see <https://www.gnu.org/licenses/>.
/* /*
This file contains functions for operating with big endian ordered byte arrays. This file contains functions for operating with 128-bit unsigned integer
Using big.Integer is significantly slower than doing math strictly on 128-bit numbers represented as big endian ordered byte fixed size arrays.
numbers. At a minimum, you have to do expensive instantiation of big.Integer. Note that arrays always be 16 byte length and may contain leading zeros.
The functions below do not require copying arrays and allocate less memory.
Functions missing: Using V math.big is significantly slower than doing math strictly on
fn add_128(a [16]u8, b [16]u8) [16]u8 128-bit numbers. At a minimum, you have to do expensive instantiation of
fn diff_128(a [16]u8, b [16]u8) [16]u8 big.Integer.
The functions below do not requires copying and allocates less memory.
*/ */
module netaddr module netaddr
@ -29,6 +30,39 @@ module netaddr
import math.bits import math.bits
const max_128 = [16]u8{init: 0xff} const max_128 = [16]u8{init: 0xff}
const one_128 = [u8(0), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]!
@[direct_array_access; inline]
fn add_128(a [16]u8, b [16]u8) [16]u8 {
mut res := [16]u8{}
mut num := u16(0)
for i := 15; i >= 0; i-- {
num += u16(a[i])
num += u16(b[i])
res[i] += u8(num % 256)
num /= 256
}
if num > 0 {
panic('128 bit overflow detected')
}
return res
}
@[direct_array_access; inline]
fn sub_128(a [16]u8, b [16]u8) [16]u8 {
mut res := [16]u8{}
mut borrowed := u8(0)
for i := 15; i >= 0; i-- {
if a[i] < b[i] {
res[i] = (a[i] + 256) - borrowed - b[i]
borrowed = 1
} else {
res[i] = a[i] - borrowed - b[i]
borrowed = 0
}
}
return res
}
@[direct_array_access; inline] @[direct_array_access; inline]
fn bit_len_128(a [16]u8) int { fn bit_len_128(a [16]u8) int {

23
ip6.v
View File

@ -514,7 +514,7 @@ pub:
host_address ?Ipv6Addr host_address ?Ipv6Addr
prefix_len int prefix_len int
mut: mut:
current big.Integer current [16]u8
} }
// Ipv6Net.new creates new IPv6 network from given Ipv6Addr and prefix. // Ipv6Net.new creates new IPv6 network from given Ipv6Addr and prefix.
@ -548,7 +548,7 @@ pub fn Ipv6Net.new(addr Ipv6Addr, prefix int) !Ipv6Net {
broadcast_address: broadcast broadcast_address: broadcast
host_address: host_addr host_address: host_addr
prefix_len: prefix prefix_len: prefix
current: net_addr.bigint() current: net_addr.u8_array_fixed()
} }
} }
@ -617,7 +617,7 @@ pub fn Ipv6Net.from_string(cidr string) !Ipv6Net {
broadcast_address: broadcast broadcast_address: broadcast
host_address: host_addr host_address: host_addr
prefix_len: prefix_len prefix_len: prefix_len
current: net_addr.bigint() current: net_addr.u8_array_fixed()
} }
} }
@ -639,14 +639,15 @@ pub fn Ipv6Net.from_bigint(addr big.Integer, prefix int) !Ipv6Net {
} }
host_mask := net_mask.bitwise_xor(max_u128) host_mask := net_mask.bitwise_xor(max_u128)
broadcast := net_addr.bitwise_or(host_mask) broadcast := net_addr.bitwise_or(host_mask)
net_addr6 := Ipv6Addr.from_bigint(net_addr)!
return Ipv6Net{ return Ipv6Net{
network_address: Ipv6Addr.from_bigint(net_addr)! network_address: net_addr6
network_mask: Ipv6Addr.from_bigint(net_mask)! network_mask: Ipv6Addr.from_bigint(net_mask)!
host_mask: Ipv6Addr.from_bigint(host_mask)! host_mask: Ipv6Addr.from_bigint(host_mask)!
broadcast_address: Ipv6Addr.from_bigint(broadcast)! broadcast_address: Ipv6Addr.from_bigint(broadcast)!
host_address: host_addr host_address: host_addr
prefix_len: prefix prefix_len: prefix
current: net_addr current: net_addr6.u8_array_fixed()
} }
} }
@ -689,13 +690,15 @@ pub fn (n Ipv6Net) capacity() big.Integer {
// } // }
// ``` // ```
pub fn (mut n Ipv6Net) next() ?Ipv6Addr { pub fn (mut n Ipv6Net) next() ?Ipv6Addr {
if n.current >= n.broadcast_address.bigint() + big.one_int { // Possible optimization: do not calculate `limit` on each fn call (use LRU cache?)
limit := add_128(n.broadcast_address.addr, one_128)
if compare_128(n.current, limit) in [0, 1] {
return none return none
} }
defer { defer {
n.current = n.current + big.one_int n.current = add_128(n.current, one_128)
} }
return Ipv6Addr.from_bigint(n.current)! return Ipv6Addr.from_octets(n.current)!
} }
// first returns the first usable host address in network. // first returns the first usable host address in network.
@ -703,7 +706,7 @@ pub fn (n Ipv6Net) first() Ipv6Addr {
if n.prefix_len in [127, 128] { if n.prefix_len in [127, 128] {
return n.network_address return n.network_address
} }
return Ipv6Addr.from_bigint(n.network_address.bigint() + big.one_int) or { panic(err) } return Ipv6Addr.from_octets(add_128(n.network_address.addr, one_128)) or { panic(err) }
} }
// last returns the last usable host address in network. // last returns the last usable host address in network.
@ -711,7 +714,7 @@ pub fn (n Ipv6Net) last() Ipv6Addr {
if n.prefix_len in [127, 128] { if n.prefix_len in [127, 128] {
return n.broadcast_address return n.broadcast_address
} }
return Ipv6Addr.from_bigint(n.broadcast_address.bigint() - big.one_int) or { panic(err) } return Ipv6Addr.from_octets(sub_128(n.broadcast_address.addr, one_128)) or { panic(err) }
} }
// nth returns the Nth address in network. Supports negative indexes. // nth returns the Nth address in network. Supports negative indexes.

View File

@ -185,6 +185,12 @@ fn test_ipv6_net_format() {
assert net.format(.verbose | .with_host_mask) == 'fe80:ffff:0000:0000:0000:0000:0000:0000/0000:0000:0000:0000:ffff:ffff:ffff:ffff' assert net.format(.verbose | .with_host_mask) == 'fe80:ffff:0000:0000:0000:0000:0000:0000/0000:0000:0000:0000:ffff:ffff:ffff:ffff'
} }
fn test_ipv6_net_first_last() {
net := netaddr.Ipv6Net.from_string('fe80:ffff::/64')!
assert net.first().str() == 'fe80:ffff::1'
assert net.last().str() == 'fe80:ffff::ffff:ffff:ffff:fffe'
}
fn test_ipv6_net_next() { fn test_ipv6_net_next() {
net := netaddr.Ipv6Net.from_string('fe80::/64')! net := netaddr.Ipv6Net.from_string('fe80::/64')!
mut addrs := []netaddr.Ipv6Addr{} mut addrs := []netaddr.Ipv6Addr{}