Skip to content

Commit

Permalink
API improvements & fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ipfreely-uk committed Apr 11, 2024
1 parent b5122e2 commit b14e3e0
Show file tree
Hide file tree
Showing 34 changed files with 719 additions and 261 deletions.
8 changes: 4 additions & 4 deletions ip/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const (
type Address[A any] interface {
// Structs that conform to this interface must be produced by this package
sealed()
// IP address family
// IP address family - [V4] or [V6]
Family() Family[A]
// Address as bytes
Bytes() []byte
Expand All @@ -36,15 +36,15 @@ type Address[A any] interface {
Or(A) A
// Bitwise XOR
Xor(A) A
// Bit shift. Use negative int for left shift; use positive in for right shift.
// Bit shift. Use negative int for left shift; use positive int for right shift.
Shift(int) A
// Returns 1 if operand is less than this.
// Returns -1 if operand is more than this.
// Returns 0 if operand is equal.
Compare(A) int
// Similar to math/bits.LeadingZeros*
// Equivalent to math/bits.LeadingZeros
LeadingZeros() int
// Similar to math/bits.TrailingZeros*
// Equivalent to math/bits.TrailingZeros
TrailingZeros() int
// Canonical string form
String() string
Expand Down
67 changes: 39 additions & 28 deletions ip/address4.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@ import (
)

// Immutable 32bit unsigned integer IP [Address] representation
type Address4 struct {
type A4 struct {
value uint32
}

func (a Address4) sealed() {}
func (a A4) sealed() {}

func (a Address4) Family() Family[Address4] {
// Returns [V4]
func (a A4) Family() Family[A4] {
a.sealed()
return V4()
}

func (a Address4) Bytes() []byte {
// Returns 4 byte slice
func (a A4) Bytes() []byte {
return []byte{
byte(a.value >> 24),
byte(a.value >> 16),
Expand All @@ -26,74 +28,83 @@ func (a Address4) Bytes() []byte {
}
}

func (a Address4) Not() Address4 {
return Address4{
// Bitwise NOT
func (a A4) Not() A4 {
return A4{
^a.value,
}
}

func (a Address4) Add(addend Address4) Address4 {
return Address4{
// Addition with overflow
func (a A4) Add(addend A4) A4 {
return A4{
a.value + addend.value,
}
}

func (a Address4) Subtract(addend Address4) Address4 {
return Address4{
// Subtraction with underflow
func (a A4) Subtract(addend A4) A4 {
return A4{
a.value - addend.value,
}
}

func (a Address4) Multiply(multiplicand Address4) Address4 {
return Address4{
// Multiplication with overflow
func (a A4) Multiply(multiplicand A4) A4 {
return A4{
a.value * multiplicand.value,
}
}

func (a Address4) Divide(denominator Address4) Address4 {
return Address4{
// Division
func (a A4) Divide(denominator A4) A4 {
return A4{
a.value / denominator.value,
}
}

func (a Address4) Mod(denominator Address4) Address4 {
return Address4{
// Modulus
func (a A4) Mod(denominator A4) A4 {
return A4{
a.value % denominator.value,
}
}

func (a Address4) And(operand Address4) Address4 {
return Address4{
// Bitwise AND
func (a A4) And(operand A4) A4 {
return A4{
a.value & operand.value,
}
}

func (a Address4) Or(operand Address4) Address4 {
return Address4{
// Bitwise OR
func (a A4) Or(operand A4) A4 {
return A4{
a.value | operand.value,
}
}

func (a Address4) Xor(operand Address4) Address4 {
return Address4{
// Bitwise XOR
func (a A4) Xor(operand A4) A4 {
return A4{
a.value ^ operand.value,
}
}

func (a Address4) Shift(bits int) Address4 {
func (a A4) Shift(bits int) A4 {
bits = bits % a.Family().Width()
var v uint32
if bits > 0 {
v = a.value >> bits
} else {
v = a.value << (-1 * bits)
}
return Address4{
return A4{
v,
}
}

func (a Address4) Compare(other Address4) int {
func (a A4) Compare(other A4) int {
if a.value < other.value {
return -1
}
Expand All @@ -103,15 +114,15 @@ func (a Address4) Compare(other Address4) int {
return 0
}

func (a Address4) LeadingZeros() int {
func (a A4) LeadingZeros() int {
return bits.LeadingZeros32(a.value)
}

func (a Address4) TrailingZeros() int {
func (a A4) TrailingZeros() int {
return bits.TrailingZeros32(a.value)
}

func (a Address4) String() string {
func (a A4) String() string {
b := a.Bytes()
addr, _ := netip.AddrFromSlice(b)
return addr.String()
Expand Down
50 changes: 25 additions & 25 deletions ip/address6.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ import (
)

// Immutable 128bit unsigned integer IP [Address] representation
type Address6 struct {
type A6 struct {
high uint64
low uint64
}

func (a Address6) sealed() {}
func (a A6) sealed() {}

func (a Address6) Family() Family[Address6] {
func (a A6) Family() Family[A6] {
a.sealed()
return V6()
}

func (a Address6) Bytes() []byte {
func (a A6) Bytes() []byte {
return []byte{
byte(a.high >> 56),
byte(a.high >> 48),
Expand All @@ -39,38 +39,38 @@ func (a Address6) Bytes() []byte {
}
}

func (a Address6) Not() Address6 {
return Address6{
func (a A6) Not() A6 {
return A6{
^a.high,
^a.low,
}
}

func (a Address6) Add(addend Address6) Address6 {
func (a A6) Add(addend A6) A6 {
var low = a.low + addend.low
var high = a.high + addend.high
if low < addend.low || low < a.low {
high = high + 1
}
return Address6{
return A6{
high,
low,
}
}

func (a Address6) Subtract(subtrahend Address6) Address6 {
func (a A6) Subtract(subtrahend A6) A6 {
var low = a.low - subtrahend.low
var high = a.high - subtrahend.high
if a.low < subtrahend.low {
high = high - 1
}
return Address6{
return A6{
high,
low,
}
}

func (a Address6) Multiply(multiplicand Address6) Address6 {
func (a A6) Multiply(multiplicand A6) A6 {
this := ToBigInt(a)
that := ToBigInt(multiplicand)
max := ToBigInt(MaxAddress(a.Family()))
Expand All @@ -79,7 +79,7 @@ func (a Address6) Multiply(multiplicand Address6) Address6 {
return address
}

func (a Address6) Divide(denominator Address6) Address6 {
func (a A6) Divide(denominator A6) A6 {
this := ToBigInt(a)
that := ToBigInt(denominator)
max := ToBigInt(MaxAddress(a.Family()))
Expand All @@ -88,36 +88,36 @@ func (a Address6) Divide(denominator Address6) Address6 {
return address
}

func (a Address6) Mod(denominator Address6) Address6 {
func (a A6) Mod(denominator A6) A6 {
this := ToBigInt(a)
that := ToBigInt(denominator)
result := this.Mod(this, that)
address, _ := FromBigInt(a.Family(), result)
return address
}

func (a Address6) And(operand Address6) Address6 {
return Address6{
func (a A6) And(operand A6) A6 {
return A6{
a.high & operand.high,
a.low & operand.low,
}
}

func (a Address6) Or(operand Address6) Address6 {
return Address6{
func (a A6) Or(operand A6) A6 {
return A6{
a.high | operand.high,
a.low | operand.low,
}
}

func (a Address6) Xor(operand Address6) Address6 {
return Address6{
func (a A6) Xor(operand A6) A6 {
return A6{
a.high ^ operand.high,
a.low ^ operand.low,
}
}

func (a Address6) Shift(bits int) Address6 {
func (a A6) Shift(bits int) A6 {
var high uint64
var low uint64
if bits > 0 {
Expand All @@ -131,13 +131,13 @@ func (a Address6) Shift(bits int) Address6 {
high = a.high<<n | x
low = a.low << n
}
return Address6{
return A6{
high,
low,
}
}

func (a Address6) Compare(other Address6) int {
func (a A6) Compare(other A6) int {
if a.high < other.high {
return -1
}
Expand All @@ -153,23 +153,23 @@ func (a Address6) Compare(other Address6) int {
return 0
}

func (a Address6) LeadingZeros() int {
func (a A6) LeadingZeros() int {
high0 := bits.LeadingZeros64(a.high)
if high0 == 64 {
return bits.LeadingZeros64(a.low) + 64
}
return high0
}

func (a Address6) TrailingZeros() int {
func (a A6) TrailingZeros() int {
low0 := bits.TrailingZeros64(a.low)
if low0 == 64 {
return bits.TrailingZeros64(a.high) + 64
}
return low0
}

func (a Address6) String() string {
func (a A6) String() string {
b := a.Bytes()
addr, _ := netip.AddrFromSlice(b)
return addr.String()
Expand Down
5 changes: 3 additions & 2 deletions ip/bigint.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import (
"math/big"
)

// Converts any address to big integer
// Converts any [Address] to big integer
func ToBigInt[A Address[A]](address A) *big.Int {
return big.NewInt(0).SetBytes(address.Bytes())
}

// Converts big integer to address
// Converts big integer to [Address].
// Returns error if value out of range for address family.
func FromBigInt[A Address[A]](family Family[A], i *big.Int) (A, error) {
b := i.Bytes()
maxlen := family.Width() / 8
Expand Down
19 changes: 19 additions & 0 deletions ip/bigint_example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ip_test

import (
"math/big"

"github.com/ipfreely-uk/go/ip"
)

func ExampleToBigInt() {
family := ip.V6()
first := ip.MinAddress(family)
last := ip.MaxAddress(family)
diff := last.Subtract(first)

n := ip.ToBigInt(diff)
rangeSize := n.Add(n, big.NewInt(1))

println("Number of addresses = ", rangeSize.String())
}
File renamed without changes.
2 changes: 1 addition & 1 deletion ip/compare/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package compare

// Generic comparison interface
type Comparable[C any] interface {
// -1 less; 0 equal; 1 more
// Returns -1 if operand less; 0 if operand equal; 1 if operand greater
Compare(C) int
}

Expand Down
2 changes: 2 additions & 0 deletions ip/family.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ type Family[A any] interface {
// Create address from bytes.
// Returns error if slice is not [Width]/8 bytes.
FromBytes(...byte) (A, error)
// As FromBytes but panics on error
MustFromBytes(...byte) A
// Create address from unsigned integer.
// All values are valid.
FromInt(i uint32) A
Expand Down
Loading

0 comments on commit b14e3e0

Please sign in to comment.