Compare commits

...

6 Commits

13 changed files with 95 additions and 50 deletions

View File

@ -1,9 +1,7 @@
name: CI
name: Docs
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
workflow_dispatch:
jobs:
@ -19,9 +17,6 @@ jobs:
unzip -q /tmp/v.zip -d /tmp
echo /tmp/v >> "$GITHUB_PATH"
- name: Run tests
run: v -stats test .
- name: Build docs
run: |
v doc -f html -m .

27
.github/workflows/test.yaml vendored Normal file
View File

@ -0,0 +1,27 @@
name: Lint and test
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup V
run: |
wget -qO /tmp/v.zip https://github.com/vlang/v/releases/latest/download/v_linux.zip
unzip -q /tmp/v.zip -d /tmp
echo /tmp/v >> "$GITHUB_PATH"
- name: Run tests
run: |
v fmt -verify .
v vet -v -W -I -F -r .
v missdoc -r --verify .
v -stats test .

View File

@ -14,14 +14,15 @@
// along with netaddr. If not, see <https://www.gnu.org/licenses/>.
/*
This file contains functions for operating with big endian ordered byte arrays.
Using big.Integer is significantly slower than doing math strictly on 128-bit
numbers. At a minimum, you have to do expensive instantiation of big.Integer.
The functions below do not require copying arrays and allocate less memory.
This file contains functions for operating with 128-bit unsigned integer
numbers represented as big endian ordered byte fixed size arrays.
Note that arrays always be 16 byte length and may contain leading zeros.
Functions missing:
fn add_128(a [16]u8, b [16]u8) [16]u8
fn diff_128(a [16]u8, b [16]u8) [16]u8
Using V math.big is significantly slower than doing math strictly on
128-bit numbers. At a minimum, you have to do expensive instantiation of
big.Integer.
The functions below do not requires copying and allocates less memory.
*/
module netaddr
@ -29,6 +30,39 @@ module netaddr
import math.bits
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]
fn bit_len_128(a [16]u8) int {

View File

@ -1,26 +0,0 @@
SRC_DIR ?= src
DOC_DIR ?= doc
TESTS_DIR ?= tests
all: fmt vet missdoc test
fmt:
v fmt -verify -diff $(SRC_DIR)
vet:
v vet -W -r -I -F $(SRC_DIR)
missdoc:
v missdoc -r --verify $(SRC_DIR)
test:
v test .
doc:
v doc -f html -m . -o $(DOC_DIR)
clean:
rm -r $(DOC_DIR) || true
serve: clean doc
v -e "import net.http.file; file.serve(folder: '$(DOC_DIR)')"

View File

@ -46,7 +46,7 @@ fn main() {
if ip is netaddr.Ipv4Net || ip is netaddr.Ipv6Net {
panic('${ip} seems to be network, not a single host addresses')
}
println(addr)
println(ip)
}
```
@ -265,3 +265,9 @@ println(ip) // 2001:db8::8429:6bff:fedc:ef8b
Note that using EUI in IPv6 address may cause security issues. See
[RFC 4941](https://datatracker.ietf.org/doc/html/rfc4941) for details.
# License
`netaddr` is released under LGPL 3.0 or later license.
SPDX Lincese ID: `LGPL-3.0-or-later`.

View File

View File

@ -514,7 +514,7 @@ pub:
host_address ?Ipv6Addr
prefix_len int
mut:
current big.Integer
current [16]u8
}
// 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
host_address: host_addr
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
host_address: host_addr
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)
broadcast := net_addr.bitwise_or(host_mask)
net_addr6 := Ipv6Addr.from_bigint(net_addr)!
return Ipv6Net{
network_address: Ipv6Addr.from_bigint(net_addr)!
network_address: net_addr6
network_mask: Ipv6Addr.from_bigint(net_mask)!
host_mask: Ipv6Addr.from_bigint(host_mask)!
broadcast_address: Ipv6Addr.from_bigint(broadcast)!
host_address: host_addr
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 {
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
}
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.
@ -703,7 +706,7 @@ pub fn (n Ipv6Net) first() Ipv6Addr {
if n.prefix_len in [127, 128] {
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.
@ -711,7 +714,7 @@ pub fn (n Ipv6Net) last() Ipv6Addr {
if n.prefix_len in [127, 128] {
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.

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'
}
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() {
net := netaddr.Ipv6Net.from_string('fe80::/64')!
mut addrs := []netaddr.Ipv6Addr{}