diff --git a/prefix.go b/prefix.go index d58762b..32464fa 100644 --- a/prefix.go +++ b/prefix.go @@ -638,8 +638,12 @@ func (p *Prefix) availablePrefixes() (uint64, []string) { totalAvailable := uint64(0) availablePrefixes := []string{} for _, pfx := range pfxs { + bits := maxBits - pfx.Bits() + if bits < 0 { + continue + } // same as: totalAvailable += uint64(math.Pow(float64(2), float64(maxBits-pfx.Bits))) - totalAvailable += 1 << (maxBits - pfx.Bits()) + totalAvailable += 1 << bits availablePrefixes = append(availablePrefixes, pfx.String()) } // we are not reporting more that 2^31 available prefixes diff --git a/prefix_test.go b/prefix_test.go index 4e44dd8..96aed7e 100644 --- a/prefix_test.go +++ b/prefix_test.go @@ -10,6 +10,7 @@ import ( "sync" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" ) @@ -1644,3 +1645,83 @@ func TestNamespaceFromContext(t *testing.T) { }) } } +func TestAvailablePrefixes(t *testing.T) { + testCases := []struct { + name string + cidr string + expectedTotal uint64 + expectedAvailablePfx []string + }{ + { + name: "192.168.0.0/32", + cidr: "192.168.0.0/32", + expectedTotal: 0, + expectedAvailablePfx: []string{}, + }, + { + name: "192.168.0.0/31", + cidr: "192.168.0.0/31", + expectedTotal: 0, + expectedAvailablePfx: []string{}, + }, + { + name: "192.168.0.0/30", + cidr: "192.168.0.0/30", + expectedTotal: 1, + expectedAvailablePfx: []string{"192.168.0.0/30"}, + }, + { + name: "192.168.0.0/24", + cidr: "192.168.0.0/24", + expectedTotal: 64, + expectedAvailablePfx: []string{"192.168.0.0/24"}, + }, + { + name: "2001:0db8:85a3::/128", + cidr: "2001:0db8:85a3::/128", + expectedTotal: 0, + expectedAvailablePfx: []string{}, + }, + { + name: "2001:0db8:85a3::/127", + cidr: "2001:0db8:85a3::/127", + expectedTotal: 0, + expectedAvailablePfx: []string{}, + }, + { + name: "2001:0db8:85a3::/126", + cidr: "2001:0db8:85a3::/126", + expectedTotal: 1, + expectedAvailablePfx: []string{"2001:db8:85a3::/126"}, + }, + { + name: "Invalid CIDR", + cidr: "Invalid CIDR", + expectedTotal: 0, + expectedAvailablePfx: []string{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + prefix := &Prefix{ + Cidr: tc.cidr, + isParent: false, + availableChildPrefixes: make(map[string]bool), + } + + totalAvailable, availablePrefixes := prefix.availablePrefixes() + + assert.Equal( + t, tc.expectedTotal, totalAvailable, + "Expected totalAvailable: %d, got: %d", + tc.expectedTotal, totalAvailable, + ) + assert.ElementsMatchf( + t, availablePrefixes, tc.expectedAvailablePfx, + "Expected availablePrefixes: %v, got: %v", + tc.expectedAvailablePfx, availablePrefixes, + ) + }) + } +}